第11章:Docker 安全加固
本章目标:理解 Docker 的安全威胁和攻击面,掌握容器安全加固的最佳实践。
11.1 Docker 安全威胁概览
11.1.1 容器安全的四个维度
┌────────────────────────────────────────────────────────┐
│ Docker 安全威胁模型 │
│ │
│ 1. 镜像安全 2. 运行时安全 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ - 恶意基础镜像 │ │ - 容器逃逸 │ │
│ │ - 漏洞依赖包 │ │ - 资源耗尽 │ │
│ │ - 硬编码密钥 │ │ - 未授权访问 │ │
│ │ - 过多的权限 │ │ - 进程注入 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ 3. 网络安全 4. 编排安全 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ - 网络隔离不足 │ │ - API 未授权访问 │ │
│ │ - 端口暴露 │ │ - 密钥管理不当 │ │
│ │ - 中间人攻击 │ │ - 配置泄露 │ │
│ │ - DNS 劫持 │ │ - 权限过大 │ │
│ └──────────────────┘ └──────────────────┘ │
└────────────────────────────────────────────────────────┘
11.1.2 常见攻击类型
| 攻击类型 | 描述 | 危害等级 |
|---|---|---|
| 容器逃逸 | 从容器突破隔离获取宿主机权限 | ⭐⭐⭐⭐⭐ |
| 恶意镜像 | 使用包含后门的基础镜像 | ⭐⭐⭐⭐ |
| 漏洞利用 | 利用镜像中的已知漏洞 | ⭐⭐⭐ |
| 密钥泄露 | 镜像中包含硬编码的密码/密钥 | ⭐⭐⭐ |
| 资源耗尽 | 容器消耗过多系统资源 | ⭐⭐ |
| 横向移动 | 从一个容器攻击其他容器 | ⭐⭐⭐ |
11.2 镜像安全
11.2.1 使用可信的基础镜像
dockerfile
# ✅ 使用官方镜像(经过验证)
FROM ubuntu:22.04
FROM python:3.11-slim
FROM node:20-alpine
# ✅ 使用 distroless 镜像(Google 的安全镜像)
FROM gcr.io/distroless/python3-debian12
FROM gcr.io/distroless/java17-debian12
# ❌ 避免使用未知来源的镜像
FROM random-user/custom-image:latest
# ✅ 使用特定标签而非 latest
FROM nginx:1.25.3-alpine # 指定版本
# ❌ FROM nginx:latest # 版本不可控
11.2.2 镜像安全扫描
bash
# 使用 Docker Scout 扫描
docker scout cves nginx:latest
docker scout recommendations nginx:latest
# 使用 Trivy 扫描
trivy image --severity HIGH,CRITICAL nginx:latest
# 在 CI/CD 中集成扫描
# 扫描失败则停止构建
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
11.2.3 镜像签名与验证
bash
# 启用 Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# 推送时自动签名
docker push myregistry.com/myapp:v1.0
# 拉取时自动验证
docker pull myregistry.com/myapp:v1.0
11.2.4 多阶段构建减少攻击面
dockerfile
# ✅ 多阶段构建:构建工具不进入最终镜像
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .
FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
# 最终镜像只有静态二进制,没有 shell、包管理器等
11.3 运行时安全
11.3.1 不要使用 root 用户
dockerfile
# ✅ 创建并使用非 root 用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# 或使用现有用户
USER nobody:nogroup
bash
# 验证容器是否以 root 运行
docker exec <container> whoami
# root → ❌ 不安全
# appuser → ✅ 安全
# 查看容器进程的用户
docker exec <container> ps aux
# root 1 ... → ❌ 主进程是 root
# appuser 1 ... → ✅ 主进程是非 root
11.3.2 只读文件系统
bash
# 使用只读文件系统
docker run -d --read-only nginx
# 需要写入的目录使用 tmpfs
docker run -d --read-only \
--tmpfs /tmp \
--tmpfs /var/run \
nginx
11.3.3 禁用特权模式
bash
# ❌ 绝不要使用 --privileged
docker run --privileged nginx # 危险!
# ✅ 使用最小权限
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
# 查看容器的 capabilities
docker inspect --format='{{.HostConfig.CapAdd}}' <container>
11.3.4 资源限制
bash
# 限制 CPU 和内存,防止资源耗尽攻击
docker run -d \
--cpus="1" \
--memory="512m" \
--memory-swap="512m" \
--pids-limit=100 \
nginx
# 参数说明:
# --cpus 限制 CPU 核数
# --memory 限制内存
# --memory-swap 限制 swap(设为与 memory 相同可禁用 swap)
# --pids-limit 限制进程数(防止 fork bomb)
11.3.5 禁用不需要的能力
bash
# 删除所有 capabilities,按需添加
docker run -d \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
nginx
# 常用 capabilities:
# NET_BIND_SERVICE 绑定低端口
# CHOWN 修改文件所有者
# SETUID/SETGID 设置用户/组 ID
# SYS_PTRACE 调试进程(生产禁用)
11.4 网络安全
11.4.1 最小化端口暴露
bash
# ✅ 只暴露必要的端口
docker run -d -p 80:80 nginx
# ❌ 不要暴露管理端口
# docker run -d -p 8080:8080 admin-panel # 如果不需要外部访问
# ✅ 限制绑定地址
docker run -d -p 127.0.0.1:8080:80 nginx # 只允许本机访问
11.4.2 网络隔离
bash
# ✅ 使用自定义网络隔离服务
docker network create frontend
docker network create backend
docker run -d --name nginx --network frontend nginx
docker run -d --name app --network backend myapp
docker run -d --name db --network backend mysql
# 将 nginx 连接到 backend(作为代理)
docker network connect backend nginx
# ❌ 不要让数据库直接暴露到前端网络
11.4.3 DNS 安全
bash
# 自定义 DNS 配置
docker run -d \
--dns 8.8.8.8 \
--dns-search "" \
nginx
# 避免使用宿主机的 DNS 配置(可能泄露内部域名)
11.5 密钥管理
11.5.1 不要在镜像中存储密钥
dockerfile
# ❌ 绝对不要这样做!
ENV DATABASE_PASSWORD=secret123
COPY .env /app/.env
COPY cert.pem /app/cert.pem
# ✅ 使用 Docker Secrets(Swarm 模式)
# ✅ 使用环境变量(运行时注入)
# ✅ 使用外部密钥管理服务(Vault 等)
11.5.2 Docker Secrets
yaml
# docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
11.5.3 BuildKit Secret Mount
dockerfile
# 在构建时使用 secret(不会保留在镜像层中)
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install
bash
# 构建时传递 secret
docker build --secret id=npmrc,src=.npmrc -t myapp .
11.6 Docker Daemon 安全
11.6.1 限制 Docker Socket 访问
bash
# Docker Socket 权限
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker 0 ... /var/run/docker.sock
# ⚠️ 任何可以访问 Docker Socket 的用户等同于 root 权限!
# 不要将 Docker Socket 挂载到容器中(除非绝对必要)
# ❌ 危险:挂载 Docker Socket
docker run -v /var/run/docker.sock:/var/run/docker.sock docker
# ✅ 如果必须访问 Docker API,使用代理
11.6.2 启用审计日志
bash
# 配置 Docker Daemon 审计
# /etc/docker/daemon.json
{
"debug": false,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
# 使用 auditd 监控 Docker 相关文件
auditctl -w /var/run/docker.sock -p rwxa -k docker
auditctl -w /usr/bin/docker -p rwxa -k docker
auditctl -w /etc/docker -p rwxa -k docker
11.6.3 限制容器的系统调用
bash
# 使用 seccomp 配置文件限制系统调用
docker run -d --security-opt seccomp=custom-profile.json nginx
# 使用默认的 seccomp 配置
docker run -d --security-opt seccomp=default nginx
# 禁用 seccomp(不推荐)
docker run -d --security-opt seccomp=unconfined nginx
11.6.4 使用 AppArmor/SELinux
bash
# AppArmor(Ubuntu/Debian)
docker run -d --security-opt apparmor=docker-default nginx
# SELinux(CentOS/RHEL)
docker run -d --security-opt label=level:s0:c100,c200 nginx
11.7 安全基线检查
11.7.1 Docker Bench Security
bash
# 运行 Docker Bench Security 检查
docker run --rm --net host --pid host \
--userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
docker/docker-bench-security
# 输出安全评分和建议
11.7.2 安全检查清单
bash
# 检查清单:
# 1. 镜像安全
docker images --format "{{.Repository}}:{{.Tag}}" | while read img; do
echo "检查 $img ..."
trivy image --severity HIGH,CRITICAL "$img" 2>/dev/null | head -20
done
# 2. 运行容器安全
docker ps --format "{{.Names}}" | while read container; do
echo "检查 $container ..."
# 检查是否以 root 运行
docker exec "$container" whoami 2>/dev/null
# 检查特权模式
docker inspect --format='{{.HostConfig.Privileged}}' "$container"
done
# 3. 网络安全
docker ps --format "{{.Names}}:{{.Ports}}"
11.8 安全加固最佳实践
11.8.1 Dockerfile 安全清单
✅ Dockerfile 安全最佳实践:
1. 使用可信的基础镜像
2. 使用特定版本标签(非 latest)
3. 使用多阶段构建
4. 使用非 root 用户运行
5. 使用只读文件系统
6. 不要在镜像中存储密钥
7. 定期扫描镜像漏洞
8. 使用 .dockerignore 排除敏感文件
9. 添加 HEALTHCHECK 健康检查
10. 使用 LABEL 添加安全信息
11.8.2 运行时安全清单
✅ 运行时安全最佳实践:
1. 使用 --security-opt=no-new-privileges
2. 使用 --cap-drop ALL --cap-add <needed>
3. 使用 --read-only --tmpfs /tmp
4. 限制资源:--cpus --memory --pids-limit
5. 不要使用 --privileged
6. 不要挂载 Docker Socket
7. 使用自定义网络隔离
8. 只暴露必要的端口
9. 定期更新镜像
10. 监控容器行为
11.8.3 完整的安全加固示例
bash
# 安全加固的容器运行示例
docker run -d \
--name secure-app \
--user 1000:1000 \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
--security-opt seccomp=default \
--cpus="1" \
--memory="256m" \
--memory-swap="256m" \
--pids-limit=50 \
--network app-network \
-p 8080:80 \
--health-cmd "curl -f http://localhost/ || exit 1" \
--health-interval 30s \
--restart unless-stopped \
myapp:latest
11.9 动手实验
实验 11.1:安全扫描
bash
# 1. 扫描官方镜像
trivy image nginx:latest
trivy image python:3.11-slim
trivy image node:20-alpine
# 2. 对比不同基础镜像的安全性
trivy image --severity HIGH,CRITICAL ubuntu:22.04
trivy image --severity HIGH,CRITICAL ubuntu:22.04-minimal
trivy image --severity HIGH,CRITICAL alpine:3.19
# 3. 扫描你自己的镜像
docker build -t myapp:latest .
trivy image myapp:latest
实验 11.2:安全运行
bash
# 1. 对比普通运行和安全运行
# 普通运行
docker run -d --name insecure nginx
docker exec insecure whoami # root ❌
# 安全运行
docker run -d --name secure \
--user 1000:1000 \
--read-only \
--tmpfs /tmp \
--cap-drop ALL \
--security-opt no-new-privileges:true \
nginx
docker exec secure whoami # nginx ✅
# 2. 验证只读文件系统
docker exec secure touch /test.txt
# touch: cannot touch '/test.txt': Read-only file system ❌
实验 11.3:Docker Bench Security
bash
# 运行安全基线检查
docker run --rm --net host --pid host \
--userns host --cap-add audit_control \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
docker/docker-bench-security
# 分析输出,修复发现的问题
11.10 本章小结
| 安全维度 | 关键措施 |
|---|---|
| 镜像安全 | 使用可信镜像、扫描漏洞、签名验证 |
| 运行时安全 | 非 root 用户、只读文件系统、最小权限 |
| 网络安全 | 网络隔离、端口最小化、DNS 安全 |
| 密钥管理 | 不存储在镜像、使用 Secrets、外部管理 |
| Daemon 安全 | 限制 Socket 访问、审计日志、seccomp |
11.11 课后练习
- 检查题:使用 Docker Bench Security 检查你的 Docker 环境,修复发现的问题。
- 实践题:为你的应用编写安全加固的 Dockerfile 和运行命令。
- 研究题:了解容器逃逸的原理和防护措施。
📖 下一章:Docker 监控与日志 ------ 搭建容器可观测性体系