Docker 和 Podman 中的 Linux Capabilities

在运行 Docker (或 Podman) 容器时,有时即使使用 root 用户或者使用了 sudo,也会出现 Operation not permitted 的错误信息。这是因为容器中的 root 并不具有完整的 root 权限。这种权限的控制是通过 Linux capabilities 实现的。本文将首先介绍 Linux capabilities 的概念,然后以 Docker 为例介绍如何调整容器的 capabilities,最后介绍 Docker 和 Podman 在默认 capabilities 上的差异,以为容器的开发者和用户提供参考。

Linux Capabilities

经典的 Linux 权限控制模型将用户分为普通用户和特权用户 (例如 root 用户和具有 sudo 权限的用户)。特权用户拥有系统上的所有权限,容易导致安全问题。例如,一个 Web 服务器需要特权以监听 443 或 80 端口,但不应当访问其他用户的文件或修改系统内核;而如果 Web 服务器被攻破,攻击者将获得系统上的所有权限。

Linux capabilities 将系统中的特权分为多个不同的 capabilities,可以通过赋予进程部分特权而不是完整的 root 权限来降低系统的风险。例如,CAP_NET_BIND_SERVICE 允许进程绑定小于 1024 的端口,而不需要完整的 root 权限。Linux capabilities 的列表可以通过 man 7 capabilities 查看。

Docker 中的 Linux Capabilities

与服务器不同,容器不需要完整的 root 权限,因为容器的目的是运行一个或一些特定的应用程序,而不是整个系统。例如:

  • 容器通常不需要管理网络和日志,因为容器的网络和日志是由 Docker Engine 管理的。
  • 容器通常不需要设置时间,因为它的时间由宿主机提供
  • 容器通常不需要运行 reboot 命令,因为容器的生命周期由 Docker Engine 管理

因此,Docker 通过白名单的方式限制了容器的 capabilities,即容器默认仅具有特定的 capabilities。Docker 所使用的 capabilities 可以在这里查看

这里提供的链接仅代表写作本文时 Docker 的默认 capabilities。随着 Docker 的版本更新,这些默认值可能会发生变化,你可以通过查看最新版本的 Docker 源码来获取最新的默认 capabilities。

如果你希望进一步限制容器的 capabilities 以增加安全性,可以通过 --cap-drop 选项来删除 capabilities。如果容器中的程序确实需要某些 Capabilities,可以通过 --cap-add 选项添加这些 capabilities。对于容器的开发者,在需要额外的 capabilities 时,建议在 README 中明确说明。

谨慎使用 --privileged 选项

--privileged 选项会赋予容器完整的 root 权限,导致容器的隔离性受损,因此,除非必要,不要使用 --privileged 选项。即使你信任容器的开发者没有恶意,也不代表他的代码不会有错误或漏洞。

Podman 与 Docker 的 Capabilities 差异

Podman 通过进一步限制容器的 capabilities 来获得比 Docker 更高的安全性。Podman 的 capabilities 默认值可以在这里查看

Podman 的默认 capabilities 比 Docker 更加严格,因此在 Podman 中运行的容器可能会出现更多的 Operation not permitted 错误,例如 sudo 将无法使用 CAP_AUDIT_WRITE Capability。Podman 用户在运行容器时如果遇到 Operation not permitted 错误而其他人却无法复现,很有可能就是 Podman 的 capabilities 额外限制导致的。

参考资料

  1. capabilities(7) - Linux manual page
  2. Linux kernel capabilities - Docker docs
作者

Cao Mingjun

发布于

2024-09-27

更新于

2024-09-27

许可协议

评论