2、容器安全基础:容器运行时安全、镜像安全扫描、容器逃逸防护

聊到容器安全,我经常跟团队说一句话:容器不是轻量级虚拟机。很多人一上手就把容器当虚拟机用,SSH 进去、装包、改配置…… 嗯,这其实是个危险的开始。

容器共享的是宿主机内核。这意味着什么?说白了,一旦容器里的进程突破了隔离边界,它就能直接操作宿主机。这就是我们常说的容器逃逸。我见过不少生产环境因为这个问题出过事,所以今天咱们把容器安全的三个核心维度掰开揉碎了讲清楚。

2.1 容器运行时安全

运行时安全,关注的是容器启动后到销毁前这个阶段。我个人习惯把它分成三个层面:

  • 内核能力(Capabilities)裁剪:容器默认拥有很多能力,比如 CAP_SYS_ADMINCAP_NET_ADMIN。这些能力如果被滥用,就是逃逸的入口。
  • Seccomp 策略:限制容器能调用的系统调用。你想想看,一个 Web 容器根本不需要 mountptrace 这类系统调用。
  • 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_ADMINNET_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 实现逃逸。

为什么会这样?因为容器共享宿主机内核。一旦内核有漏洞,所有容器都受影响。所以,内核安全是容器安全的基石

我建议从以下几个方面加固:

  1. 禁止挂载敏感目录:比如 /var/run/docker.sock/proc/sys 等。
  2. 使用只读根文件系统--read-only 参数可以防止容器写入恶意文件。
  3. 启用 User Namespace 重映射:让容器内的 root 用户映射到宿主机上的非特权用户。
  4. 定期更新内核:及时打上安全补丁。

来看一个典型的逃逸攻击示例:

# 攻击者利用挂载的 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
  • 使用 seccompAppArmor

嗯,这里要注意一点:即使你做了所有配置,也不能保证 100% 安全。安全是一个持续的过程,不是一锤子买卖。我建议你定期做渗透测试,模拟攻击者的视角来检验你的防护是否有效。

最后总结一下:镜像安全是入口,运行时安全是过程,逃逸防护是底线。这三者缺一不可。下次咱们聊 Kubernetes 安全的时候,你会发现很多思路是相通的。