「服务改了配置就是起不来,日志也没报错」「明明给了 777 权限还是拒绝访问」——很多人被这类诡异问题坑过,最后发现是 SELinux 或 AppArmor 在「多管闲事」。它们是 Linux 的**强制访问控制(MAC)**机制,比传统的文件权限更底层、更严格。这一篇讲清 SELinux 和 AppArmor 的原理、配置和排错方法,让你不再把「关掉它」当成唯一解法。
一、什么是强制访问控制
传统的 Linux 权限(rwx、owner/group/other)叫 DAC(自主访问控制):文件属主可以随意决定谁能访问。问题在于——如果某个进程以 root 运行,它就能访问任何文件,一旦被攻破,危害巨大。
MAC(强制访问控制) 在 DAC 之上再加一层:由系统策略统一决定,进程能访问哪些资源,连 root 也要遵守。它就是「纵深防御」里的第 4 层(见第 79 篇)。
| 决定者 | 文件属主 | 系统策略 |
| root 能否绕过 | 能 | 不能 |
| 粒度 | 用户/组/其他 | 进程/路径/标签 |
| 目的 | 基本的访问控制 | 限制进程能力,防提权 |
二、两大流派
MAC 实现
├─ SELinux(红帽系:RHEL/CentOS/Rocky/Fedora)—— 基于「标签」
└─ AppArmor(Debian 系:Ubuntu/Debian/SUSE)—— 基于「路径」
| 发行版 | RHEL/CentOS/Rocky | Ubuntu/Debian |
| 模型 | 标签(label/context) | 路径(path) |
| 配置 | 复杂,策略语言 | 简单,可读文本 |
| 学习曲线 | 陡 | 缓 |
先确认你的系统用哪个:
# 看 SELinux
getenforce
# 看 AppArmor
aa-status
三、SELinux 基础
三种模式
getenforce
| Enforcing | 强制执行策略,违规被拒绝 |
| Permissive | 只记录不阻止(排错时用) |
| Disabled | 完全关闭 |
临时切换:
sudo setenforce 0 # 改 Permissive
sudo setenforce 1 # 改 Enforcing
配置文件 /etc/selinux/config(永久,需重启):
SELINUX=enforcing
SELINUXTYPE=targeted
⚠️ 排错黄金流程
服务报「Permission denied」时,先别急着关 SELinux,改用 Permissive 模式测试:
# 1. 切换 Permissive
sudo setenforce 0
# 2. 重启服务,如果好了 → 是 SELinux 问题
sudo systemctl restart myservice
# 3. 切回 Enforcing,查看拒绝日志找真因
sudo setenforce 1
sudo ausearch -m avc -ts recent
记住:Permissive 是「排错工具」,不是「解决方案」。 找到真因后要么调上下文、要么开布尔值、要么写策略,而不是永久禁用。
查看拒绝日志
# 看 AVC 拒绝记录
sudo ausearch -m avc -ts recent
sudo journalctl -t audit | grep AVC
# 用 sealert 得到人类可读的建议
sudo dnf install setroubleshoot-server -y
sudo sealert -a /var/log/audit/audit.log
AVC denied 就是「SELinux 拒绝了某操作」,日志里会写清楚「哪个进程、对哪个资源、做了什么操作」以及修复建议。
四、SELinux 上下文与布尔值
SELinux 的核心是标签(context)。每个进程和文件都带一个安全上下文,格式:
user:role:type:level
system_u:object_r:httpd_sys_content_t:s0
排障最关注的是 type 字段(第三个)。
查看上下文
# 文件上下文
ls -Z /var/www/html/index.html
# system_u:object_r:httpd_sys_content_t:s0 …
# 进程上下文
ps -eZ | grep nginx
# system_u:system_r:httpd_t:s0 nginx
规则:进程的 type(如 httpd_t)必须能访问文件的 type(如 httpd_sys_content_t),否则被拒。
修改文件上下文
场景: 把网站文件放到 /web/ 而不是默认的 /var/www/html/,nginx 访问被拒。
# 查看应该用什么上下文
semanage fcontext -l | grep httpd_sys_content
# 把 /web 目录标记为 web 内容类型
sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
# 应用规则到实际文件
sudo restorecon -Rv /web
关键点: semanage fcontext 是持久规则,restorecon 把规则应用到现有文件。只做前者不生效,只做后者重启可能丢,两者都要。
布尔值(boolean):开关式策略
SELinux 有一些「即开即用」的开关,对应常见需求:
# 列出所有布尔值
getsebool -a
# 查看 web 相关
getsebool -a | grep httpd
# 开启「允许 httpd 连接网络」
sudo setsebool -P httpd_can_network_connect on
-P 表示持久化(重启后仍生效)。
典型场景:
| web 服务连数据库 | httpd_can_network_connect_db on |
| web 服务访问外网 | httpd_can_network_connect on |
| ftp 读写家目录 | ftp_home_dir on |
| 允许 http 写目录 | httpd_unified on |
先查布尔值,再改上下文,最后才考虑写策略——这是 SELinux 排错的优先级。
五、SELinux 常见排错
1. 服务起不来(端口问题)
场景: 给 nginx 配了 8080 端口,起不来。
nginx: [emerg] bind() to 0.0.0.0:8080 failed (13: Permission denied)
原因: SELinux 只允许 httpd 绑定特定端口。
# 查看 httpd 允许的端口
semanage port -l | grep http_port_t
# 把 8080 加入 httpd 允许端口
sudo semanage port -a -t http_port_t -p tcp 8080
2. 文件访问被拒
场景: 网站文件放自定义目录,403。
# 查看 AVC 日志确认
sudo ausearch -m avc -ts recent | grep nginx
# 修正上下文(见上文)
sudo semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
sudo restorecon -Rv /web
3. 服务重启后上下文丢失
cp/mv/tar 有时不带上下文,或文件系统不保留。用 restorecon 重建:
sudo restorecon -Rv /path
4. 实在要用「文档化」的关闭
如果确认某策略不适用于业务,且不想关全局,可以:
# 只对特定服务设白名单(推荐)
semanage permissive -a httpd_t
# 完全禁用(不推荐,仅测试)
setenforce 0
permissive 只让 httpd_t 域宽容,其他仍受保护,比全局关闭安全得多。
六、AppArmor 基础
Ubuntu/Debian 默认用 AppArmor,它基于路径而不是标签,profile 是纯文本,易懂。
查看状态
sudo aa-status
apparmor module is loaded.
42 profiles are loaded.
38 profiles are in enforce mode.
/usr/sbin/nginx
/usr/bin/man
…
4 profiles are in complain mode.
两种模式
| enforce | 强制执行,违规拒绝 |
| complain | 只记录不阻止(排错用) |
切换模式
# 改某 profile 为 complain(排错)
sudo aa-complain /usr/sbin/nginx
# 改回 enforce
sudo aa-enforce /usr/sbin/nginx
# 禁用某 profile
sudo ln -s /etc/apparmor.d/usr.sbin.nginx /etc/apparmor.d/disable/
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.nginx
排错
# 查看拒绝日志
sudo dmesg | grep -i apparmor
sudo journalctl | grep apparmor
sudo dmesg | grep "apparmor=\\"DENIED\\""
生成/调整 profile
# 让 AppArmor 学习一个程序的行为
sudo aa-genprof /usr/bin/myapp
# 从日志自动生成 profile
sudo aa-logprof
七、MAC 加固的正确心态
| 出问题就 Disabled/disable | ❌ 等于拆掉一层防线 |
| 保持 Enforcing,用 Permissive 定位 | ✅ 正确姿势 |
| 先调上下文/布尔值 | ✅ 优先方案 |
| 为特定服务设 permissive | ✅ 折中 |
| 写自定义策略 | ✅ 终极方案(复杂) |
SELinux/AppArmor 的初衷是「即使服务被攻破,也限制它能造成的破坏」。 关掉它,等于放弃了这层保护。正确的做法是理解它为什么拒绝,然后精准地放行必要操作。
八、⚠️ 常见错误
九、原理探究:安全上下文与策略匹配
SELinux 的标签模型
SELinux 给每个主体(进程)和每个客体(文件、端口、socket)都打一个安全上下文。访问时,内核做一件事:
主体(进程上下文)的操作 → 查策略 → 客体(文件/端口上下文)
决策不看你「是谁(root 还是普通用户)」,而看「进程带什么标签、文件带什么标签、策略允不允许这对标签交互」。
举例:
nginx 进程 上下文:system_u:system_r:httpd_t
网站文件 上下文:system_u:object_r:httpd_sys_content_t
策略:允许 httpd_t 读 httpd_sys_content_t → 通过 ✓
/etc/shadow 上下文:system_u:object_r:shadow_t
策略:不允许 httpd_t 读 shadow_t → 拒绝 ✗
这就是为什么 nginx(哪怕以 root 运行)也读不了 /etc/shadow——不是权限问题,是 SELinux 策略层面根本不允许。这就是「强制访问控制」的威力:连 root 都要服从策略。
为什么按「端口」也要管
不光是文件,端口也有标签。http_port_t 是允许 httpd 绑定的端口集合。这样即使攻击者让 nginx 绑定到别的端口,也绑不上——限制的不是「谁能绑」,而是「这个进程能绑哪些」。
AppArmor 的路径模型
AppArmor 更直观,profile 里直接写路径规则:
/usr/sbin/nginx {
/etc/nginx/** r,
/var/www/** r,
/var/log/nginx/** w,
network tcp,
}
意思是「nginx 只能读 /etc/nginx、/var/www,写 /var/log/nginx,允许 TCP」。基于路径的好处是可读性好,缺点是路径变动(如软链接、mount)容易绕过。
两者对比的本质
- SELinux 基于标签:资源带标签,进程带标签,策略管「标签对标签」。强势但复杂。
- AppArmor 基于路径:策略管「进程对路径」。简单但不够细。
无论哪种,核心思想都是一样的:给进程划定一个「只能碰这些」的笼子,即使它被攻破,也翻不出这个笼子。 这就是「沙箱(sandbox)」和「最小权限」在网络访问控制层的落地。
和容器安全的关系
Docker/Kubernetes 的容器隔离,也会用到 SELinux/AppArmor:
# docker 指定 SELinux 标签
docker run –security-opt label=type:container_t ...
# docker 指定 AppArmor profile
docker run –security-opt apparmor=docker-default ...
K8s 的 Pod Security Context 也能指定 seLinuxOptions。理解 MAC,也是理解容器安全的基础。
十、SELinux/AppArmor 速查表
# ===== SELinux =====
getenforce # 看模式
setenforce 0 / 1 # 临时切 Permissive/Enforcing
ls -Z file # 看文件上下文
ps -eZ | grep proc # 看进程上下文
ausearch -m avc -ts recent # 看拒绝日志
semanage fcontext -a -t TYPE "路径" # 加上下文规则
restorecon -Rv /path # 应用上下文
semanage port -a -t http_port_t -p tcp 8080 # 放开端口
getsebool -a | grep xxx # 看布尔值
setsebool -P xxx on # 开布尔值
semanage permissive -a domain_t # 单域宽容
# ===== AppArmor =====
aa-status # 看状态
aa-complain /path # 切 complain
aa-enforce /path # 切 enforce
dmesg | grep -i apparmor # 看日志
aa-genprof /usr/bin/app # 生成 profile
一句话收尾:SELinux 用「标签」、AppArmor 用「路径」,都是给进程划定活动范围,连 root 也管。 遇到「权限给了还被拒」,先想它俩;排错用 Permissive/complain 定位,再用 semanage/布尔值精准放行——别把关闭当解法,那等于自断一层防线。
至此,第八篇「网络配置、远程与安全篇」10 篇全部完成。下一篇进入第九篇「存储、磁盘、备份与恢复篇」,从磁盘分区开始,把存储这块硬骨头啃下来。
网硕互联帮助中心



评论前必须登录!
注册