在CentOS服务器上跑Web应用,最怕的就是一个漏洞把整个系统拖下水。SELinux沙箱隔离就是用系统级强制访问控制(MAC)机制,把Web进程"关进笼子"——即使Web应用被攻破,攻击者也只能在限定范围内活动,无法读取系统密码文件、无法访问其他用户数据、无法提权到root。这不是防火墙能解决的问题,而是操作系统内核层面的进程隔离方案。下面我直接讲怎么做、做什么、注意什么。

一、为什么传统权限控制不够用

Linux传统的DAC(自主访问控制)只看用户和组的rwx权限。Web服务通常以apache或nginx用户运行,一旦这个用户被入侵,攻击者就拥有了该用户能访问的所有文件。你可能说"我把文件权限设成600不就行了",但现实是Web应用需要读取配置文件、上传目录、日志目录,这些文件权限必然要放开。SELinux的核心思路完全不同:它不管你是谁,只看进程的"安全上下文"(security context)和文件的"安全标签"是否匹配。进程被限制在一个极小的策略域里,超出策略的操作直接被内核拒绝。

二、SELinux基础概念快速理解

在动手之前,你需要理解三个核心概念。第一是"安全上下文"(security context),每个进程和文件都有一个标签,格式类似"system_u:object_r:httpd_sys_content_t:s0"。第二是"策略"(policy),CentOS默认使用targeted策略,它定义了哪些进程类型可以访问哪些文件类型。第三是"布尔值"(boolean),这是一些开关,用来快速调整策略的宽松程度,而不需要重新编写策略规则。

三、确认SELinux状态和当前模式

先检查SELinux是否在运行。执行以下命令:

sestatus

输出会显示SELinux状态(enabled/disabled)和当前模式(enforcing/permissive/disabled)。如果是disabled,需要先启用它:

setenforce 1
sed -i 's/SELINUX=disabled/SELINUX=enforcing/' /etc/selinux/config

enforcing模式是真正拦截违规操作,permissive模式只记录不拦截,适合调试阶段。生产环境必须用enforcing。

四、查看Web进程的安全上下文

用ps命令加-Z参数可以看到进程的安全上下文:

ps -eZ | grep httpd

你会看到类似"system_u:system_r:httpd_t:s0"的输出。httpd_t就是Apache进程的类型标签。所有以httpd_t运行的进程共享同一套访问规则。这就是隔离的起点——我们要做的就是确保这个类型只能访问它该访问的东西。

五、使用SELinux布尔值快速加固

CentOS的targeted策略预置了大量布尔开关,针对Web场景有几个特别关键的:

getsebool -a | grep httpd

重点关注以下几个:

httpd_can_network_connect——控制Web进程是否能发起外连。如果你的Web应用不需要主动连接外部数据库或API,关掉它:

setsebool -P httpd_can_network_connect off

httpd_can_network_connect_db——专门控制是否能连数据库。如果用本地Unix Socket连MySQL就不需要开:

setsebool -P httpd_can_network_connect_db off

httpd_execmem——控制是否允许执行内存映射。攻击者常用execmem提权,必须关:

setsebool -P httpd_execmem off

httpd_read_user_content——控制是否能读取用户家目录。如果Web根目录不在/home下,关掉:

setsebool -P httpd_read_user_content off

注意-P参数表示永久生效,重启不会丢失。

六、文件安全上下文的正确标记

这一步很多人忽略,但它是沙箱隔离的基石。Web根目录、上传目录、日志目录都需要正确的安全标签。用restorecon命令可以根据策略自动恢复默认标签:

restorecon -Rv /var/www/html

如果你的Web应用有自定义目录,比如/data/uploads,需要手动标记:

semanage fcontext -a -t httpd_sys_rw_content_t "/data/uploads(/.*)?"
restorecon -Rv /data/uploads

httpd_sys_rw_content_t表示Web进程可读写的内容类型。如果只是只读内容用httpd_sys_content_t,可执行脚本用httpd_sys_script_exec_t。标签选错了,要么访问被拒,要么隔离失效。

七、针对PHP/Python等动态语言的额外隔离

如果你的Web应用用PHP-FPM或Python uWSGI,这些进程同样需要纳入SELinux管控。PHP-FPM进程默认标签是httpd_php_t或unconfined_service_t,取决于配置。检查一下:

ps -eZ | grep php-fpm

确保PHP-FPM的上下文是httpd_php_t。如果不是,检查/etc/selinux/targeted/contexts/customizable_types里的定义,或者用semanage命令修正:

semanage permissive -a httpd_php_t

上面这条是把PHP-FPM设为permissive模式(只记录不拦截),适合初期调试。等策略调通了再去掉permissive。

八、使用SELinux沙箱策略限制进程能力

除了布尔值和文件标签,还可以通过自定义策略模块进一步收紧。假设你发现Web进程试图访问/etc/shadow,audit日志会记录AVC拒绝信息。用audit2allow工具可以生成针对性的策略模块:

ausearch -m avc -ts recent | audit2allow -M my_web_sandbox
semodule -i my_web_sandbox.pp

但更好的做法是主动限制。创建一个自定义策略模块,明确禁止Web进程的某些能力:

cat > my_web_confined.te << 'EOF'
policy_module(my_web_confined, 1.0)

gen_require(`
    type httpd_t;
')

# 禁止httpd_t读取所有shadow文件
neverallow httpd_t shadow_t:file { read open };

# 禁止httpd_t执行非Web目录的程序
neverallow httpd_t bin_t:file { execute execute_no_trans open read };
EOF

checkmodule -M -m -o my_web_confined.mod my_web_confined.te
semodule_package -o my_web_confined.pp -m my_web_confined.mod
semodule -i my_web_confined.pp

这个策略明确禁止了httpd_t读取shadow文件和执行非Web目录的二进制。你可以根据实际需求继续添加规则。

九、审计日志的监控和分析

SELinux的审计日志在/var/log/audit/audit.log。用ausearch和sealert可以快速定位问题:

ausearch -m avc -ts today | sealert -a /var/log/audit/audit.log

sealert会给出人类可读的分析报告,告诉你哪个进程被拒绝了什么操作。这是调试策略的核心工具。建议配合logrotate做日志轮转,否则audit日志会快速膨胀。

十、常见坑和实战建议

第一,不要一上来就把SELinux设成enforcing然后不管了。先用permissive模式跑一周,收集所有AVC日志,确认没有误杀正常业务再切enforcing。第二,不要用setenforce 0临时关闭SELinux来"解决"问题,这等于把门拆了。第三,/var/lib/mysql、/var/lib/pgsql这类数据库目录需要单独标记为mysqld_db_t或postgresql_db_t,Web进程不应该直接碰。第四,如果用了Docker或容器,容器内的SELinux策略和宿主机是隔离的,需要在容器启动时加--security-opt label:type:container_t或者用--privileged(但这会削弱隔离)。第五,CentOS 8以后逐步转向AlmaLinux或Rocky Linux,SELinux机制完全一致,操作命令通用。

十一、总结:SELinux沙箱不是银弹但必须有

SELinux沙箱隔离不会阻止所有攻击,但它能把攻击的"爆炸半径"控制在最小范围。一个被攻破的Web进程在SELinux约束下,无法读取/etc/passwd、无法提权、无法横向移动到其他服务。配合最小权限原则、及时补丁、WAF等多层防御,才是真正的安全体系。在CentOS上花半天时间把SELinux策略调通,比你装十个安全软件都管用。这是内核级别的防线,值得每一个运维和安全人员认真对待。