Podman 挂载目录 Permission Denied:SELinux 导致的一个坑

使用 Podman 挂载主机目录到容器时,即使容器内是 root 用户,也可能遇到 Permission denied 错误。本文将解释这一现象的根源——SELinux,并提供修复方法。

问题场景

我在配置一个 Quadlet 管理的服务,Quadlet .container 文件大致如下:

[Container]
Entrypoint=/app/entry.sh
Image=docker.io/library/alpine:3.15
Pod=sniproxy.pod
Volume=/home/nite/pod/sniproxy/data/bin:/app:ro
Volume=/home/nite/pod/sniproxy/data/etc:/config:ro

[Service]
Restart=always

[Install]
WantedBy=default.target

配置完成后,systemctl --user daemon-reloadsystemctl --user start sniproxy.service,服务启动失败。

通过 journalctl 查看日志,发现是 Permission denied 错误。为验证,我将 Entrypoint 注释掉,设置 Exec=sleep infinity,重启服务后 podman exec 进入容器,执行 ls -l /app 同样报 Permission denied

容器内是 root 用户,传统的文件权限(owner、group、other)检查应该放行,问题显然出在别处。

根本原因:SELinux

在 Fedora、CentOS、RHEL 等启用了 SELinux 的发行版上,文件访问受到 SELinux 安全上下文的额外约束。

Podman 启动容器时,容器内的进程会被赋予一个 SELinux 标签,通常是 container_t。而主机家目录下创建的目录(如 ~/sniproxy/data),默认标签是 user_home_t

SELinux 的默认策略明确禁止持有 container_t 标签的进程访问 user_home_t 标签的目录。这是出于安全考虑:防止容器内的进程(即使被攻破)读取或破坏主机上的个人文件。

所以Permission denied 并不是因为传统权限不够,而是 SELinux 策略阻止了 container_tuser_home_t 的访问。

解决方案:修改 SELinux 标签

解决方法是让 Podman 在挂载时修改目录的 SELinux 标签。在卷定义的末尾加上:

  • :z:将目录标签改为 container_file_t,这是一个共享标签,多个容器都可以访问。
  • :Z:为目录创建一个私有标签,仅该容器可访问。

对于大多数场景,:z 是最常用的选择。

修改前

Volume=/home/nite/pod/sniproxy/data/bin:/app:ro
Volume=/home/nite/pod/sniproxy/data/etc:/config:ro

修改后

Volume=/home/nite/pod/sniproxy/data/bin:/app:ro,z
Volume=/home/nite/pod/sniproxy/data/etc:/config:ro,z

修改完 Quadlet 文件后,执行:

systemctl --user daemon-reload
systemctl --user restart sniproxy.service

现在 podman exec 进入容器,ls -l /app 可以正常工作了。