它到底管什么
- 普通权限(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 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。
典型故障:换了网页目录
# 把网页放到一个非标准位置
$ 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?
# 第一步:临时切 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
它不在默认安装里:sudo dnf install -y policycoreutils-python-utils
两个常用工具
- restorecon -Rv 路径
- 把文件的标签恢复成系统默认该有的样子。大部分 SELinux 问题一条这个就解决了——尤其是从别处
cp或mv过来的文件,标签往往是错的。 - ausearch -m avc -ts recent
- 看最近的 SELinux 拒绝记录。
avc就是"访问被拒绝"的日志类型。
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」——如果后端服务是好的,十有八九就是这个布尔值没开。这个场景在实际工作中非常常见。
今天的面试点
千万别答「直接关掉」——这是明确的减分项,会让对方觉得你不懂也不想懂。
该这么答:「先判断是不是它拦的——临时 setenforce 0 试一下,问题消失就说明是。然后看是哪类问题:文件标签不对就 restorecon 修,需要放开某类行为就查 getsebool 改布尔值。生产环境我不会直接 disable,那等于把一层防御拆了。」
「普通权限和 SELinux 是两套系统,都得过。777 只解决了第一层。」
「我会 ls -Z 看文件的 SELinux 类型对不对,再用 ausearch -m avc -ts recent 看有没有拒绝记录。如果文件是从别处 mv 过来的,标签通常是错的,restorecon 一下就好。」
这个回答几乎立刻能证明你在红帽系上干过活。
今天的练习
getenforce和sestatus,确认当前模式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」的坑,明天正式用上。