
Enforce 模式下文件读写拦截为什么没生效
常见现象是策略写了 deny /etc/shadow r,但容器里仍能 cat /etc/shadow 成功。根本原因不是规则无效,而是策略没真正加载进内核强制路径,或进程没绑定到该 profile。AppArmor 的 enforce 模式只对“已关联 profile 且处于 enforce 状态”的进程起作用,不靠文件路径自动匹配——必须确保目标进程的 /proc/1/attr/current 显示 profile 名称 + (enforce)。
确认进程是否真受 enforce 策略约束
别只看 aa-status 列出 profile 名单,得验证具体进程:
- 查主进程当前 profile:
sudo cat /proc/1/attr/current(容器内)或sudo cat /proc/$(pgrep -f "nginx")/attr/current(宿主机) - 输出应为类似
/usr/sbin/nginx (enforce);若显示(complain)或为空,说明没生效 -
sudo aa-status --verbose | grep -A5 "nginx"可看详细绑定状态和规则计数 - 若 profile 名称含空格或特殊字符,
aa-status可能漏显示,必须查/proc/*/attr/current
deny 规则被绕过的典型路径
AppArmor 的 deny 是“后匹配”规则,优先级低于显式 allow;只要前面有更宽泛的 allow 覆盖了同一路径,deny 就不会触发。常见踩坑点:
-
/etc/** rw,允许了整个/etc目录,后面加deny /etc/shadow r,无效——因为**已覆盖该路径 - 使用
/**或/*这类通配符太宽,建议改用精确路径或最小化递归,如/etc/nginx/** rw,而非/etc/** rw, - 没禁掉符号链接跳转:如果
/tmp/link → /etc/shadow,而策略只 deny/etc/shadow,但允许/tmp/**,攻击者可通过 link 绕过 - 忘记 deny
/{,var/}run/**和/dev/**,某些服务会通过这些路径间接访问敏感资源
生产环境 enforce 下阻断越权读写的实操要点
直接上线 enforce 容易导致服务崩溃,必须按最小闭环验证:
- 先在 complain 模式下跑够 24 小时,用
sudo dmesg | grep avc | tail -20收集真实访问行为,再补 deny 规则 - deny 规则必须放在 profile 开头或所有 allow 同级位置,避免被覆盖;推荐统一写在 profile 顶部,加注释标明意图
- 对 root 用户主目录、
/home/**、/root/**、/proc/sys/**这三类路径,必须显式deny rwx,,不能依赖“默认不放行” - 容器场景中,若用 Docker,需确保启动时指定
--security-opt apparmor=xxx,否则默认走 unconfined profile - 每次 reload 后,立刻用
sudo aa-status | grep -E "(enforce|profile)"确认状态,再执行测试命令如dd if=/etc/shadow of=/dev/null 2>/dev/null && echo "leaked"
最易被忽略的是 profile 加载顺序和进程重绑定时机——服务重启不等于 profile 重载,systemctl restart 后必须手动 apparmor_parser -r,否则旧 profile 仍在内存中运行。











