2、容器安全基础:容器运行时安全、镜像安全扫描、容器逃逸防护
聊到容器安全,我经常跟团队说一句话:容器不是轻量级虚拟机。很多人一上手就把容器当虚拟机用,SSH 进去、装包、改配置…… 嗯,这其实是个危险的开始。
容器共享的是宿主机内核。这意味着什么?说白了,一旦容器里的进程突破了隔离边界,它就能直接操作宿主机。这就是我们常说的容器逃逸。我见过不少生产环境因为这个问题出过事,所以今天咱们把容器安全的三个核心维度掰开揉碎了讲清楚。
2.1 容器运行时安全
运行时安全,关注的是容器启动后到销毁前这个阶段。我个人习惯把它分成三个层面:
- 内核能力(Capabilities)裁剪:容器默认拥有很多能力,比如
CAP_SYS_ADMIN、CAP_NET_ADMIN。这些能力如果被滥用,就是逃逸的入口。 - Seccomp 策略:限制容器能调用的系统调用。你想想看,一个 Web 容器根本不需要
mount或ptrace这类系统调用。 - AppArmor / SELinux:强制访问控制,给容器加上一层“铁布衫”。
核心原则:最小权限。给容器分配它恰好够用的能力,多一个都不给。
我在项目中遇到过这样一个案例:一个 Java 应用容器,因为开发图方便,直接给了 --privileged 参数。结果被攻击者利用 mount 命令挂载了宿主机根目录,整个集群差点沦陷。所以,--privileged 这个参数,我建议你永远不要在生产环境使用。
来看一个实际的安全配置示例:
# 运行容器时,只赋予必要的 Capabilities
docker run --cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt seccomp=default.json \
--security-opt apparmor=my-apparmor-profile \
my-app:latest
这里 --cap-drop=ALL 先丢弃所有能力,再单独添加 NET_BIND_SERVICE(允许绑定低端口)。这种做法,我称之为“白名单思维”。
小技巧:可以用 docker inspect 查看容器的 Capabilities 配置。如果发现 CapAdd 里有 SYS_ADMIN 或 NET_ADMIN,就要警惕了。
2.2 镜像安全扫描
镜像安全,是容器安全的第一道防线。你想想看,如果基础镜像里就藏着漏洞,那跑起来的容器就是“带病上岗”。
镜像扫描的核心工作,就是分析镜像的每一层文件系统,找出已知漏洞(CVE)、敏感信息(密钥、密码)、恶意软件等。
常用的扫描工具有:
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Trivy | 轻量、快速、支持多种语言 | CI/CD 流水线集成 |
| Clair | 静态分析、支持分层扫描 | 私有镜像仓库集成 |
| Anchore | 策略引擎、可自定义规则 | 企业级合规要求 |
| Docker Scout | Docker 官方工具、集成度高 | Docker Desktop 用户 |
我个人习惯在 CI/CD 流水线里加入 Trivy。为什么?因为它快,而且能直接输出 JSON 格式的报告,方便后续处理。
来看一个 GitLab CI 的集成示例:
container_scan:
stage: test
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 my-app:$CI_COMMIT_SHA
only:
- main
这里 --exit-code 1 的意思是:如果发现高危或严重漏洞,就让流水线失败。我曾经在一个基础镜像里扫描出 200 多个高危漏洞,全是过时的 Alpine 版本导致的。后来我们统一维护了一个“黄金基础镜像”,漏洞数量直接降到了个位数。
避坑指南:我曾经遇到过扫描工具报漏洞,但实际是误报的情况。比如某些 CVE 只影响特定内核版本,而容器用的是不同内核。所以,扫描结果一定要人工复核,不能全盘接受。
2.3 容器逃逸防护
容器逃逸,是容器安全里最让人头疼的问题。说白了,就是攻击者从容器内部“跑”到了宿主机上。常见的逃逸路径有:
- 内核漏洞利用:比如 Dirty Pipe(CVE-2022-0847),通过内核漏洞直接提权。
- 配置不当:比如挂载了
/var/run/docker.sock,容器就能直接控制宿主机 Docker 守护进程。 - Capabilities 滥用:比如
CAP_SYS_ADMIN配合cgroup实现逃逸。
为什么会这样?因为容器共享宿主机内核。一旦内核有漏洞,所有容器都受影响。所以,内核安全是容器安全的基石。
我建议从以下几个方面加固:
- 禁止挂载敏感目录:比如
/var/run/docker.sock、/proc、/sys等。 - 使用只读根文件系统:
--read-only参数可以防止容器写入恶意文件。 - 启用 User Namespace 重映射:让容器内的 root 用户映射到宿主机上的非特权用户。
- 定期更新内核:及时打上安全补丁。
来看一个典型的逃逸攻击示例:
# 攻击者利用挂载的 docker.sock 逃逸
docker -H unix:///var/run/docker.sock run -it --privileged --pid=host ubuntu bash
# 然后就可以在容器内执行 nsenter 进入宿主机命名空间
nsenter --target 1 --mount --uts --ipc --net --pid -- bash
这个攻击路径,说白了就是利用了 docker.sock 的权限。我在一次红蓝对抗演练中,就亲眼看到攻击者用这招拿下了整个 Kubernetes 集群的控制权。所以,永远不要把 docker.sock 挂载到容器里。
防护黄金法则:
- 不要用
--privileged - 不要挂载
docker.sock - 不要给
CAP_SYS_ADMIN - 使用
seccomp和AppArmor
嗯,这里要注意一点:即使你做了所有配置,也不能保证 100% 安全。安全是一个持续的过程,不是一锤子买卖。我建议你定期做渗透测试,模拟攻击者的视角来检验你的防护是否有效。
最后总结一下:镜像安全是入口,运行时安全是过程,逃逸防护是底线。这三者缺一不可。下次咱们聊 Kubernetes 安全的时候,你会发现很多思路是相通的。