玩Linux运维,特别是跑CentOS的服务器,最怕两种极端:一种是嫌麻烦直接关掉SELinux,把安全大门敞开;另一种是开着SELinux,结果业务一上线各种“Permission denied”,排查半天发现是安全策略拦了,于是又回到第一种。其实SELinux没那么可怕,它的核心价值在于强制访问控制,能把进程的权限锁死在一个最小范围内,就算你的Web应用被拿下了,攻击者也很难横向移动到数据库或系统关键目录。问题在于默认策略太粗糙,我们需要把它调细,调到刚好能让业务正常运行,又不留多余的口子。下面直接讲怎么精细化配置。
理解SELinux的标签和类型强制精细化配置的前提是你得知道SELinux到底靠什么做判断。它不只看用户和组,主要看上下文标签。每个文件、进程、端口都有一个标签,格式是“用户:角色:类型:级别”,日常排错和配置里,最关键的就是“类型”这个字段。比如httpd进程的类型是httpd_t,网站根目录/var/www/html的文件类型是httpd_sys_content_t。SELinux策略里写好了规则,规定httpd_t只能读取httpd_sys_content_t类型的文件,你要是在/opt/web下放了网站文件,类型默认是usr_t,httpd当然读不了,就报错了。所以精细化配置的本质,就是让文件、端口、进程的类型标签匹配上策略允许的范围,或者自定义策略模块来放行特定操作。
快速诊断:别盲目改策略,先看日志出问题时,第一反应不是去改配置,而是先确认是不是SELinux拦的。最直接的办法是看审计日志,但原生日志可读性差,得用工具翻译。CentOS默认装了setroubleshoot,它会把拒绝信息转成人类能看懂的描述,存在/var/log/messages里。你可以这样查:
sealert -a /var/log/audit/audit.log
输出会直接告诉你被拒绝的原因,甚至给出修复建议,比如建议你执行某个semanage命令或修改布尔值。如果没有sealert,先用ausearch快速过滤一下:
ausearch -m avc -ts recent
看到avc: denied字样,就说明有拦截。记下里面的scontext(源上下文,即进程标签)和tcontext(目标上下文,即文件或端口标签),以及tclass(操作类型,比如file、dir、tcp_socket)。这三个信息是后续所有调整的依据。千万别一看到denied就跑去改策略,先确认这个操作是否真的合理。如果是个异常行为,比如httpd进程试图读取/etc/shadow,那该拦就得拦,别放行。
调整文件上下文:让文件待在正确的位置最常见的问题就是文件放错了目录,或者自定义目录没有打上正确的标签。SELinux的策略很大程度上依赖路径,系统默认哪些目录该是什么类型,都在策略里定义好了。如果你非要把网站文件放在/data/www下面,那就要手动给这个目录打标签。临时改一下可以用chcon,但这个命令扛不住文件系统重标,一执行restorecon就还原了。正规做法是用semanage把路径规则写进策略数据库。
先查一下标准目录的标签规则,看看httpd_sys_content_t是怎么绑定的:
semanage fcontext -l | grep httpd_sys_content_t
然后添加你自己的目录规则:
semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?"
最后应用规则:
restorecon -Rv /data/www
注意路径末尾的(/.*)?,这表示包括目录本身以及下面所有子目录和文件。不加这个正则,规则只对目录本身生效,里面的文件不会自动继承。改完后用ls -Z看一眼,确认上下文已经变成httpd_sys_content_t。如果后续文件有写入需求,比如上传目录,那还需要把类型改成httpd_sys_rw_content_t,同时配合布尔值开启写入权限。
端口上下文:服务监听非标准端口业务里常有这种需求,比如为了避开冲突,把SSH从22改成2222,或者把HTTP从80改成8080。端口在SELinux里也有类型标签,策略规定了哪些进程类型能绑定哪些端口类型。你改了端口,但端口标签没变,服务就起不来。先查一下当前端口标签分配:
semanage port -l | grep ssh semanage port -l | grep http
如果要把SSH加到2222端口,这个端口默认可能没标签,或者标签不对,需要把2222端口的类型设为ssh_port_t:
semanage port -a -t ssh_port_t -p tcp 2222
同样,给HTTP加8080端口:
semanage port -a -t http_port_t -p tcp 8080
加完之后再重启服务,一般就能绑定了。如果报端口已存在标签,说明之前有人分配过,可以用-m参数修改,或者先-d删除再-a添加。删除前务必确认这个端口没有其他服务在用,否则会把别的服务搞挂。
布尔值开关:策略里的快捷开关SELinux策略里预置了很多布尔值,相当于一个个功能开关,控制着某些特定行为是否允许。你不用去改底层策略,只需要打开或关闭对应的布尔值就能放行一些常见操作。比如httpd要连接数据库,默认是不允许httpd进程发起网络连接的,需要打开httpd_can_network_connect_db:
setsebool -P httpd_can_network_connect_db on
再比如httpd读写用户家目录下的文件,需要开httpd_enable_homedirs。想看当前所有布尔值状态,用getsebool -a,配合grep过滤httpd相关的:
getsebool -a | grep httpd
注意setsebool后面一定要加-P,不然重启就还原了。布尔值虽然方便,但别上来就全开,那等于把SELinux架空了。每开一个之前,想清楚这个操作是否必要,是否可以用更细粒度的文件上下文调整来代替。
自定义策略模块:当默认规则不够用时有些场景下,调整文件上下文和布尔值都解决不了,比如你跑了一个自己写的守护进程,或者某个商业软件的行为非常规,默认策略完全没有覆盖。这时候就需要生成自定义策略模块。别手写,太容易出错,直接用audit2allow工具从审计日志里提取规则。
先让服务跑一遍,触发所有被拦截的操作,然后导出日志生成策略:
ausearch -m avc -ts recent | audit2allow -M mycustompolicy
这个命令会生成两个文件:mycustompolicy.te(源文件)和mycustompolicy.pp(编译好的模块)。在安装之前,务必打开.te文件仔细审查,看看到底放行了什么。audit2allow只是机械地把所有拒绝都转成允许规则,它不会判断这些操作是否危险。你可能会发现有些规则过于宽泛,比如直接允许进程读取所有文件类型,这种就要手动收紧,把通配符改成具体的类型。确认无误后再安装:
semodule -i mycustompolicy.pp
安装后模块会持久化,重启也在。如果后续发现规则有问题,可以用semodule -r mycustompolicy卸载。自定义模块是最后的手段,能不用尽量不用,因为每增加一个模块,就多一份维护成本,系统升级或迁移时可能会因为策略冲突导致问题。
日常维护与验证精细化配置不是一次性的活。每次部署新应用、调整目录结构、修改端口,都要养成习惯,先用restorecon检查一下文件标签是否正确,再用semanage确认端口分配。测试时可以把SELinux临时设成宽容模式,这样不会实际拦截,但会记录所有违反策略的操作,方便一次性收集日志:
setenforce 0
测完记得切回强制模式:
setenforce 1
另外,定期用sealert -a /var/log/audit/audit.log扫描一遍审计日志,看看有没有新出现的拒绝记录。很多安全事件早期就是靠这些日志发现的,别等出了事再翻。
还有一点容易被忽略,就是备份和迁移。你的自定义semanage规则和模块,在系统重装或迁移到新服务器时不会自动带过去。建议把semanage fcontext和port的导出命令写进运维脚本,定期备份:
semanage fcontext -l > fcontext_backup.txt semanage port -l > port_backup.txt semodule -l > module_backup.txt
这样恢复时有据可查,不至于重新踩坑。
SELinux精细化配置说到底就是一层窗户纸,捅破了你会发现它不过是一套基于标签的强制访问控制体系。核心思路就三步:先通过日志定位问题,然后用semanage调整文件或端口标签,实在不行再用布尔值或自定义模块放行。整个过程不需要神化它,也不用妖魔化它,把它当成一个严格的安检员,你只要按规矩给它看正确的证件,它就不会为难你。保持策略最小化,定期审计,你的CentOS服务器就能在安全性和可用性之间找到那个最佳平衡点。
