在CentOS服务器安全防护中,SELinux和防火墙(firewalld/iptables)不是二选一的关系,而是必须协同工作的两道防线。很多运维人员只开防火墙就觉得安全了,或者只关了SELinux觉得省事,这两种做法都会留下明显的安全缺口。真正的协同防御是:防火墙控制网络层面谁能连进来、连哪个端口,SELinux控制进程层面即使被连进来了也只能做有限的事。下面我直接用一个Web服务器的真实场景,把这套协同防御从头到尾讲透。
一、为什么SELinux和防火墙必须配合使用
防火墙(以CentOS 7/8/9默认的firewalld为例)工作在网络层,它决定的是"哪个IP能访问哪个端口"。比如你开放了80和443端口,那外部流量就能打到你的Nginx进程上。但问题来了——如果Nginx被攻破了呢?攻击者拿到了Nginx进程的权限,他能干什么?这时候就轮到SELinux出场了。SELinux工作在进程层,它给每个进程贴标签,规定这个进程只能读哪些文件、只能开哪些端口、只能连哪些网络资源。即使防火墙放行了,SELinux也能把进程的活动范围锁死。两层叠加,攻击面大幅缩小。
二、环境准备与SELinux状态确认
先确认你的CentOS版本和SELinux状态。CentOS 7用的是SELinux的targeted策略,CentOS 8/9用的也是targeted但底层机制有变化。先执行以下命令查看状态:
getenforce sestatus
如果返回是Enforcing,说明SELinux正在强制执行,这是生产环境的正确状态。如果是Permissive,说明只记录不拦截,等于没开。如果是Disabled,那就完全关了,强烈建议改回Enforcing。修改方法:
sed -i 's/SELINUX=disabled/SELINUX=enforcing/' /etc/selinux/config setenforce 1
注意setenforce 1只是临时生效,重启后以config文件为准。另外要确认firewalld在运行:
systemctl status firewalld systemctl enable firewalld
三、防火墙规则的基础配置
假设我们部署一台Web服务器,只需要开放80(HTTP)、443(HTTPS)和22(SSH管理)端口。基础防火墙规则如下:
firewall-cmd --permanent --zone=public --add-service=http firewall-cmd --permanent --zone=public --add-service=https firewall-cmd --permanent --zone=public --add-service=ssh firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client firewall-cmd --reload
这里有个细节:去掉dhcpv6-client是因为服务器一般不需要IPv6的DHCP客户端服务对外暴露。如果你还需要限制SSH只允许特定IP访问,可以用rich rule:
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ssh" accept' firewall-cmd --reload
这样只有10.0.0.0/24网段的机器能SSH进来,其他IP即使端口开放也连不上。这是防火墙层面的第一道过滤。
四、SELinux策略对Web服务的约束配置
SELinux默认的targeted策略下,Nginx(httpd)进程只能使用特定的端口和文件路径。你可以用semanage命令查看和修改。先看Nginx被允许使用哪些端口:
semanage port -l | grep http_port_t
默认输出应该包含80、443、488、8008、8009、8443这些端口。如果你的Nginx要监听非标准端口比如8080,就需要手动添加:
semanage port -a -t http_port_t -p tcp 8080
再看文件上下文。Nginx进程默认只能访问/var/www/html/目录下标记为httpd_sys_content_t的文件。如果你把网站根目录放在/data/web/,就需要重新打标签:
semanage fcontext -a -t httpd_sys_content_t "/data/web(/.*)?" restorecon -Rv /data/web
这一步非常关键。很多人改了目录路径后Nginx报403,不是权限问题,是SELinux不让读。restorecon会递归地把新目录标记成正确的上下文类型。
五、协同防御的核心案例:限制Nginx的网络行为
这是本文最核心的部分。假设攻击者通过某种方式让Nginx执行了恶意代码,想让Nginx进程主动连接外部的C&C服务器。防火墙层面你可能已经限制了出站,但SELinux还能再加一层保险。用SELinux的布尔值来控制Nginx是否允许发起网络连接:
getsebool -a | grep httpd
你会看到几个关键布尔值:
httpd_can_network_connect --> off httpd_can_network_relay --> off httpd_can_sendmail --> off
默认情况下httpd_can_network_connect是off,意味着Nginx进程不能主动发起对外网络连接。这就是SELinux的协同防御价值——即使防火墙出站规则没配好,SELinux也能兜底。如果你的应用确实需要Nginx连接后端数据库或者调用外部API,才需要开启:
setsebool -P httpd_can_network_connect on
注意-P参数是永久生效,不加的话重启就恢复了。但我的建议是:能不开就不开,需要连后端就用反向代理或者独立的应用进程去连,不要让Web服务器进程本身有出站能力。
六、处理SELinux拒绝日志与故障排查
协同配置过程中一定会遇到SELinux拦截的情况。这时候不要急着关SELinux,而是去看审计日志找原因。CentOS的SELinux拒绝日志在/var/log/audit/audit.log。用ausearch或audit2why工具分析:
ausearch -m avc -ts recent audit2why -a
比如你看到一条拒绝记录说Nginx无法读取/etc/shadow文件,那显然是你配置有误或者有恶意代码在尝试提权。正常情况下Nginx根本不需要碰/etc/shadow。如果是正常业务需要访问某个自定义路径被拦截,就用前面讲的semanage fcontext加规则,而不是关SELinux。
还有一个实用技巧:如果你确认某个操作是安全的但SELinux一直拦,可以用audit2allow生成自定义策略模块:
ausearch -m avc -ts recent | audit2allow -M my_custom_policy semodule -i my_custom_policy.pp
这比直接setenforce 0安全得多,因为你是针对特定行为开了口子,而不是把整个防护关掉。
七、防火墙与SELinux联动的高级场景
再举一个实际场景:你的服务器上跑了多个服务,比如Nginx、MySQL、Redis。防火墙层面你只开放了80、443、3306、6379端口。但SELinux层面,MySQL进程(mysqld_t)默认只能用3306,Redis(redis_t)只能用6379。如果有人把Redis配置改成监听3306,SELinux会直接拦截,因为3306端口的标签是mysqld_port_t,不是redis_port_t。这就是协同防御的妙处——即使你防火墙配错了开放了多余端口,SELinux也能根据进程类型做二次校验。
对于需要限制进程只能监听特定IP的场景,SELinux也有办法。比如让Nginx只监听127.0.0.1(本地),外部通过防火墙做端口转发:
semanage port -m -t http_port_t -p tcp 127.0.0.1:8080
然后防火墙只转发外部80到本地8080,这样Nginx进程本身不直接暴露在公网,又多了一层隔离。
八、日常维护与检查清单
最后给一份可执行的日常安全检查清单,建议每周跑一次:
# 检查SELinux状态 getenforce # 检查防火墙规则是否有多余开放 firewall-cmd --list-all # 检查SELinux布尔值是否有异常开启 getsebool -a | grep -i "on" | grep -v "^httpd_use_" # 检查最近的SELinux拒绝事件 ausearch -m avc -ts recent | wc -l # 检查关键服务的端口标签是否正确 semanage port -l | grep -E "http_port_t|mysqld_port_t|redis_port_t"
如果拒绝事件数量突然暴增,说明可能有攻击或配置变更,需要立即排查。布尔值如果发现不该开的开了,马上关掉。防火墙规则如果发现有临时测试留下的开放端口,及时清理。
九、总结与核心观点
CentOS安全防护的核心思路不是堆砌工具,而是让每一层防护做好自己该做的事。防火墙管网络出入口,SELinux管进程行为边界,两者不重复、不冲突、互相补位。实际运维中最常见的错误就是两个极端:要么全关只靠防火墙,要么嫌麻烦全开只靠SELinux。正确做法是两个都开、都配好、定期检查。SELinux不是麻烦,它是你服务器被攻破后的最后一道保险。花半天时间把策略配好,能省掉后面无数个通宵排障的夜晚。
