Linux运维6 min read次阅读

SELinux 实战:从排错到策略定制

为什么人人都想关掉 SELinux

nginx 起不来、ssh 连不上、mysql 读写被拒——日志里一句 Permission denied,关掉 SELinux 立刻好了。于是很多人 setenforce 0 一关了之,还写进开机脚本。

但 SELinux 是强制访问控制(MAC):即使 root,进程也只能在自己的"域(domain)"里活动。它挡住的不是文件权限,而是进程越权(比如 Web 服务被攻破后去读 /etc/shadow)。关掉它,等于拆掉 Linux 最后一道纵深防御。

正确姿势:让 SELinux 一直 enforcing,学会和它共处。

三种模式

getenforce            # 看当前:Enforcing / Permissive / Disabled
setenforce 1          # 临时切到 Enforcing(不改配置文件,重启失效)
setenforce 0          # 临时切到 Permissive(只记录不拦截,用于排错)

/etc/selinux/config

SELINUX=enforcing      # enforcing | permissive | disabled
SELINUXTYPE=targeted   # 一般就是 targeted(只保护常见服务,性能开销小)

排错黄金流程:setenforce 0 复现,确认是 SELinux 拦的 → 看日志 → 用 audit2allow 生成策略 → 恢复 enforcing。绝不停在 permissive。

核心概念:标签与域迁移

SELinux 给一切贴标签:用户:角色:类型:级别,日常最关心类型(type),也叫 domain。

# 看文件标签
ls -Z /var/www/html/
# system_u:object_r:httpd_sys_content_t:s0  index.html

# 看进程域
ps -eZ | grep nginx
# system_u:system_r:httpd_t:s0  nginx: master process

规则本质是:"域 httpd_t 对类型 httpd_sys_content_tread 权限"。进程想访问某资源,必须满足"进程域 → 资源类型"的 allow 规则,否则拒绝。

常见坑:把网站文件放到 /home/srv/custom,标签是 user_home_t / default_thttpd_t 无权读 → 报 403。不是权限问题,是标签问题。

排错四步法

# 1. 确认是否被拒(Permissive 下也会记日志但不拦)
#    RHEL7+/CentOS:日志在 audit.log;老系统用 /var/log/messages
ausearch -m AVC -ts recent
# type=AVC msg=audit(...): denied { read } for pid=1234
#   comm="nginx" name="index.html" dev="sda1" scontext=system_u:system_r:httpd_t:s0
#   tcontext=system_u:object_r:user_home_t:s0 tclass=file

# 2. 看 human 可读的解释
sealert -a /var/log/audit/audit.log

# 3. 从拒绝日志自动生成策略模块
grep nginx /var/log/audit/audit.log | audit2allow -M mynginx
# 生成 mynginx.te(策略源码)和 mynginx.pp(编译后模块)
semodule -i mynginx.pp     # 加载模块

# 4. 恢复 enforcing 验证
setenforce 1

audit2allow-M 会同时给出 .te 文本,务必先读再加载——它有时会过度授权(如直接 allow httpd_t user_home_t:file read;)。能精确修复就别给太宽。

文件标签修复(restorecon / chcon)

# 把目录恢复成该路径默认标签(最常用、最安全)
restorecon -Rv /var/www/html/

# 手动改标签(重启后可能被 restorecon 覆盖,仅临时验证用)
chcon -t httpd_sys_content_t /srv/custom/index.html

# 给自定义目录设定默认标签规则(永久)
semanage fcontext -a -t httpd_sys_content_t "/srv/custom(/.*)?"
restorecon -Rv /srv/custom

经验:chcon 是临时,semanage fcontext + restorecon 才是永久。改了标签重启还在,靠的是 fcontext 规则。

布尔值:服务能力的开关

很多"服务能不能干某事"由布尔值控制,比写策略简单:

getsebool -a | grep httpd
setsebool -P httpd_can_network_connect on   # 允许 httpd 出网(连 DB/代理)
setsebool -P httpd_enable_homedirs on       # 允许访问用户家目录
setsebool -P httpd_read_user_content on

# 数据库相关
setsebool -P mysql_connect_any on           # mysql 连任意端口

-P 表示持久化(写进磁盘),否则重启失效。布尔值列表 getsebool -a 很长,按服务名 grep 即可。

端口标签:非标准端口放行

# 想让 httpd 监听 8080(默认只允许 80/443)
semanage port -a -t http_port_t -p tcp 8080

# 看某类型允许的端口
semanage port -l | grep http_port_t

进程绑定端口也要过 SELinux 端口标签,否则 Permission denied——这是很多自定义端口起不来却查不出原因的根源。

10 条实战底线

  1. 永远保持 enforcing,排错用 permissive 临时切,定位完立刻恢复。
  2. 服务起不来先 setenforce 0 复现,确认是不是 SELinux 再下手,别瞎改权限。
  3. 文件标签不对用 restorecon -Rv,别先改 chmod 777
  4. chcon 临时、semanage fcontext + restorecon 永久,二选一要清楚。
  5. 日志在 /var/log/audit/audit.log,用 ausearch -m AVCsealert 看。
  6. audit2allow 生成策略后先读 .tesemodule -i,避免过度授权。
  7. 服务出网/读家目录先查布尔值(getsebool -a | grep 服务名),比写策略省事。
  8. 非标准端口绑定前 semanage port -a -t <type>_port_t 放行。
  9. 容器场景:宿主 SELinux 启用时给卷加 :z(共享)或 :Z(私有)标签,否则容器读不到。
  10. 不要把 SELINUX=disabled 写进配置——disabledpermissive 不同,重启用 disabled 后标签上下文会失效,再开回来要全量 relabel

SELinux 不是敌人,是那个"平时无聊、出事救命"的保安。学会用 audit2allow + 布尔值 + restorecon 三板斧,你会发现它比 chmod 777 优雅得多,也安全得多。

分享:

相关文章

评论区