Skip to content

Docker 安全最佳实践

本章将深入讲解 Docker 的安全配置和最佳实践,帮助你构建安全的容器化应用,防范常见的安全威胁。

Docker 安全概述

容器安全的重要性

容器技术虽然提供了应用隔离,但与传统虚拟机相比,容器共享主机内核,因此需要特别关注安全配置:

  • 内核共享风险:所有容器共享主机内核
  • 权限提升:不当配置可能导致容器逃逸
  • 镜像安全:恶意镜像可能包含后门或漏洞
  • 网络暴露:不当的网络配置可能暴露内部服务
  • 数据泄露:敏感数据可能通过容器泄露

Docker 安全模型

┌─────────────────────────────────────────────────────────┐
│                    主机操作系统                          │
├─────────────────────────────────────────────────────────┤
│                   Docker 守护进程                       │
├─────────────────────────────────────────────────────────┤
│  容器 1        │  容器 2        │  容器 3              │
│  ┌───────────┐ │  ┌───────────┐ │  ┌───────────┐      │
│  │   应用    │ │  │   应用    │ │  │   应用    │      │
│  │ Namespace │ │  │ Namespace │ │  │ Namespace │      │
│  │  Cgroups  │ │  │  Cgroups  │ │  │  Cgroups  │      │
│  │ Capabilities│ │  │Capabilities│ │  │Capabilities│    │
│  └───────────┘ │  └───────────┘ │  └───────────┘      │
└─────────────────────────────────────────────────────────┘

镜像安全

使用可信的基础镜像

dockerfile
# ✅ 使用官方镜像
FROM node:16-alpine

# ✅ 使用具体版本标签
FROM nginx:1.21.6-alpine

# ❌ 避免使用 latest 标签
FROM ubuntu:latest

# ✅ 使用镜像摘要确保完整性
FROM nginx@sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee

# ✅ 使用最小化镜像
FROM alpine:3.16
FROM scratch  # 空镜像,适用于静态编译的程序

镜像漏洞扫描

bash
# 使用 Docker 内置扫描
docker scan nginx:latest

# 使用 Trivy 扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image nginx:latest

# 使用 Clair 扫描
docker run -d --name clair-db postgres:latest
docker run -d --name clair --link clair-db:postgres \
  quay.io/coreos/clair:latest

# 使用 Anchore 扫描
docker run -d --name anchore-db postgres:latest
docker run -d --name anchore-engine --link anchore-db:anchore-db \
  anchore/anchore-engine:latest

构建安全的镜像

dockerfile
# 多阶段构建,减少攻击面
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:16-alpine AS runtime
# 创建非特权用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

# 只复制必要文件
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .

# 删除不必要的包
RUN apk del .build-deps

# 使用非 root 用户
USER nextjs

# 设置只读根文件系统
# 运行时使用: docker run --read-only myapp

镜像签名和验证

bash
# 启用 Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# 推送签名镜像
docker push myregistry/myapp:v1.0

# 拉取时自动验证签名
docker pull myregistry/myapp:v1.0

# 使用 Notary 管理签名
notary init myregistry/myapp
notary publish myregistry/myapp

容器运行时安全

用户和权限管理

bash
# ✅ 使用非 root 用户运行
docker run -u 1000:1000 nginx

# ✅ 在 Dockerfile 中创建用户
FROM alpine
RUN adduser -D -s /bin/sh appuser
USER appuser

# ✅ 删除不必要的权限
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# ✅ 禁用新权限获取
docker run --security-opt no-new-privileges nginx

# ❌ 避免特权模式
docker run --privileged nginx  # 危险!

Linux Capabilities 管理

bash
# 查看默认 capabilities
docker run --rm -it alpine sh -c 'apk add libcap && capsh --print'

# 删除所有 capabilities
docker run --cap-drop=ALL alpine

# 只添加必要的 capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# 常用 capabilities
# NET_BIND_SERVICE: 绑定特权端口 (<1024)
# SYS_TIME: 修改系统时间
# NET_ADMIN: 网络管理
# SYS_ADMIN: 系统管理(危险)

资源限制

bash
# 内存限制
docker run -m 512m nginx

# CPU 限制
docker run --cpus="1.5" nginx

# 进程数限制
docker run --pids-limit 100 nginx

# 文件描述符限制
docker run --ulimit nofile=1024:1024 nginx

# 磁盘 I/O 限制
docker run --device-read-bps /dev/sda:1mb nginx

文件系统安全

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

