本节摘要:SELinux 从"文件访问域"上限制容器能碰什么,seccomp 从"系统调用"上限制容器能做什么。Podman 在启用 SELinux 的发行版上默认打标签隔离,seccomp 默认 profile 与 Docker 同源但演进更激进。本节讲两道防线的机制、默认姿态、以及查日志定位拒绝原因的方法——安全配置的第一能力是"看懂内核为什么不让你"。
SELinux 的思路是把"主体能不能访问客体"从权限位层面抽走,交给独立的策略引擎判断。容器场景里,它给容器进程与文件分别贴标签:进程跑在容器域(container_t),数据被贴容器文件标签(container_file_t),策略规定容器域只能读写带容器标签的文件——宿主系统文件的其他标签一概拒绝。
这就解释了第 5.2 节的第一个坑:你把家目录文件挂进容器,文件带的是用户主目录标签,容器进程在容器域——策略拒绝。挂载选项 :Z 的本质是一次 chcon(改标签)操作:把目录贴成容器文件标签,域与标签匹配,放行。
看一次真实的拒绝记录,这是排错的抓手:
# 容器内尝试读宿主系统文件失败后,查审计日志 sudo ausearch -m avc -ts recent | tail -12 # type=AVC msg=audit(...): avc: denied { read } for # pid=... comm="cat" name="shadow" # scontext=system_u:system_r:container_t:s0:c123,c456 # tcontext=system_u:object_r:shadow_t:s0 # tclass=file # 解读:scontext 是容器进程的标签,tcontext 是目标文件的 # 标签,denied { read } 是被拒的操作——cat 一个 shadow 标签 # 文件被拒,这正是 SELinux 在拦"容器读系统敏感文件" # 两个引擎的对比点: # Docker:也需要手动处理标签(文档里的 --security-opt label=...), # 历史上默认行为各版本不一,社区教程里经常直接教用户关闭 SELinux # Podman:把标签操作做进了 -v 的 :Z/:z 语法,默认路径更顺 # 但两边的底层防线是同一个内核模块——SELinux 不关心引擎是谁
值得点破的一层对比:网上大量 Docker 教程以"关闭 SELinux 解决权限报错"收场,这是把防线拆了迁就症状。Podman 的工程选择是把正确动作做便捷(一个冒号),让"关掉防线"不再是最短路径。防线用不用得上是用户的事,但工具不该诱导用户拆防线。
seccomp 在比 SELinux 更细的粒度上工作:拦截进程发出的系统调用本身。默认 profile 是一份黑名单——放行绝大多数调用,拦截公认高危的一批(内核密钥环操作、旧式模块加载、若干历史漏洞相关的调用族)。Podman 与 Docker 的默认 profile 都源自 moby 项目的维护,但 Podman 更激进地跟进新内核的调用面(新增高危调用更早进黑名单)。
自定义的典型场景:某些运行时需要被默认黑名单拦掉的调用。比如老版本 JVM 的性能诊断工具依赖 perf_events 相关调用,容器里一跑就报操作不允许。两种修法:
# 修法一:为单个容器放宽(最小范围原则) podman run --security-opt seccomp=/path/to/unconfined-jvm.json \ --rm myapp:jdk8 ./run-diagnostics.sh # 自定义 profile 在默认基础上开少数口子 # 修法二:审计后确认不需要,直接禁用——不推荐,除非是一次性 # 调试容器且容器内没有不可信输入 podman run --security-opt unconfined --rm ... # 解读:unconfined 等于拆掉这道防线,审计意义上容器 # 重新获得全部系统调用能力
初学者常把两者混为一谈,用一张对照表分开:
| 维度 | SELinux | seccomp |
|---|---|---|
| 检查对象 | 文件等资源的访问(主体-客体) | 进程发出的系统调用 |
| 粒度 | 标签与域的策略匹配 | 按调用号逐个拦截 |
| 典型拦截 | 容器读宿主敏感文件 | 容器加载内核模块、操纵密钥环 |
| 拒绝的报错形态 | EACCES 加审计日志 | EPERM 或进程被杀 |
| 关闭的常见借口 | "挂载目录权限报错" | "JVM 工具跑不起来" |
| 正确的松绑方式 | :Z/:z 或 audit2allow | 自定义 profile 开口子 |

背景:同事的容器在 RHEL 服务器上读挂载目录报 Permission denied,ls 看权限位完全正常(所有人可读),他准备提工单。操作:先复现——podman run -v /opt/shared:/s alpine cat /s/conf,失败;再查审计——ausearch 里出现 avc denied,scontext 是 container_t,tcontext 是 usr_t(/opt 下文件的默认标签);结论:不是权限位问题,是 SELinux 策略拒绝,目录没贴容器标签。处置:数据目录加 :Z 重跑,成功。解读:权限位是 DAC(自主访问控制)层面的检查,SELinux 是 MAC(强制访问控制)层面,两层都放行才真放行;报错信息同为 Permission denied,必须查日志才能分层。变式:如果目录必须保持原标签(系统工具也在用它),就不能 :Z,改为容器侧用 audit2allow 生成定制策略——但那是最后一招,优先调整数据目录布局。