Docker 安全实践
容器安全是生产环境部署的关键考虑因素。本文将详细介绍 Docker 安全的各个方面,包括镜像安全、运行时安全、网络安全和最佳实践。
目录
安全概述
1.1 容器安全层次
Docker 安全层次:
┌─────────────────────────────────────────┐
│ 第 7 层: 应用安全 │
│ - 代码安全、依赖管理 │
├─────────────────────────────────────────┤
│ 第 6 层: 镜像安全 │
│ - 基础镜像、漏洞扫描 │
├─────────────────────────────────────────┤
│ 第 5 层: 容器运行时安全 │
│ - 资源限制、权限控制 │
├─────────────────────────────────────────┤
│ 第 4 层: Docker 守护进程安全 │
│ - TLS、访问控制 │
├─────────────────────────────────────────┤
│ 第 3 层: 主机安全 │
│ - 内核安全、系统加固 │
├─────────────────────────────────────────┤
│ 第 2 层: 网络安全 │
│ - 网络隔离、防火墙 │
├─────────────────────────────────────────┤
│ 第 1 层: 物理/基础设施安全 │
│ - 数据中心、硬件安全 │
└─────────────────────────────────────────┘1.2 安全威胁模型
| 威胁类型 | 描述 | 防护措施 |
|---|---|---|
| 镜像漏洞 | 基础镜像或依赖存在 CVE | 定期扫描、使用最小镜像 |
| 权限提升 | 容器逃逸获取主机权限 | 非 root 用户、安全选项 |
| 数据泄露 | 敏感信息泄露 | Secrets 管理、加密 |
| 网络攻击 | 未授权访问、中间人攻击 | 网络隔离、TLS |
| 资源耗尽 | DoS 攻击 | 资源限制、监控 |
镜像安全扫描
2.1 使用 Docker Scout
Docker Scout 是 Docker 官方提供的镜像安全扫描工具。
bash
# 启用 Docker Scout
docker scout enroll
# 扫描镜像
docker scout cves myimage:latest
# 生成详细报告
docker scout cves --format sarif --output report.sarif myimage:latest
# 比较两个镜像
docker scout compare myimage:old myimage:new
# 查看快速概览
docker scout quickview myimage:latest2.2 使用 Trivy
Trivy 是一个全面的安全扫描器,支持容器镜像、文件系统等。
bash
# 安装 Trivy
# macOS
brew install trivy
# Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# 扫描镜像
trivy image myimage:latest
# 扫描特定严重程度
trivy image --severity HIGH,CRITICAL myimage:latest
# 输出 JSON 格式
trivy image --format json --output report.json myimage:latest
# 扫描镜像文件系统
trivy filesystem /path/to/project
# 扫描 Dockerfile
trivy config Dockerfile2.3 使用 Snyk
bash
# 安装 Snyk CLI
npm install -g snyk
# 登录 Snyk
snyk auth
# 扫描 Docker 镜像
snyk container test myimage:latest
# 监控镜像
snyk container monitor myimage:latest
# 生成 SBOM
snyk container sbom --format=cyclonedx1.4+json myimage:latest2.4 集成到 CI/CD
yaml
# GitHub Actions 示例
name: Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'2.5 镜像安全最佳实践
dockerfile
# 1. 使用官方基础镜像
FROM python:3.11-slim-bookworm
# 2. 使用特定版本标签
FROM node:18.17.1-alpine3.18
# 3. 使用最小化基础镜像
FROM python:3.11-alpine
# 或
FROM gcr.io/distroless/python3
# 4. 定期更新基础镜像
# 在 CI/CD 中设置定期重建
# 5. 多阶段构建减少攻击面
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html最小权限原则
3.1 使用非 Root 用户
dockerfile
# 方法 1: 使用现有用户
FROM node:18-alpine
USER node
WORKDIR /app
COPY --chown=node:node . .
CMD ["node", "server.js"]
# 方法 2: 创建专用用户
FROM python:3.11-slim
RUN groupadd --gid 1000 appgroup && \
useradd --uid 1000 --gid appgroup --shell /bin/false appuser
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["python", "app.py"]
# 方法 3: Alpine 创建用户
FROM alpine:3.18
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["./myapp"]3.2 文件权限控制
dockerfile
FROM python:3.11-slim
# 创建用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
# 复制文件并设置权限
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 设置安全的文件权限
RUN chmod -R 755 /app && \
find /app -type f -exec chmod 644 {} \; && \
chmod 755 /app/entrypoint.sh && \
chown -R appuser:appgroup /app
# 移除不必要的权限
RUN chmod -R -w /app/static && \
chmod -R -w /app/templates
USER appuser
# 使用只读根文件系统运行(运行时参数)
# docker run --read-only ...3.3 能力(Capabilities)管理
bash
# 查看容器能力
docker run --rm alpine sh -c 'apk add libcap && capsh --print'
# 删除所有能力,只添加必要的
docker run --rm \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
nginx:alpine
# 常用能力需求:
# - NET_BIND_SERVICE: 绑定特权端口 (<1024)
# - CHOWN: 更改文件所有者
# - SETGID/SETUID: 切换用户组/用户
# - SYS_TIME: 修改系统时间3.4 Seccomp 配置
bash
# 使用默认 Seccomp 配置(推荐)
docker run --rm alpine
# 使用自定义 Seccomp 配置
docker run --rm \
--security-opt seccomp=/path/to/seccomp-profile.json \
myapp:latest
# 禁用 Seccomp(不推荐)
docker run --rm \
--security-opt seccomp=unconfined \
myapp:latest自定义 Seccomp 配置示例:
json
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86"],
"syscalls": [
{
"names": [
"accept",
"accept4",
"bind",
"clone",
"close",
"connect",
"execve",
"exit",
"exit_group",
"fcntl",
"fstat",
"futex",
"getpid",
"getrandom",
"listen",
"mmap",
"mprotect",
"munmap",
"openat",
"read",
"recvfrom",
"recvmsg",
"sendmsg",
"sendto",
"socket",
"write"
],
"action": "SCMP_ACT_ALLOW"
}
]
}Secret 管理
4.1 Docker Secrets(Swarm 模式)
bash
# 创建 secret
echo "my-secret-password" | docker secret create db_password -
# 从文件创建
docker secret create tls_cert cert.pem
# 查看 secrets
docker secret ls
# 在服务中使用 secret
docker service create \
--name myapp \
--secret db_password \
--secret tls_cert \
myimage:latest
# secret 在容器中的位置:/run/secrets/<secret_name>4.2 Docker Compose Secrets
yaml
version: '3.8'
services:
web:
image: myapp:latest
secrets:
- db_password
- api_key
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
API_KEY_FILE: /run/secrets/api_key
db:
image: postgres:15-alpine
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt4.3 使用环境变量(开发环境)
bash
# ❌ 不安全:直接在命令行传递
docker run -e PASSWORD=secret123 myapp
# ✅ 安全:从文件读取
docker run --env-file .env myapp
# ✅ 安全:使用 --env-file
# .env 文件内容:
# PASSWORD_FILE=/run/secrets/password4.4 使用外部 Secret 管理工具
bash
# 使用 HashiCorp Vault
docker run --rm \
-v $(pwd)/vault-config:/vault/config \
-e VAULT_ADDR=http://vault:8200 \
vault:latest \
vault kv get secret/myapp/db_password
# 使用 AWS Secrets Manager
docker run --rm \
-e AWS_REGION=us-east-1 \
-v ~/.aws:/root/.aws:ro \
amazon/aws-cli \
secretsmanager get-secret-value --secret-id myapp/db_password网络安全策略
5.1 网络隔离
bash
# 创建内部网络(无外部访问)
docker network create --internal internal-network
# 创建加密网络
docker network create \
--driver overlay \
--opt encrypted \
secure-network
# 禁用容器间通信
docker network create \
--opt com.docker.network.bridge.enable_icc=false \
isolated-network5.2 防火墙规则
bash
# 查看 Docker 创建的 iptables 规则
iptables -L DOCKER -n -v
iptables -L DOCKER-ISOLATION-STAGE-1 -n -v
# 自定义防火墙规则
# 限制容器访问特定 IP
iptables -I DOCKER-USER -i docker0 -d 192.168.1.100 -j DROP
# 限制容器出站连接
iptables -I DOCKER-USER -i docker0 -p tcp --dport 3306 -j DROP5.3 TLS 加密
bash
# 生成 CA 证书
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem
# 生成服务器证书
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=docker-server" -new -key server-key.pem -out server.csr
openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-out server-cert.pem
# 配置 Docker 守护进程使用 TLS
# /etc/docker/daemon.json
{
"tls": true,
"tlscacert": "/etc/docker/ca.pem",
"tlscert": "/etc/docker/server-cert.pem",
"tlskey": "/etc/docker/server-key.pem",
"tlsverify": true
}5.4 服务网格安全
yaml
# Docker Compose + Istio 示例
version: '3.8'
services:
istio-proxy:
image: istio/proxyv2:1.18.0
volumes:
- ./istio-config:/etc/istio/config
command:
- proxy
- sidecar
- --configPath
- /etc/istio/config
network_mode: service:app
app:
image: myapp:latest
environment:
- SERVICE_MESH_ENABLED=true运行时安全
6.1 资源限制
bash
# 内存限制
docker run -d \
--memory="512m" \
--memory-swap="1g" \
--memory-reservation="256m" \
myapp:latest
# CPU 限制
docker run -d \
--cpus="1.5" \
--cpu-shares=512 \
--cpuset-cpus="0,1" \
myapp:latest
# 进程数限制
docker run -d \
--pids-limit=100 \
myapp:latest
# 存储限制
docker run -d \
--storage-opt size=10G \
myapp:latest6.2 只读文件系统
bash
# 使用只读根文件系统
docker run -d \
--read-only \
--tmpfs /tmp:noexec,nosuid,size=100m \
--tmpfs /var/cache:size=10m \
myapp:latest
# Docker Compose 配置
version: '3.8'
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=100m
- /var/cache:size=10m
- /app/tmp:size=50m6.3 安全选项
bash
# 禁用特权模式(默认)
docker run -d --privileged=false myapp:latest
# 不添加新权限
docker run -d \
--security-opt no-new-privileges:true \
myapp:latest
# 使用 AppArmor 配置
docker run -d \
--security-opt apparmor=docker-default \
myapp:latest
# 使用自定义 AppArmor
docker run -d \
--security-opt apparmor=myapp-profile \
myapp:latest6.4 日志和审计
bash
# 配置日志驱动
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"labels": "production_status",
"env": "OS_VERSION"
}
}
# 启用审计日志
auditctl -w /etc/docker/ -k docker
auditctl -w /var/lib/docker/ -k docker
auditctl -w /var/run/docker.sock -k docker安全加固清单
7.1 主机安全
- [ ] 使用最新的稳定内核
- [ ] 启用 SELinux/AppArmor
- [ ] 配置防火墙规则
- [ ] 禁用不必要的服务
- [ ] 定期更新系统补丁
- [ ] 配置日志监控和告警
- [ ] 使用专用 Docker 主机
7.2 Docker 守护进程安全
- [ ] 启用 TLS 加密通信
- [ ] 配置用户命名空间
- [ ] 限制默认 ulimit
- [ ] 启用日志轮转
- [ ] 配置资源配额
- [ ] 禁用实验性功能(生产环境)
7.3 镜像安全
- [ ] 使用官方基础镜像
- [ ] 定期扫描镜像漏洞
- [ ] 使用最小化基础镜像
- [ ] 多阶段构建减少攻击面
- [ ] 固定镜像版本标签
- [ ] 签名和验证镜像
- [ ] 定期更新依赖
7.4 容器运行时安全
- [ ] 使用非 root 用户
- [ ] 启用只读根文件系统
- [ ] 删除不必要的 capabilities
- [ ] 配置资源限制
- [ ] 使用 Seccomp 配置
- [ ] 配置健康检查
- [ ] 限制进程数
7.5 网络安全
- [ ] 使用自定义网络
- [ ] 限制端口暴露
- [ ] 配置网络策略
- [ ] 启用网络加密
- [ ] 禁用 ICC(如不需要)
- [ ] 配置 TLS 证书
7.6 数据安全
- [ ] 使用 Docker Secrets
- [ ] 加密敏感数据
- [ ] 定期备份数据
- [ ] 配置数据保留策略
- [ ] 使用只读挂载配置文件
安全工具推荐
| 工具 | 用途 | 推荐场景 |
|---|---|---|
| Docker Scout | 镜像扫描 | 开发/CI |
| Trivy | 综合扫描 | CI/CD |
| Snyk | 漏洞管理 | 企业环境 |
| Falco | 运行时安全 | 生产监控 |
| Sysdig | 容器安全 | 企业监控 |
| Twistlock | 完整安全平台 | 企业级 |
| Aqua Security | 容器安全 | 企业级 |