# 只读根文件系统 + 临时文件系统
docker run --read-only --tmpfs /tmp --tmpfs /var/run nginx

# 禁用 suid/sgid 程序
docker run --security-opt no-new-privileges nginx

# 使用 AppArmor 配置文件
docker run --security-opt apparmor:docker-default nginx

# 使用 SELinux 标签
docker run --security-opt label:type:container_t nginx

网络安全

网络隔离

bash
# 创建隔离网络
docker network create --internal backend-network

# 运行在隔离网络中
docker run --network backend-network postgres

# 禁用网络
docker run --network none alpine

# 使用主机网络(谨慎使用)
docker run --network host nginx

端口和服务暴露

bash
# ✅ 只暴露必要端口
docker run -p 127.0.0.1:8080:80 nginx  # 只绑定本地接口

# ✅ 使用非标准端口
docker run -p 8080:80 nginx

# ❌ 避免暴露所有端口
docker run -P nginx  # 可能暴露不必要的端口

# ✅ 使用防火墙规则
iptables -A INPUT -p tcp --dport 8080 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP

TLS 和加密

yaml
# docker-compose.yml 中配置 TLS
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro
      - ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro
      - ./nginx-ssl.conf:/etc/nginx/nginx.conf:ro
    environment:
      - SSL_CERT_PATH=/etc/nginx/ssl/cert.pem
      - SSL_KEY_PATH=/etc/nginx/ssl/key.pem

秘密信息管理

避免在镜像中存储秘密

dockerfile
# ❌ 不要在镜像中硬编码秘密
ENV API_KEY=secret123
COPY secret.key /app/

# ✅ 使用运行时注入
# 通过环境变量或挂载文件传递

使用 Docker Secrets

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

# 在服务中使用 secret
docker service create \
  --name myapp \
  --secret db_password \
  myapp:latest

# 在容器中访问 secret(位于 /run/secrets/)
cat /run/secrets/db_password

使用外部秘密管理

yaml
# 使用 HashiCorp Vault
version: '3.8'

services:
  app:
    image: myapp:latest
    environment:
      - VAULT_ADDR=https://vault.company.com
      - VAULT_TOKEN_FILE=/run/secrets/vault_token
    secrets:
      - vault_token
    command: |
      sh -c '
        export VAULT_TOKEN=$$(cat /run/secrets/vault_token)
        export DB_PASSWORD=$$(vault kv get -field=password secret/db)
        exec myapp
      '

secrets:
  vault_token:
    external: true

日志和监控安全

安全日志配置

bash
# 配置日志驱动
docker run --log-driver=syslog \
  --log-opt syslog-address=tcp://logserver:514 \
  --log-opt tag="{{.Name}}" \
  nginx

# 限制日志大小
docker run --log-driver=json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx

# 发送到集中日志系统
docker run --log-driver=fluentd \
  --log-opt fluentd-address=localhost:24224 \
  --log-opt tag="docker.{{.Name}}" \
  nginx

安全监控

yaml
# 使用 Falco 进行运行时安全监控
version: '3.8'

services:
  falco:
    image: falcosecurity/falco:latest
    privileged: true
    volumes:
      - /var/run/docker.sock:/host/var/run/docker.sock
      - /dev:/host/dev
      - /proc:/host/proc:ro
      - /boot:/host/boot:ro
      - /lib/modules:/host/lib/modules:ro
      - /usr:/host/usr:ro
      - /etc:/host/etc:ro
    command: falco --modern-bpf

Docker 守护进程安全

守护进程配置

json
// /etc/docker/daemon.json
{
  "icc": false,                    // 禁用容器间通信
  "userland-proxy": false,         // 禁用用户态代理
  "no-new-privileges": true,       // 禁用新权限
  "selinux-enabled": true,         // 启用 SELinux
  "userns-remap": "default",       // 启用用户命名空间
  "live-restore": true,            // 守护进程重启时保持容器运行
  "log-driver": "json-file",       // 配置日志驱动
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",    // 使用安全的存储驱动
  "tls": true,                     // 启用 TLS
  "tlscert": "/etc/docker/cert.pem",
  "tlskey": "/etc/docker/key.pem",
  "tlsverify": true,
  "tlscacert": "/etc/docker/ca.pem"
}

TLS 配置

bash
# 生成 CA 私钥
openssl genrsa -aes256 -out ca-key.pem 4096

# 生成 CA 证书
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

# 生成服务器私钥
openssl genrsa -out server-key.pem 4096

# 生成服务器证书签名请求
openssl req -subj "/CN=docker-host" -sha256 -new -key server-key.pem -out server.csr

