目录

Docker 安全加固:生产环境必须检查的清单

背景

大多数 Docker 安全事件不是黑客发现了什么 0day,是你忘了关特权模式、用了 root 运行容器、或者开放了不必要的端口。

这篇文章是实战清单,不讲原理,直接告诉你做什么检查。

基础配置检查

1. 不要用 root 运行容器

# ❌ 错误:容器内用 root
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

# ✅ 正确:创建非 root 用户
FROM node:20
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]
# Kubernetes 里也适用
securityContext:
  runAsNonRoot: true
  runAsUser: 1000

2. 只读文件系统

# docker-compose.yml
services:
  app:
    read_only: true
    # 需要写入的地方用 tmpfs
    tmpfs:
      - /tmp
      - /var/run

容器内文件系统是只读的,恶意代码无法写入。

3. 限制 Capabilities

Linux capabilities 是root权限的精细切分。容器不需要 CAP_SYS_ADMIN。

# docker-compose.yml
services:
  app:
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  # 仅添加需要的
# 检查当前容器 capabilities
docker inspect container_name | grep -i cap

镜像安全

4. 用 distroless 或 alpine

# ❌ 不用 ubuntu/debian(200+MB,系统漏洞面大)
FROM ubuntu:22.04

# ✅ 用 distroless(最小化,只有运行时需要的)
FROM gcr.io/distroless/static-debian12

# ✅ 或 alpine(轻量,只有 5MB)
FROM alpine:3.19

5. 不缓存依赖(构建时)

# ❌ 错误:把整个项目 COPY 进去再装依赖
COPY . .
RUN npm install

# ✅ 正确:先复制 package.json,装依赖,再复制代码
COPY package.json package-lock.json* ./
RUN npm ci --only=production
COPY . .

好处:

  • 依赖安装失败时能快速定位
  • Docker 构建缓存利用更高效
  • 减少镜像层大小

6. 设置 health check

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]

网络隔离

7. 网络最小化

services:
  app:
    networks:
      - backend-network
  
  db:
    networks:
      - backend-network
    # DB 不需要暴露到外部

networks:
  backend-network:
    driver: bridge

应用只暴露 API 端口,数据库完全在内网。

8. 不用 host 网络模式

# ❌ 危险:容器直接用宿主机网络
network_mode: "host"

# ✅ 正确:默认 bridge,必要时用端口映射
services:
  app:
    ports:
      - "8080:3000"

host 模式绕过了 Docker 的网络隔离,容器可以直接访问宿主机网络。

密钥管理

9. 不用环境变量存密钥

# ❌ 危险:密钥写在环境变量里
environment:
  DATABASE_PASSWORD: "supersecret"

# ✅ 正确:用 Docker secrets(Swarm 模式)
secrets:
  db_password:
    file: ./db_password.txt

services:
  app:
    secrets:
      - source: db_password
        target: db_password
        mode: 0400

# ✅ 或者用外部密钥管理(Vault、AWS Secrets Manager)
# Kubernetes 用 external secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
spec:
  secretStoreRef:
    name: vault-backend
  target:
    name: app-secrets
  data:
    - secretKey: db-password
      remoteRef:
        key: prod/database/password

运行时安全

10. 用 AppArmor / seccomp 限制系统调用

# 默认 seccomp 配置已经屏蔽了 44 个危险系统调用
# 但有些场景需要更严格

# 查看默认 seccomp
docker run --rm \
  alpine \
  cat /proc/sys/kernel/seccomp/action_on_sigfault

生产环境建议用非特权容器:

security_opt:
  - no-new-privileges:true

11. 资源限制

services:
  app:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

防止容器耗尽宿主机资源。

CI/CD 安全检查

12. Trivy 扫描镜像漏洞

# .github/workflows/ci.yml
- name: Scan image for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.IMAGE_TAG }}
    format: 'sarif'
    severity: 'CRITICAL,HIGH'
    exit-code: '1'  # 发现漏洞就 fail
# 本地也跑一下
trivy image your-image:tag

# 扫描已知 CVE
trivy image -- vuln-type=os,library your-image:tag

13. 不要用 :latest

# ❌ :latest 意味着你不知道实际版本
image: myapp:latest

# ✅ 用确定版本
image: myapp:v2.3.1
image: myapp@sha256:abc123...

总结清单

生产环境 Docker 必须做:

□ 非 root 用户运行
□ 文件系统只读
□ Capabilities 最小化
□ distroless/alpine 基础镜像
□ health check
□ 网络隔离
□ 不用 host 模式
□ 密钥不用 env 存
□ 资源限制
□ CI/CD 漏洞扫描
□ 固定镜像版本

这条清单过一遍,大多数 Docker 安全事件可以避免。