SELinux
SELinux
目录
- 软件包安装
- SELinux 是什么:MAC vs DAC
- 三种模式:Enforcing / Permissive / Disabled
- 核心模型:类型、主体、客体与策略
- 上下文与文件标签:ls -Z / restorecon / chcon / semanage fcontext
- 布尔值:setsebool / getsebool / semanage boolean
- 排障:AVC 拒绝、ausearch、sealert、audit2why
- 实战:常见服务场景全流程
- 进阶:audit2allow 与自定义策略
- 常见问题排查清单
- 总结与练习
- 参考资料
软件包安装
SELinux 本身已内置在 Rocky/RHEL 内核与策略包中,默认启用。运维常用的是管理工具包:
# 查看已安装的 SELinux 工具
rpm -qa | grep selinux
# 安装常用管理工具(如缺失)
sudo dnf install -y policycoreutils-python-utils # 提供 semanage、audit2allow 等
sudo dnf install -y setools-console # 提供 seinfo、sesearch 等
sudo dnf install -y setroubleshoot setroubleshoot-server # 排障工具 sealert 等
查看当前 SELinux 状态
getenforce # 输出 Enforcing / Permissive / Disabled
sestatus # 更详细:模式、策略、配置文件路径等
说明:SELinux 默认已启用。刚学建议在虚拟机里操作,因为配置不当可能影响系统正常启动。
一、SELinux 是什么:MAC vs DAC
1.1 定义
SELinux(Security-Enhanced Linux,安全增强的 Linux) 是由美国 NSA 主导、后并入 Linux 内核的一种强制访问控制(MAC)机制。它在传统 Linux 权限之上,为"进程能访问什么"再加一层系统统一策略约束。
1.2 MAC vs DAC:理解 SELinux 的前提
| 对比 | 自主访问控制(DAC) | 强制访问控制(MAC) |
|---|---|---|
| 谁决定 | 文件属主决定谁能访问 | 系统统一策略决定 |
| 依据 | 权限位(rwx)、owner/group | 主体(进程)域、客体类型 |
| 例子 | chmod、chown |
SELinux 的类型/布尔值 |
| 局限 | 进程被攻破拿到 root 即可越权 | 即使进程是 root,仍受策略限制 |
- DAC:你(属主)说了算——这就是我们《权限管理》里学的 rwx/owner/group。
- MAC:系统策略说了算——即使进程以 root 运行,SELinux 仍按类型/布尔值判断能否访问。
1.3 它解决什么问题
传统 Linux 下,如果一个 Web 服务被攻破并获得 root,攻击者就能读写任何文件。SELinux 让每个进程(域)只能做策略允许的事——即使拿到 root,也被限制在策略范围内。
一句话:SELinux 是"即使你拿到 root 也越不了权"的最后一道防线。
参考:Red Hat - Getting started with SELinux
二、三种模式:Enforcing / Permissive / Disabled
2.1 三种模式的含义
| 模式 | 行为 | 用途 |
|---|---|---|
| Enforcing(强制) | 按策略强制拒绝,并记录 AVC 日志 | 生产环境默认 |
| Permissive(宽容) | 只记录不拒绝(不合规的也放行,但记日志) | 排障、测试 |
| Disabled(禁用) | 完全关闭 SELinux | 极少用 |
2.2 查看与临时切换
getenforce # 当前模式
sudo setenforce 0 # 临时切到 Permissive(重启失效)
sudo setenforce 1 # 临时切回 Enforcing
2.3 持久配置(/etc/selinux/config)
sudo vim /etc/selinux/config
# SELINUX=enforcing # 可改为 permissive / disabled
# SELINUXTYPE=targeted # 策略类型,默认 targeted
- 持久修改需重启才生效(
setenforce只改运行时)。 - 从 Disabled 切到 Enforcing 会触发全盘 relabel(重新打标签),可能耗时较长。
排障技巧:遇到"SELinux 拦了我",先
setenforce 0到 Permissive 验证——如果切宽容后问题消失,说明确实是 SELinux 拦的,再按后文方法精准修复(而不是长期留在 Permissive/Disabled)。
参考:Red Hat - SELinux 模式
三、核心模型:类型、主体、客体与策略
3.1 三个核心角色
- 主体(subject):进程。运行在某个 domain(域/类型) 中,如
httpd_t(Web 服务域)、sshd_t。 - 客体(object):文件、目录、端口、进程等。有类型(type),如
httpd_sys_content_t(Web 内容)。 - 策略(policy):一堆 allow 规则,形如
allow domain type:class {permissions},决定"哪个域能对哪类客体做什么"。
3.2 allow 规则长什么样
allow httpd_t httpd_sys_content_t:file { read getattr open };
# 意思是:允许 httpd_t 这个域 读取 httpd_sys_content_t 类型的文件
SELinux 就是根据这些规则,判断"主体域能否对客体类型做某操作"。
3.3 targeted 策略与 unconfined
- 默认策略是 targeted:只约束特定的"目标"域(httpd_t、sshd_t 等),其余不受限。
- unconfined:不受 SELinux 约束的进程(管理员 shell 等),基本等同于传统 Linux 行为。
3.4 为什么"类型"决定能不能读
- 同一个文件,权限位(DAC)是"任意用户可读",但 SELinux(MAC)可能只允许特定域读。
- 例子:
nginx能读/var/www/html(标签httpd_sys_content_t),却读不了/home(标签是user_home_t等),因为httpd_t域没有读user_home_t的规则——即使权限位允许。
参考:Red Hat - SELinux 概念与上下文
四、上下文与文件标签:ls -Z / restorecon / chcon / semanage fcontext
4.1 什么是 SELinux 上下文
每个文件、进程、端口都有一个 SELinux 上下文(context),用 -Z 选项查看:
ls -Z /var/www/html/index.html
# user:role:type:level
# system_u:object_r:httpd_sys_content_t:s0
四元组:user:role:type:level——其中决定能否访问的通常是 type(类型)。
4.2 查看各类客体的上下文
ls -Z # 文件
ps -eZ # 进程(看 domain)
ss -Z # 端口
id -Z # 当前 shell 的上下文
4.3 标签不对会怎样
如果文件标签错误(比如 cp 一个文件到 /var/www/html,标签却是来源目录的),进程即使有权限也可能被 SELinux 拦:
cp /tmp/index.html /var/www/html/ # 标签可能是 tmp_t,不是 httpd_sys_content_t
sudo restorecon -Rv /var/www/html # 修正为策略默认标签
restorecon:按策略默认规则恢复标签(推荐、幂等)。chcon:临时改标签(重启/relabel 会复位,少用)。
4.4 持久修改标签:semanage fcontext
要永久给某个自定义目录设置标签,用 semanage fcontext + restorecon:
# 把 /opt/myweb 设为 httpd 可读内容类型
sudo semanage fcontext -a -t httpd_sys_content_t "/opt/myweb(/.*)?"
sudo restorecon -Rv /opt/myweb
# 查看已有规则
sudo semanage fcontext -l | grep /opt/myweb
为什么用
semanage fcontext+restorecon:chcon只改当前,一重启/relabel 就复位;semanage fcontext写进策略默认规则,restorecon按它恢复,才能持久。
参考:Red Hat - 文件标签
五、布尔值:setsebool / getsebool / semanage boolean
5.1 什么是布尔值
布尔值(boolean) 是 SELinux 策略里的开关,用于在不编写新策略的情况下,动态启用/禁用某个功能。这是解决"某个服务被拦"最常用的手段。
5.2 查看与修改
getsebool -a # 查看所有布尔值
getsebool httpd_can_network_connect # 查看单个
# 临时修改(重启失效)
sudo setsebool httpd_can_network_connect 1
# 永久修改(-P 写进策略,重启保留)
sudo setsebool -P httpd_can_network_connect 1
# 用 semanage 查看带说明
sudo semanage boolean -l | grep httpd
5.3 高频常用布尔值
| 布尔值 | 作用 | 场景 |
|---|---|---|
httpd_can_network_connect |
允许 Web 服务对外发起网络连接 | nginx/apache 反向代理 |
httpd_can_network_connect_db |
允许 Web 服务连接数据库 | PHP 连 MySQL 等 |
samba_enable_home_dirs |
允许 Samba 共享用户家目录 | Samba |
nfs_export_all_rw |
允许 NFS 导出 rw 共享 | NFS |
httpd_read_user_content |
允许 Web 读用户目录内容 | 多用户虚拟主机 |
- 呼应本系列:《nginx》里反代连不上,就是
setsebool -P httpd_can_network_connect 1。 - 布尔值改完即时生效,无需重启服务。
参考:Red Hat - SELinux 布尔值
六、排障:AVC 拒绝、ausearch、sealert、audit2why
6.1 AVC 拒绝消息在哪
SELinux 拒绝访问时会写 AVC(Access Vector Cache) 日志。通常落在:
/var/log/audit/audit.log # auditd 审计日志(最常查)
journalctl -t avc # systemd journal 里的 AVC 消息
/var/log/messages # 部分系统也写这里
6.2 常用排障命令
# 查看最近被拒绝的 AVC 记录
sudo ausearch -m avc -ts recent
# 查看针对某进程的拒绝
sudo ausearch -m avc -c httpd
# 用 sealert 生成人类可读的分析与修复建议
sudo sealert -a /var/log/audit/audit.log
# 用 audit2why 解释某条拒绝的原因
sudo ausearch -m avc -ts recent | audit2why
# 直接生成允许策略(进阶,见第八章)
sudo ausearch -m avc -ts recent | audit2allow -M myapp
6.3 排障三步法
- 确认是 SELinux 拦的:
setenforce 0到 Permissive,问题消失则确认。 - 看拒绝原因:
ausearch -m avc -ts recent+sealert -a/audit2why,弄清"哪个域访问哪个类型被拒、缺什么权限"。 - 精准修复:按原因选一种——
- 标签不对 →
semanage fcontext+restorecon(第四章); - 需要开关 →
setsebool -P 布尔值 1(第五章); - 端口不对 →
semanage port -a(第七章); - 确实需要 →
audit2allow自定义策略(第八章)。
- 标签不对 →
常见错误做法:一遇到被拦就
setenforce 0或改 Disabled。正确做法是定位后用最小改动精准修复,保持 Enforcing。
参考:Red Hat - Analyzing SELinux denial messages
七、实战:常见服务场景全流程
下面把前几篇串起来,按"症状→诊断→修复→验证"走完常见场景。
7.1 场景一:nginx 读不了自定义目录
# 症状:nginx 访问 /opt/myweb 返回 403 或 404,但权限位没问题
# 诊断:看标签
ls -Zd /opt/myweb # 期望 httpd_sys_content_t,可能是 default_t
# 修复:持久设置标签
sudo semanage fcontext -a -t httpd_sys_content_t "/opt/myweb(/.*)?"
sudo restorecon -Rv /opt/myweb
# 验证:再看标签 + 重载 + 访问
sudo systemctl reload nginx
curl -I http://127.0.0.1/
7.2 场景二:nginx 反向代理连不上后端
# 症状:nginx 反代到 127.0.0.1:8080 报 (13) Permission denied
# 诊断:ausearch 看拒绝,确认是 connect 被拒
sudo ausearch -m avc -ts recent | grep connect
# 修复:开布尔值(Web 服务允许出站连接)
sudo setsebool -P httpd_can_network_connect 1
# 验证:重载后 curl 后端成功
sudo systemctl reload nginx
7.3 场景三:服务改到非标准端口
# 症状:把 httpd 监听端口改成 8080,但外部连不上(防火墙已放行)
# 修复:用 semanage port 把该端口加入 http 端口类型
sudo semanage port -a -t http_port_t -p tcp 8080
# 查看
sudo semanage port -l | grep 8080
# 验证:systemctl restart httpd 后 curl
7.4 场景四:NFS 共享读写受限
# 症状:客户端挂载 NFS 后写不进去
# 修复:确认导出与布尔值
sudo setsebool -P nfs_export_all_rw 1
# 并确保服务端共享目录标签正确
每个场景的共同点:先诊断(看标签/布尔值/拒绝日志),再最小修复,最后验证。不要一上来就关 SELinux。
参考:Red Hat - SELinux 与各服务
八、进阶:audit2allow 与自定义策略
8.1 为什么需要自定义策略
当标签、布尔值、端口都试过仍被拦,且确实需要该访问时,可以用 audit2allow 根据拒绝日志自动生成一个自定义策略模块。
8.2 用 audit2allow 生成并加载模块
# 1. 从拒绝日志生成策略模块
sudo ausearch -m avc -ts recent | sudo audit2allow -M myapp
# 生成 myapp.pp 和 myapp.te
# 2. 加载模块
sudo semanage module -a myapp.pp
# 3. 验证
sudo semanage module -l | grep myapp
8.3 谨慎使用
audit2allow是"根据日志放行",可能放行过宽。生成后建议先查看myapp.te内容,确认只放行了必要权限。- 优先考虑布尔值/标签这类受控方案;自定义策略是兜底。
- 卸载模块:
sudo semanage module -d myapp。
8.4 SELinux 用户与角色(了解即可)
id -Z # 当前 SELinux 用户
sudo semanage user -l # 查看 SELinux 用户
sudo semanage login -l # 查看系统用户与 SELinux 用户的映射
- 默认
unconfined_u(不受限管理员)。 - 进阶的强制环境会为不同用户分配受限角色(
user_u等),属于更高安全要求的场景。
参考:Red Hat - 自定义策略
九、常见问题排查清单
- 服务连不上/读不了,权限位却正常 → 八成是 SELinux。
setenforce 0验证,再看标签/布尔值。 - 改了配置不生效 → 分清:改
/etc/selinux/config要重启;setenforce只改运行时。 cp的文件被拦 → 标签可能是来源目录的;restorecon -Rv修正。- 反代/连数据库报 Permission denied →
setsebool -P httpd_can_network_connect(_db) 1。 - 自定义目录读不了 →
semanage fcontext+restorecon设标签。 - 改监听端口后连不上 →
semanage port -a -t 对应类型 -p tcp 端口。 - 找不到拒绝日志 → 检查
auditd是否运行;journalctl -t avc;或ausearch -m avc -ts recent。 - 一重启就从 Permissive 变回 Enforcing → 你只用了
setenforce(运行时);要持久改/etc/selinux/config。
十、总结与练习
10.1 核心速查
| 需求 | 命令 |
|---|---|
| 查看模式 | getenforce / sestatus |
| 临时切 Permissive | sudo setenforce 0 |
| 查看文件标签 | ls -Z 文件 |
| 修正标签 | sudo restorecon -Rv 目录 |
| 持久设标签 | sudo semanage fcontext -a -t 类型 路径 |
| 开布尔值(永久) | sudo setsebool -P 布尔值 1 |
| 加端口 | sudo semanage port -a -t 类型 -p tcp 端口 |
| 看拒绝 | sudo ausearch -m avc -ts recent |
| 看建议 | sudo sealert -a /var/log/audit/audit.log |
| 生成策略 | sudo ausearch -m avc -ts recent | sudo audit2allow -M 名称 |
10.2 动手练习建议
# 1. 看状态
getenforce; sestatus
# 2. 看文件/进程/端口的标签
ls -Z /etc/passwd /var/www/html
ps -eZ | head
# 3. 体验"类型决定访问":建一个自定义目录,让 nginx 读(场景一)
# 4. 体验布尔值:nginx 反代(场景二)
# 5. 看拒绝日志(触发一次被拦后 ausearch)
# 6. 排障三步:setenforce 0 验证 → ausearch/audit2why 定位 → 精准修复
提示:练习在虚拟机进行。保持 Enforcing,用排障三步法精准修复,避免直接关 SELinux——这才是生产环境正确的做法。
参考资料
- Red Hat - Using SELinux(RHEL 9):https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/using_selinux/
- Red Hat - Analyzing SELinux denial messages:https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/using_selinux/analyzing-selinux-denial-messages_using-selinux
- Red Hat - SELinux Users and Administrators Guide(RHEL 7):https://docs.redhat.com/zh-cn/documentation/Red_Hat_Enterprise_Linux/7/html/selinux_users_and_administrators_guide/
- Rocky Linux - Learning SELinux:https://docs.rockylinux.org/guides/security/learning_selinux/
- 菜鸟教程 - Linux 教程:https://www.runoob.com/linux/linux-tutorial.html