# 签名服务器证书
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -out server-cert.pem

# 生成客户端私钥和证书
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -out cert.pem

# 启动带 TLS 的 Docker 守护进程
dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376

安全扫描和审计

容器安全扫描

bash
# 使用 Docker Bench Security
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh

# 使用 Lynis 系统审计
docker run --rm -v /:/rootfs:ro --pid=host cisofy/lynis audit system

# 使用 OpenSCAP 合规性检查
docker run --rm -v /:/host:ro --privileged \
  quay.io/compliance-operator/openscap:latest \
  oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_docker \
  /usr/share/xml/scap/ssg/content/ssg-rhel7-ds.xml

运行时安全监控

yaml
# 使用 Sysdig Falco
version: '3.8'

services:
  falco:
    image: falcosecurity/falco:latest
    privileged: true
    volumes:
      - /var/run/docker.sock:/host/var/run/docker.sock
      - /dev:/host/dev
      - /proc:/host/proc:ro
      - /boot:/host/boot:ro
      - /lib/modules:/host/lib/modules:ro
      - /usr:/host/usr:ro
      - /etc:/host/etc:ro
      - ./falco_rules.yaml:/etc/falco/falco_rules.local.yaml
    environment:
      - FALCO_GRPC_ENABLED=true
      - FALCO_GRPC_BIND_ADDRESS=0.0.0.0:5060

合规性和标准

CIS Docker Benchmark

bash
# 下载 CIS Docker Benchmark
wget https://www.cisecurity.org/cis-benchmarks/

# 主要检查项目:
# 1. 主机配置
# 2. Docker 守护进程配置
# 3. Docker 守护进程配置文件
# 4. 容器镜像和构建文件
# 5. 容器运行时
# 6. Docker 安全操作

NIST 容器安全指南

遵循 NIST SP 800-190 容器安全指南:

  1. 镜像安全:使用可信镜像源,定期扫描漏洞
  2. 注册表安全:保护镜像注册表,使用访问控制
  3. 编排器安全:配置 Kubernetes/Swarm 安全策略
  4. 容器安全:最小权限原则,资源限制
  5. 主机安全:加固主机操作系统

实战安全配置

生产环境安全配置

yaml
version: '3.8'

services:
  web:
    image: myapp:v1.0
    user: "1000:1000"
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
      - apparmor:docker-default
    networks:
      - frontend
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M
          cpus: '0.25'

  db:
    image: postgres:13-alpine
    user: "999:999"
    read_only: true
    tmpfs:
      - /tmp
      - /var/run/postgresql
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password
    networks:
      - backend
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

volumes:
  db_data:
    driver: local

secrets:
  db_password:
    external: true

安全检查清单

镜像安全:

  • [ ] 使用官方或可信的基础镜像
  • [ ] 使用具体版本标签,避免 latest
  • [ ] 定期扫描镜像漏洞
  • [ ] 使用多阶段构建减少攻击面
  • [ ] 不在镜像中存储秘密信息

容器运行时安全:

  • [ ] 使用非 root 用户运行容器
  • [ ] 启用只读根文件系统
  • [ ] 删除不必要的 Linux capabilities
  • [ ] 设置资源限制
  • [ ] 禁用新权限获取

网络安全:

  • [ ] 使用自定义网络隔离服务
  • [ ] 只暴露必要的端口
  • [ ] 使用 TLS 加密通信
  • [ ] 配置防火墙规则

数据安全:

  • [ ] 使用 Docker secrets 管理敏感信息
  • [ ] 加密静态数据
  • [ ] 定期备份重要数据
  • [ ] 实施访问控制

监控和审计:

  • [ ] 配置安全日志记录
  • [ ] 实施运行时安全监控
  • [ ] 定期进行安全审计
  • [ ] 建立事件响应流程

本章小结

本章全面介绍了 Docker 安全的各个方面:

关键要点:

  • 镜像安全:使用可信镜像,定期扫描漏洞
  • 运行时安全:最小权限原则,资源限制
  • 网络安全:网络隔离,TLS 加密
  • 秘密管理:避免硬编码,使用专门工具
  • 监控审计:实时监控,定期审计

安全原则:

  • 最小权限原则
  • 深度防御策略
  • 定期更新和扫描
  • 持续监控和审计
  • 事件响应准备

在下一章中,我们将学习 Docker 的性能优化,包括镜像优化、容器性能调优和资源管理。

延伸阅读

本站内容仅供学习和研究使用。