容器技术已成为现代应用部署的事实标准,但许多团队在享受 Docker 带来的便利时,往往忽视了其潜在的安全风险。一个配置不当的容器,轻则成为内网横向移动的跳板,重则导致主机完全沦陷(Container Breakout)。本文汇总了 30 条生产级 Docker 安全加固 Checklist,涵盖构建时(Build Time)、运行时(Run Time)、主机安全、网络控制、监控响应五大维度,并附上开箱即用的 Bash 脚本和 Ansible Playbook,适合个人开发者、运维工程师和安全团队参考落地。
一、镜像安全(Build Time)
镜像是容器的起点,也是安全防护的第一道防线。构建阶段的疏漏,往往会在运行时以指数级放大风险。
| # | 检查项 | 安全建议 | 验证方式 |
|---|---|---|---|
| 1 | 使用可信基础镜像 | 优先选择官方镜像(如 nginx:alpine),避免 latest 标签 |
docker inspect <image> 查来源 |
| 2 | 扫描镜像漏洞 | 构建后自动扫描(Trivy / Clair / Snyk) | CI 流程集成 trivy image myapp:1.0 |
| 3 | 多阶段构建减小体积 | 编译与运行分离,移除编译工具链 | 镜像大小应 < 100MB(简单应用) |
| 4 | 禁用 root 用户 | 在 Dockerfile 中创建非 root 用户并切换 | USER 1000 或 USER appuser |
| 5 | 敏感信息不硬编码 | 密码、密钥通过 secrets 或 env 文件注入 | 检查 Dockerfile 是否含 PASSWORD= |
| 6 | 使用 .dockerignore | 排除 .git、node_modules、日志等无关文件 |
避免泄露源码或临时文件 |
| 7 | 启用内容信任(DCT) | export DOCKER_CONTENT_TRUST=1,只拉取签名镜像 |
企业私有仓库需部署 Notary |
重点说明
关于 latest 标签 :使用 latest 意味着每次构建/拉取可能获取到不同版本,无法保证镜像的可重复性,且无法精确追踪 CVE。生产环境务必固定版本号,如 nginx:1.25.3-alpine。
关于多阶段构建:以 Go 应用为例,编译阶段所需的 SDK 体积可能超过 1GB,而最终运行只需要一个二进制文件。多阶段构建可以将最终镜像大小压缩至 10MB 以内,同时消除编译工具带来的攻击面。
# 编译阶段
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# 运行阶段(仅包含二进制文件)
FROM alpine:3.19
RUN adduser -D -u 1000 appuser
COPY --from=builder /app/myapp /usr/local/bin/myapp
USER appuser
ENTRYPOINT ["myapp"]
二、容器运行时安全(Run Time)
镜像构建好后,运行方式同样至关重要。默认的 docker run 配置远比你想象的宽松。
| # | 检查项 | 安全建议 | 命令示例 |
|---|---|---|---|
| 8 | 限制能力(Capabilities) | 默认丢弃所有能力,按需添加 | --cap-drop=ALL --cap-add=NET_BIND_SERVICE |
| 9 | 禁用特权模式 | 绝对禁止 --privileged |
docker run 不含 --privileged |
| 10 | 限制资源使用 | 防止 DoS(CPU / 内存 / 进程数) | --memory=512m --cpus=1 --pids-limit=100 |
| 11 | 只读根文件系统 | 容器内文件系统不可写(除挂载卷) | --read-only + -v /tmp:/tmp |
| 12 | 禁用危险挂载 | 不挂载 /、/etc、/proc 等敏感目录 |
审计 docker inspect 的 Mounts |
| 13 | 设置重启策略为 on-failure | 避免无限重启导致资源耗尽 | --restart=on-failure:3 |
| 14 | 启用 seccomp / apparmor / selinux | 限制系统调用(如禁止 mount、reboot) | 使用默认或自定义 profile |
| 15 | 禁用 IPC 共享 | 防止容器间通过共享内存攻击 | 不使用 --ipc=host |
重点说明
关于 --privileged :特权模式会将宿主机几乎所有的设备和能力赋予容器,使容器内的 root 等同于宿主机的 root。一旦容器被入侵,攻击者可直接逃逸到宿主机。这是 最高级别的安全红线,没有任何理由在生产环境使用。
关于 Capabilities :Linux Capabilities 将传统的 root 权限拆分为细粒度能力。例如一个 Web 服务只需要绑定 80 端口,可以只授予 NET_BIND_SERVICE,而非给予完整的 root 权限:
docker run \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--memory=256m \
--cpus=0.5 \
--pids-limit=50 \
--read-only \
-v /tmp:/tmp:rw \
--restart=on-failure:3 \
nginx:1.25.3-alpine
三、Docker 主机安全
容器跑在宿主机上,主机的安全是整个容器环境的根基。
| # | 检查项 | 安全建议 |
|---|---|---|
| 16 | 升级 Docker 到最新稳定版 | 修复已知 CVE(如 CVE-2024-XXXX) |
| 17 | 限制 Docker Daemon 访问 | 禁止公网暴露 2375(非 TLS)端口 |
| 18 | 使用 TLS 加密客户端通信 | 配置 --tlsverify --tlscacert |
| 19 | 分离 Docker 数据目录 | /var/lib/docker 挂独立磁盘,设配额 |
| 20 | 审计 Docker 日志 | 记录 docker run/pull/rmi 等操作(通过 auditd 或 SIEM) |
| 21 | 定期清理无用资源 | docker system prune -f(自动化脚本) |
重点说明
关于 2375 端口 :Docker Daemon 的未认证远程 API 端口(2375)是历史上最严重的安全漏洞来源之一。曾有大量服务器因误将该端口暴露到公网,被攻击者直接通过 API 部署挖矿程序。任何情况下都不应将该端口暴露到不受信任的网络。
如果业务确实需要远程管理,请使用 TLS 双向认证(2376 端口):
dockerd \
--tlsverify \
--tlscacert=/etc/docker/certs/ca.pem \
--tlscert=/etc/docker/certs/server-cert.pem \
--tlskey=/etc/docker/certs/server-key.pem \
-H=0.0.0.0:2376
四、网络与访问控制
默认的 Docker 网络配置对隔离性考虑不足,需要主动收紧。
| # | 检查项 | 安全建议 |
|---|---|---|
| 22 | 使用自定义网络 | 避免默认 bridge 网络(无 DNS 解析) |
| 23 | 最小化端口暴露 | 只映射必要端口(如 80/443),不用 -P |
| 24 | 启用用户自定义网络隔离 | 不同业务用不同网络,禁止跨网访问 |
| 25 | 配合防火墙限制访问 | 主机 iptables 限制源 IP(如只允许可信网段) |
| 26 | 禁用容器间互访(如不需要) | --icc=false(Docker daemon 参数) |
重点说明
关于 -P 参数 :-P 会将容器所有 EXPOSE 的端口随机映射到宿主机高位端口,极易造成意外暴露。生产环境始终使用明确的端口映射,如 -p 127.0.0.1:8080:80(绑定到本地回环,而非 0.0.0.0)。
关于网络隔离实践:
# 创建专用业务网络
docker network create --driver bridge app-backend
# 前端只连接前端网络,后端数据库连接 app-backend
docker run --network=app-backend --name=mysql mysql:8.0
docker run --network=app-backend --name=backend myapp:1.0
五、监控与应急响应
安全不是一次性工作,持续的监控和应急能力是最后一道防线。
| # | 检查项 | 安全建议 |
|---|---|---|
| 27 | 监控容器行为异常 | 如突然大量外连、高 CPU、写敏感路径 |
| 28 | 启用运行时保护 | 使用 Falco、Aqua、Sysdig 实时告警 |
| 29 | 定期备份 Volume 数据 | 数据库、配置文件等持久化内容 |
| 30 | 制定容器逃逸应急流程 | 一旦发现 container breakout,立即隔离主机 |
Falco 示例规则(检测容器内异常 shell)
- rule: Terminal shell in container
desc: 检测容器内是否有交互式 shell
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
and container_entrypoint
output: >
"Shell spawned in a container (user=%user.name container=%container.name
image=%container.image.repository shell=%proc.name parent=%proc.pname)"
priority: WARNING
六、自动化检查工具推荐
一键运行 CIS Docker Benchmark
docker-bench-security 是 Docker 官方提供的安全基线检查工具,基于 CIS Docker Benchmark:
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
aquasec/docker-bench-security
运行后会对主机、Docker daemon 配置、镜像、容器等逐项检查并给出 [PASS] / [WARN] / [INFO] 结果,非常直观。
七、自动化加固脚本
以下提供单机 Bash 脚本 和集群 Ansible Playbook 两个方案,覆盖不同规模的生产环境。
目录结构
docker-hardening/
├── harden-docker.sh # Bash 脚本(单机)
├── ansible/
│ ├── inventory.ini
│ └── docker_hardening.yml # Ansible Playbook(集群)
└── README.md
7.1 Bash 脚本:harden-docker.sh
-
用途:一键加固单台 Docker 主机
-
权限要求:root 或 sudo 权限
-
支持系统:CentOS 7/8、Ubuntu 20.04+
chmod +x harden-docker.sh
sudo ./harden-docker.sh
⚠️ 注意:
userns-remap会改变容器 UID 映射,已有容器需重建- 如需保留远程 API,请改用 TLS 加密配置(本脚本默认禁用)
7.2 Ansible Playbook:docker_hardening.yml
-
用途:批量加固多台 Docker 主机
-
依赖:Ansible,目标机已安装 Docker
[docker_hosts]
node1 ansible_host=192.168.10.10
node2 ansible_host=192.168.10.11
node3 ansible_host=192.168.10.12[docker_hosts:vars]
ansible_user=rootansible-playbook -i inventory.ini docker_hardening.yml
八、加固后验证
手动检查关键项
# 检查 daemon.json
cat /etc/docker/daemon.json
# 检查是否启用 userns
docker info | grep -i userns
# 检查远程 API 是否关闭(应无输出)
ss -tuln | grep 2375
自动化合规扫描
# 运行 CIS Benchmark 检查
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker:ro \
-v /etc:/etc:ro \
aquasec/docker-bench-security
九、注意事项与风险提示
在执行加固操作前,请充分了解以下风险,做好备份和回滚预案:
| 风险点 | 应对措施 |
|---|---|
userns-remap 导致卷权限错误 |
重建容器;或对 Volume 目录执行 chown 165536:165536 |
| 已有容器无法启动 | 备份数据后重建;或临时注释 userns-remap |
| 远程 API 被业务依赖 | 改用 TLS 加密配置(需额外生成证书) |
| SELinux / AppArmor 冲突 | 在 RHEL/CentOS 上确保 SELinux 为 permissive 或配置策略 |
十、关键原则总结
历经 30 条 Checklist,其实可以归纳为四条核心原则:
- 最小权限:容器 ≠ 虚拟机,不要给 root!每多一个权限,就多一个被利用的可能。
- 不可变基础设施:容器应无状态,出问题直接重建,不要在容器内手动修复配置。
- 纵深防御:镜像扫描 + 运行时限制 + 主机加固 + 网络隔离,四层缺一不可。
- 可观测性即安全:没有监控的容器 = 黑盒炸弹。你不知道它在做什么,就无法保护它。