<p>关键在于AppArmor策略必须使用容器内挂载后的路径(如/run/secrets/** r)而非宿主机路径,并显式声明与bind mount权限匹配的读写规则,避免与SELinux叠加拦截,推荐用bane工具结合--allow-path参数补全挂载路径权限。</p>

Docker Bind Mount 和 AppArmor 一起用,关键不是“能不能配”,而是“怎么配才不冲突、不越权、不被拦”。两者一个管路径映射,一个管文件访问权限,配合不当,容器一启动就报 Permission denied,日志里还找不到明确原因。
核心原则是:AppArmor 规则必须显式允许 Bind Mount 挂载点的读写行为,且路径需与挂载目标完全一致。
绑定挂载路径必须在 AppArmor 策略中显式声明
Bind Mount 把宿主机目录(比如 /home/user/app/config)挂进容器 /etc/myapp/,AppArmor 默认只认容器内部路径。如果你的策略只写了 /etc/myapp/** r,,但没说明这个路径实际来自 bind mount,内核可能因路径重解析失败而拒绝访问。
正确做法是:
- 在 AppArmor profile 中,用挂载后容器内看到的路径(即 target 路径)写规则;
- 权限要匹配实际使用方式(只读配置?可写日志?);
- 避免用
/**过度放行,优先按需细化。
例如,容器启动命令为:
docker run -v /host/secrets:/run/secrets:ro myapp
对应 AppArmor 策略片段应包含:
/run/secrets/** r,
而不是 /host/secrets/** —— 后者是宿主机路径,AppArmor 在容器命名空间里根本看不到。
注意 SELinux 或 AppArmor 的双重拦截风险
某些系统(如 RHEL/CentOS)默认启用 SELinux,而 Docker 启动时若未加 --security-opt label=disable,它会和 AppArmor 叠加生效。即使 AppArmor 放行了 /data/logs/ rw,,SELinux 的 container_file_t 上下文仍可能阻止写入。
验证方法:
# 查看是否被 SELinux 拦截 ausearch -m avc -ts recent | grep docker # 临时放宽(仅调试) docker run --security-opt label=disable -v /tmp/logs:/var/log/app myapp
生产环境建议统一用 AppArmor 或 SELinux 其一,避免策略打架。
自动生成策略时要注入挂载信息
手动写 AppArmor profile 容易漏掉 bind mount 路径。推荐用 bane 工具生成基础策略,但需额外传入挂载路径:
# 先运行容器获取进程模型(含挂载信息) docker run --rm -v /host/conf:/etc/app:ro myapp sleep 10 & # 抓取实际访问路径(需提前开启 auditd) sudo aa-logprof # 或用 bane + 显式路径白名单 bane --allow-path "/etc/app/** r" --allow-path "/var/log/app/ w," myapp
bane 默认只分析镜像内路径,不感知 -v 参数,所以必须人工补全挂载路径的权限声明。
常见失败场景与修复建议
挂载后文件打不开,日志显示
Operation not permitted
→ 检查 AppArmor 是否允许file_perm类型操作(如capability dac_override,通常不需要,除非程序调用setuid)日志目录挂载后无法写入,但
ls -l显示权限正常
→ AppArmor 缺少/{,var/}log/app/** rwk,(k表示 lock,日志轮转常用)配置文件挂载为只读,但 AppArmor 写了
w权限
→ 不冲突,但属于冗余;更危险的是反过来:挂载为rw,但策略只给了r,就会静默失败使用
:z或:Z标签(SELinux)时 AppArmor 生效异常
→ 避免混用;如必须用,先禁用 AppArmor 测试是否为 SELinux 导致,再单独调策略


















