核心是不用root运行且仅保留必需linux capabilities:默认14项能力多数冗余,应--cap-drop=all后精准--cap-add,配合--user、no-new-privileges、只读文件系统,并通过capsh --print验证生效。
要让 docker 容器真正符合最小权限原则,核心就两点:**不用 root 运行 + 只保留业务必需的 linux capabilities**。默认配置留了 14 个能力,很多根本用不上;而盲目加 cap-add 或开 --privileged,等于主动交出控制权。
明确业务真实需求,再反推需要哪些能力
别凭感觉加能力,先问清楚应用到底要做什么:
- 是否要绑定 80/443 端口?→ 只需 CAP_NET_BIND_SERVICE
- 是否要修改文件属主(比如初始化时 chown)?→ 可临时保留 CAP_CHOWN,启动后 drop
- 是否要发信号管理子进程(如 nginx -s reload)?→ 需要 CAP_KILL
- 是否要读写 /proc/sys 或调用 sethostname?→ 很可能不需要,CAP_SYS_ADMIN 是高危能力,99% 的 Web 应用完全不依赖它
- 是否要加载内核模块、挂载磁盘、修改网络栈?→ 生产容器几乎从不需这些,必须丢弃 CAP_SYS_MODULE、CAP_NET_ADMIN、CAP_SYS_ADMIN
用 --cap-drop=ALL + --cap-add 精确授权
这是最稳妥的起点。Docker 默认保留的能力列表会随版本变化,显式清空再按需添加,逻辑清晰、可审计、易复用:
docker run --rm \ --cap-drop=ALL \ --cap-add=NET_BIND_SERVICE \ --cap-add=CHOWN \ --cap-add=KILL \ --user 1001:1001 \ --security-opt no-new-privileges \ -p 80:80 \ my-app:prod
注意:--cap-add 必须在 --cap-drop=ALL 之后生效;--security-opt no-new-privileges 能阻止进程后续通过 execve 获取新能力,是关键防线。
配合非 root 用户与只读文件系统进一步收口
Capabilities 控制的是“能调用哪些系统调用”,但用户身份和文件系统权限同样关键:
- Dockerfile 中必须声明 USER 1001:1001,避免镜像构建阶段以外的 root 操作
- 运行时用 --read-only 挂载根文件系统,仅对必要路径(如 /tmp、日志目录)用 --tmpfs 或 -v 显式放开写入
- 敏感路径如 /proc, /sys, /dev 默认已受命名空间隔离,不要用 --privileged 或 --volume /:/host 强行暴露
验证配置是否真正生效
启动容器后,进容器检查实际能力集合:
docker exec -it <container-id> capsh --print</container-id>
输出中应只看到你明确 add 的几项(如 = cap_net_bind_service,cap_chown,cap_kill+eip),且 Bounding set 里不包含被 drop 的能力。再试执行非法操作,比如:ip link add dummy0 type dummy(需 NET_ADMIN)→ 应报 Operation not permittedmount -t tmpfs tmpfs /mnt(需 SYS_ADMIN)→ 同样失败
不复杂但容易忽略:最小权限不是一次配置完就结束,而是每次功能迭代后重新审视能力清单。上线前删掉调试用的 cap-add,监控日志里出现 capability denied 就说明配置过严,而不是立刻放宽——先查代码是否真需要那个系统调用。











