SELinux(Security-Enhanced Linux)是 Linux 内核中一套强制访问控制(Mandatory Access Control,MAC)框架,它独立于传统的 Unix 用户/组权限体系,为系统提供更细粒度的安全约束。即使某个进程以 root 身份运行,SELinux 也能限制它能访问的资源,从而在服务被攻破时大幅降低损失。
1. SELinux 的作用:额外一层安全边界
传统权限控制(DAC)仅根据文件的所有者、所属组和 rwx 权限决定访问许可,而 root 用户几乎无所不能。一旦 Apache 或 Nginx 等网络服务被远程攻破,攻击者往往直接获得该服务进程的权限——如果服务以 root 运行,整个系统立即沦陷。
SELinux 的策略为每个进程和文件定义了一个安全上下文(security context),包括用户、角色、类型和可选的安全级别,其中类型(type)是策略中最常用的部分。内核根据策略规则决定“哪些类型的主体(进程)能对哪些类型的目标(文件、端口等)做哪些操作”。例如:
- httpd 进程的类型为
httpd_t; - Web 内容目录
/var/www/html的类型为httpd_sys_content_t; - 策略允许
httpd_t读取httpd_sys_content_t类型的文件,但禁止httpd_t写入/etc/shadow(类型为shadow_t),更不允许访问用户家目录下的.ssh密钥。
因此,即便攻击者利用了 Web 服务的漏洞获取了 httpd_t 权限,他依然被 SELinux 限制在策略允许的范围内,难以读取密码文件、横向移动到其他服务或执行系统管理操作。
2. 三种模式及模式切换
SELinux 有三种工作模式:
- Enforcing(强制模式):策略被强制执行,违规操作会被拒绝并记录日志。这是生产环境的推荐模式。
- Permissive(宽容模式):策略不强制执行,但违规操作会被记录到日志。常用于临时调试和策略生成,也适合在开启 SELinux 前评估影响。
- Disabled(禁用模式):SELinux 完全关闭,内核不再加载任何策略,标签也不会被应用到文件和进程上。从 Disabled 切换到 Enforcing 需要重启系统并重新标记文件;从 Permissive 或 Enforcing 切换到 Disabled 通常也需要重启(部分发行版通过内核参数控制)。
查看当前模式:
getenforce
输出可能是 Enforcing、Permissive 或 Disabled。
临时切换模式(立即生效,重启后失效):
sudo setenforce 0 # 切换到 Permissive(0 或 Permissive)
sudo setenforce 1 # 切换到 Enforcing(1 或 Enforcing)
永久修改模式:需编辑配置文件 /etc/selinux/config(或 /etc/sysconfig/selinux,因发行版而异),修改 SELINUX= 行:
SELINUX=enforcing
# 或 permissive
# 或 disabled
保存后重启生效。注意:从 Disabled 切换到 Enforcing/Permissive 时,系统会在启动过程中自动重新标记文件系统(autorelabel),这会花费一些时间,重启时请耐心等待。
3. 常见问题与处理思路
在启用 SELinux 的系统中,许多“权限不足”的问题实际上源于 SELinux 策略阻止,而非传统权限错误。
a) 查看 SELinux 日志,定位被拒操作
SELinux 的拒绝记录会发送到审计日志 /var/log/audit/audit.log。使用 ausearch 或 sealert 工具可以快速过滤:
sudo ausearch -m avc -ts recent # 查看最近的 AVC 拒绝记录
sudo tail -f /var/log/audit/audit.log | grep AVC
多数现代发行版也把精简信息写入系统日志,可使用 journalctl 查看:
sudo journalctl -t setroubleshoot --since "10 minutes ago"
一条典型的拒绝日志类似:
type=AVC msg=audit(...): avc: denied { read } for pid=1234 comm="nginx" name="index.html" dev="sda1" ino=56789 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file
关键字段:
scontext(源上下文):httpd_t(进程类型)tcontext(目标上下文):default_t(文件类型)tclass:目标类别(这里是文件)denied { read }:被拒绝的操作是读
b) 调整文件上下文(常见解决方法)
如果确定某个操作合法,需要让策略允许,通常是修改目标文件的 SELinux 类型。
恢复文件默认上下文:使用 restorecon 将文件或目录重置为系统策略定义的默认类型。
sudo restorecon -Rv /var/www/html
临时改变类型(测试用):使用 chcon 可临时修改,但容易被系统重贴时覆盖。
sudo chcon -t httpd_sys_content_t /path/to/file
永久修改文件上下文映射:通过 semanage fcontext 添加一条本地规则,然后执行 restorecon 使其生效。
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/myapp(/.*)?"
sudo restorecon -Rv /var/www/myapp
c) 布尔值快速调整策略行为
许多常见应用(如允许 httpd 连接数据库、允许 nfs 挂载家目录)被封装为布尔开关,无需编写复杂策略。
# 查看所有 SELinux 布尔值
sudo getsebool -a
# 例如,允许 Apache 发起网络连接
sudo setsebool -P httpd_can_network_connect on
-P 表示永久生效(重启后保留)。
d) 排查思路总结
当遇到“权限被拒绝”但传统权限看似正常时:
- 先确认 SELinux 是否在 Enforcing 模式(
getenforce)。 - 临时切换至 Permissive 模式(
sudo setenforce 0),重新尝试操作。如果成功,说明是 SELinux 策略导致。 - 查看审计日志,获取被拒绝的详细信息。
- 根据日志中的源类型和目标类型,判断是文件上下文错误、端口标签错误还是需要开启某个布尔值。
- 修复后,务必切换回 Enforcing 模式(
sudo setenforce 1)测试。
SELinux 并不是一个“要么完全禁用、要么完美运行”的产物,通过简单的模式切换和日志分析,完全可以在保持安全增强的前提下逐步消除策略冲突。理解它的基本工作方式,是把 Linux 安全从“门锁”提升到“房间级隔离”的关键一步。