← 回目录
19 Week 04 · 网络排查

SELinux:那个老让人想关掉的东西

红帽系独有,Ubuntu 上没有——所以面试官特别爱问,而且Ubuntu 出身的候选人答不上来。这是你的机会。

它到底管什么

和普通权限的区别
两套权限系统,同时生效
普通权限(Day 06 学的)
基于用户和组:你是谁,能不能读这个文件。
SELinux
基于进程的类型nginx 这个进程,被允许访问哪一类文件跟你是不是 root 无关。
关键差别
两者是「与」的关系——普通权限过了,SELinux 还得再过一遍。这就是为什么会出现「权限看着完全正确,就是访问不了」。
它存在的意义

假设 nginx 被攻破了,攻击者拿到了 nginx 用户的权限。普通权限下,他能读所有 nginx 能读的东西。

但 SELinux 会限制 nginx 进程「只能碰网页类型的文件」——就算被攻破,也读不了 /etc/shadow这是一层纵深防御。

三种模式

看和切
$ getenforce
Enforcing

$ sudo setenforce 0        # 临时切成 Permissive
$ sudo setenforce 1        # 切回 Enforcing

$ sestatus                 # 更详细的状态
三种模式的区别
Enforcing
强制——违规操作直接拦截,并记日志。生产默认就是这个。
Permissive
宽容——不拦截,但照样记日志排查时的利器:切到这个模式如果问题消失了,就证明是 SELinux 拦的。
Disabled
完全关闭。要改配置文件并重启才能进入/退出这个模式setenforce 切不过去。
setenforce 只是临时的

setenforce 0 重启后就恢复了。永久修改要改 /etc/selinux/config 里的 SELINUX=

但先别急着改那个文件——往下看为什么。

上下文:-Z 参数

今天的核心概念

SELinux 给每个文件和每个进程都打了一个标签,叫「上下文」。加 -Z 就能看到:

看文件的标签
$ ls -Z /usr/share/nginx/html/index.html
system_u:object_r:httpd_sys_content_t:s0  index.html
                   ↑ 这个 type 才是关键:网页内容类型

$ ps -eZ | grep nginx
system_u:system_r:httpd_t:s0  3412 ? nginx: master process
                   ↑ nginx 进程的类型

规则是:httpd_t 类型的进程,只被允许读 httpd_sys_content_t 类型的文件。标签对不上,就拒绝——哪怕权限是 777

典型故障:换了网页目录

最经典的 SELinux 坑
① 制造故障
# 把网页放到一个非标准位置
$ sudo mkdir -p /web
$ echo "hello" | sudo tee /web/index.html
$ sudo chmod 644 /web/index.html      ← 权限完全正确

# 改 nginx 配置指向 /web,然后重启
$ sudo systemctl restart nginx
$ curl -I http://127.0.0.1
HTTP/1.1 403 Forbidden
     ↑ 权限明明是对的,为什么还 403?
② 三步确认是不是 SELinux
# 第一步:临时切 Permissive 试试
$ sudo setenforce 0
$ curl -I http://127.0.0.1
HTTP/1.1 200 OK      ← 好了!说明就是 SELinux 拦的

# 第二步:切回来,看看标签差在哪
$ sudo setenforce 1
$ ls -Z /web/index.html
unconfined_u:object_r:default_t:s0  /web/index.html
                       ↑ default_t,不是 httpd_sys_content_t

# 第三步:看 SELinux 的拒绝日志
$ sudo ausearch -m avc -ts recent
type=AVC ... denied { read } for pid=3413 comm="nginx"
     name="index.html" scontext=...:httpd_t tcontext=...:default_t
③ 正确修复(不是关掉它)
# 给这个目录打上正确的类型标签
$ sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"

# 应用标签
$ sudo restorecon -Rv /web

$ ls -Z /web/index.html
system_u:object_r:httpd_sys_content_t:s0  /web/index.html

$ curl -I http://127.0.0.1
HTTP/1.1 200 OK
semanage 命令找不到?

它不在默认安装里:sudo dnf install -y policycoreutils-python-utils

两个常用工具

记住这两个就够日常用
restorecon -Rv 路径
把文件的标签恢复成系统默认该有的样子大部分 SELinux 问题一条这个就解决了——尤其是从别处 cpmv 过来的文件,标签往往是错的。
ausearch -m avc -ts recent
最近的 SELinux 拒绝记录avc 就是"访问被拒绝"的日志类型。
mv 和 cp 的一个区别

cp 过来的文件会继承目标目录的标签(通常是对的);mv 过来的文件保留原来的标签(经常是错的)。

「我把文件 mv 过去就 403 了」——这是高频场景,restorecon 一下就好。

布尔值:开关式的策略

另一类常见问题

有些行为不是标签问题,而是整类操作被默认禁止了,需要打开对应的开关:

典型场景
# 场景:nginx 要反向代理到后端,但连不上
$ getsebool -a | grep httpd | head
httpd_can_network_connect --> off
     ↑ 默认不允许 httpd 主动发起网络连接

# 打开它(-P 表示永久保存)
$ sudo setsebool -P httpd_can_network_connect on

「nginx 配了反向代理但 502」——如果后端服务是好的,十有八九就是这个布尔值没开。这个场景在实际工作中非常常见。

今天的面试点

一道送命题,一道加分题
「SELinux 你一般怎么处理?」

千万别答「直接关掉」——这是明确的减分项,会让对方觉得你不懂也不想懂。

该这么答:「先判断是不是它拦的——临时 setenforce 0 试一下,问题消失就说明是。然后看是哪类问题:文件标签不对就 restorecon 修,需要放开某类行为就查 getsebool 改布尔值。生产环境我不会直接 disable,那等于把一层防御拆了。」

「权限是 777 了,为什么还是访问不了?」

普通权限和 SELinux 是两套系统,都得过。777 只解决了第一层。」

「我会 ls -Z 看文件的 SELinux 类型对不对,再用 ausearch -m avc -ts recent 看有没有拒绝记录。如果文件是从别处 mv 过来的,标签通常是错的,restorecon 一下就好。

这个回答几乎立刻能证明你在红帽系上干过活。

今天的练习

敲完再走 · 约 15 分钟
  • getenforcesestatus,确认当前模式
  • ls -Z /usr/share/nginx/html/,看看标准网页目录的标签长什么样
  • ps -eZ | grep nginx,看 nginx 进程的类型是 httpd_t
  • 完整走一遍上面那个 /web 实验:建目录 → 配 nginx → 403 → setenforce 0 验证 → 切回来 → semanage + restorecon 修好
  • sudo ausearch -m avc -ts recent,看刚才产生的拒绝记录长什么样
  • getsebool -a | grep httpd,浏览一下有哪些开关
明天预告

Day 20 是第四周收尾:SSH 和密钥登录。这是运维每天都在用的东西,也是后面学 Ansible 的前提。

Day 06 那个「.ssh 必须 700、密钥必须 600」的坑,明天正式用上。