Skip to content

安全实践

容器安全是生产环境中不可忽视的重要环节,本节介绍 Docker 安全的最佳实践。

镜像安全

使用官方或可信镜像

bash
# 优先使用官方镜像
FROM node:20-alpine      # 官方镜像
FROM nginx:alpine        # 官方镜像

# 固定版本,避免引入不可控变更
FROM node:20.11.0-alpine3.19  # 固定完整版本

扫描镜像漏洞

bash
# 使用 docker scout(Docker Desktop 内置)
docker scout cves nginx:alpine

# 使用 Trivy(开源,推荐)
# 安装
brew install trivy  # macOS
apt-get install trivy  # Ubuntu

# 扫描本地镜像
trivy image nginx:alpine

# 扫描 Dockerfile(构建前检查)
trivy config ./Dockerfile

# 在 CI/CD 中扫描(失败则阻断)
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

最小化基础镜像

dockerfile
# 减少攻击面:使用最小化镜像
FROM alpine:3.19           # 约 5MB
FROM gcr.io/distroless/static  # 约 2MB,无 shell
FROM scratch               # 0MB,仅适合静态二进制

容器运行安全

以非 root 用户运行

dockerfile
# 方式一:创建专用用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 方式二:使用镜像内置用户
USER node   # node 官方镜像内置
USER nginx  # nginx 官方镜像内置
bash
# 运行时指定用户
docker run --user 1000:1000 myapp

# 验证容器内用户
docker exec mycontainer id

禁止 root 用户运行

bash
# 运行时限制不允许提权到 root
docker run --security-opt no-new-privileges myapp

# 只读根文件系统
docker run --read-only myapp

# 只读 + 临时写入目录
docker run --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  myapp

限制 Linux 能力(Capabilities)

bash
# 删除所有能力,只添加必要的
docker run \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \  # 允许绑定低端口
  nginx

# 常见需要的能力
# NET_BIND_SERVICE - 绑定 1024 以下端口
# CHOWN - 修改文件所有者
# SETUID/SETGID - 切换用户/组

避免特权模式

bash
# 绝对避免(除非必须,如 Docker-in-Docker)
docker run --privileged myapp  # ⚠️ 危险!

# 使用最小权限替代
docker run --cap-add SYS_PTRACE myapp  # 只需要 ptrace 时

网络安全

最小化端口暴露

bash
# 只暴露必要端口
docker run -p 80:80 nginx     # 只暴露 HTTP

# 绑定到本地(只允许本机访问)
docker run -p 127.0.0.1:3306:3306 mysql

# 内部服务不暴露端口(通过自定义网络通信)
docker run --network app-net mysql  # 不加 -p,只在网络内可访问

隔离网络

bash
# 每个应用使用独立网络
docker network create app1-net
docker network create app2-net

# 不同应用的容器在不同网络,互相隔离
docker run --network app1-net service-a
docker run --network app2-net service-b

Secrets 管理

不要在镜像中存储密钥

dockerfile
# 错误做法
ENV DB_PASSWORD=secret123  # ⚠️ 会被记录到镜像层中

# 正确做法:运行时通过环境变量传入
# docker run -e DB_PASSWORD=$DB_PASSWORD myapp

使用 Docker Secrets(Swarm 模式)

bash
# 创建 secret
echo "secure_password" | docker secret create db_password -

# 在服务中使用
docker service create \
  --secret db_password \
  -e DB_PASSWORD_FILE=/run/secrets/db_password \
  myapp

使用环境变量文件(开发)

bash
# .env 文件不提交到 Git
echo ".env" >> .gitignore
echo ".env.*" >> .gitignore

# 运行时加载
docker run --env-file .env myapp

日志安全审计

bash
# 开启 Docker 审计日志
sudo auditctl -w /usr/bin/docker -p rwxa -k docker

# 查看容器操作历史
docker events --since 24h

# 监控特权操作
docker events --filter event=exec_start

定期更新

bash
# 定期更新基础镜像
docker pull node:20-alpine
docker pull nginx:alpine

# 检查哪些容器在使用过时的镜像
docker ps --format "table {{.Names}}\t{{.Image}}"

# 在 CI/CD 中自动构建最新镜像

安全扫描集成到 CI/CD

yaml
# .github/workflows/security.yml
name: Security Scan

on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: HIGH,CRITICAL
          exit-code: 1

      - name: Upload results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: trivy-results.sarif

安全检查清单

  • [ ] 使用非 root 用户运行容器
  • [ ] 基础镜像固定版本,定期更新
  • [ ] 使用漏洞扫描工具定期扫描
  • [ ] 只暴露必要端口
  • [ ] 内部服务不映射到公网端口
  • [ ] 密钥通过环境变量或 Secrets 管理,不硬编码
  • [ ] 使用只读文件系统(--read-only
  • [ ] 丢弃不必要的 Linux 能力(--cap-drop ALL
  • [ ] 避免使用 --privileged
  • [ ] 在 CI/CD 中集成安全扫描
  • [ ] 定期审查容器和镜像的安全状态

总结

Docker 安全的核心原则:

  1. 最小权限:只给容器所需的最小权限
  2. 最小攻击面:使用最小化镜像,不暴露不必要的端口
  3. 密钥管理:不在镜像中存储密钥
  4. 定期更新:保持镜像和依赖的最新安全补丁
  5. 持续扫描:在 CI/CD 中集成漏洞扫描