摘要:容器的安全边界薄于虚拟机,治理思路必须是纵深防御——供应链、运行时、守护进程三道防线层层设卡。本节把每道防线拆成可执行的加固项与验证命令,并说明逃逸攻击为何要在多层同时设防。
第 3 章讲过:namespace 隔离的是视野,不是权限。默认模式下容器内的 root 就是宿主机的 root(只是被 capabilities 与 seccomp 削减过),内核漏洞、危险挂载、错误配置都可能让容器内代码越界——即"容器逃逸"。既然单边界不可靠,工程答案就是纵深防御:假设任何一道会被突破,让攻击者在每一层都撞墙。
生产事故的常见起点是一个没人审过的镜像。四项纪律:
最小基础镜像。distroless 或 slim(第 2 章的瘦身成果在这里兑现安全红利):没有 shell、没有包管理器的镜像,攻击者拿到执行权后连工具都凑不齐。
摘要锁定。生产清单用 image@sha256:... 而非浮动 tag,杜绝"上游 tag 被覆盖投毒、下次部署自动中招"。
镜像扫描。用 trivy 这类扫描器在 CI 里扫已知漏洞:
trivy image myrepo/web:1.4.2
输出按严重级别列出 CVE 与涉及依赖。把"高危以上阻断合并"写进流水线策略,扫描才有效力。
私有仓库与签名。企业镜像走私有仓库(Harbor 一类),入口处执行内容信任与签名校验,出库镜像可追溯。
永远非 root 运行。Dockerfile 里建用户并切换:
RUN addgroup -S app && adduser -S app -G app USER app
裁剪 capabilities。root 的内核权限由一组 capabilities 构成,容器默认只保留一小撮。再砍:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx:alpine
只留"绑 80 端口"这一项权限,其余全弃。
只读根文件系统:
docker run --read-only -v /run/session --tmpfs /run/session nginx:alpine
可写层被冻结,恶意代码写不了 webshell、落不了盘;应用确实要写的目录(如 /tmp)用 tmpfs 精确开口(第 5 章的挂载知识)。
seccomp 与 syscall 过滤。默认 seccomp 配置已屏蔽三百多个危险系统调用,一般不必自定义,知道这层存在即可。
危险挂载黑名单。三条红线:不挂 docker 套接字进不受信容器(等于交出宿主机 root);不用 --privileged(等于放弃全部防线);挂宿主机敏感目录(/etc、ssh 密钥)前必须自问三遍。
验证加固效果有一条捷径——用现成的逃逸检测镜像扫一遍环境配置,它会列出"你当前的配置给了容器哪些越界能力",逐条关闭即可。

第 1 章埋的伏笔在此回收:能访问 docker 套接字约等于 root。治理要点:docker 组按管理员对待,人数最少化;绝不为远程管理把 dockerd 的 TCP 端口裸露在网络上(确需远程时务必启用 TLS 双向认证);有条件就上 rootless 模式——整个 Docker 以普通用户身份运行,User namespace 把容器内 root 映射为低权用户,即便逃逸也只是普通用户权限。rootless 的代价是部分功能受限(端口小于 1024 需额外配置、部分存储与网络特性不可用),运维习惯也要调整,属于安全敏感场景的进阶选项。
按优先级排:
容器安全的悖论:默认配置为易用性牺牲了隔离,加固就是把这笔债按风险逐笔还清。不求一次还完,但要知道欠的是什么。
最后谈一点现实取舍。每一道加固项都有运维代价:只读根文件系统会让某些临时写文件的应用报错,需要逐一开口;capability 裁剪过狠会让正统功能失灵,需要逐项试错回加;rootless 模式改变了操作习惯,脚本与权限体系都要适配。建议按风险分级实施:对外服务执行全部清单,内部工具执行前六条,实验环境至少守住三条红线。安全不是追求绝对,而是让攻击成本高于攻击收益,同时别把团队自己的运维成本先压垮。
安全管住了风险,下一节让系统"看得见"——指标与日志。