06. Docker 安全加固与性能优化
6.1 Docker 安全威胁模型
6.1.1 容器安全威胁
┌─────────────────────────────────────────────────────┐
│ 容器安全威胁 │
├─────────────────────────────────────────────────────┤
│ 1. 恶意镜像 │
│ - 包含后门、恶意软件的镜像 │
│ - 过时的软件包含已知漏洞 │
│ │
│ 2. 容器逃逸 │
│ - 突破容器隔离访问宿主机 │
│ - 利用内核漏洞 │
│ │
│ 3. 权限提升 │
│ - 容器以 root 运行 │
│ - 过度的 capabilities │
│ │
│ 4. 敏感数据泄露 │
│ - 密码、密钥硬编码在镜像中 │
│ - 日志中暴露敏感信息 │
│ │
│ 5. 资源滥用 │
│ - 拒绝服务攻击 │
│ - 挖矿程序 │
└─────────────────────────────────────────────────────┘
6.2 镜像安全
6.2.1 使用可信镜像
dockerfile
# 推荐做法 - 使用最小化镜像
FROM python:3.11-alpine
# 或
FROM gcr.io/distroless/python3
镜像来源优先级:
- 官方镜像(Docker Official Images)
- 验证发布者的镜像(Verified Publisher)
- 公司/团队的私有镜像仓库
- 社区镜像(谨慎使用)
6.2.2 镜像扫描
使用 Docker Scan:
bash
# 扫描本地镜像
docker scan myapp:latest
# 扫描时排除低危漏洞
docker scan --severity high myapp:latest
# 详细输出
docker scan --json myapp:latest
# 扫描 Dockerfile
docker scan --file Dockerfile myapp:latest
使用 Trivy(推荐):
bash
# 安装 Trivy
# macOS
brew install trivy
# Linux
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
# 扫描镜像
trivy image python:3.11
# 只显示严重和高危漏洞
trivy image --severity CRITICAL,HIGH nginx:latest
# 扫描并生成报告
trivy image --format json --output report.json myapp:latest
# 扫描 Dockerfile
trivy config Dockerfile
集成到 CI/CD:
yaml
# GitLab CI 示例
scan-image:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
6.2.3 最小化镜像
使用多阶段构建:
dockerfile
# 构建阶段
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 运行阶段 - 使用极小镜像
FROM scratch
# 只复制必要文件
COPY --from=builder /app/main /main
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/main"]
# 结果:从 800MB 减小到 10MB
使用 Distroless 镜像:
dockerfile
# Node.js 应用
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
FROM gcr.io/distroless/nodejs18-debian11
COPY --from=builder /app /app
WORKDIR /app
CMD ["server.js"]
# 优势:
# - 没有 shell(防止反弹shell攻击)
# - 没有包管理器
# - 极小的攻击面
6.2.4 不在镜像中存储秘密
dockerfile
# ❌ 错误做法
FROM node:18
WORKDIR /app
COPY . .
# 危险!密码被写入镜像层
RUN echo "DATABASE_PASSWORD=secret123" > .env
RUN npm install
# ✅ 正确做法 - 使用构建参数(仅构建时可用)
FROM node:18
WORKDIR /app
# ARG 不会保存在最终镜像中
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc && \
npm install && \
rm -f .npmrc # 立即删除
COPY . .
# ✅ 运行时使用环境变量或 secrets
# docker run -e DATABASE_PASSWORD=secret myapp
使用 Docker Secrets(Swarm/Kubernetes):
bash
# 创建 secret
echo "my-secret-password" | docker secret create db_password -
# 在 docker-compose.yml 中使用
services:
db:
image: mysql:8.0
secrets:
- db_password
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
external: true
6.3 容器运行时安全
6.3.1 以非 root 用户运行
dockerfile
# ✅ 创建并使用非特权用户
FROM python:3.11-slim
# 创建用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
# 以 root 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 更改文件所有权
COPY --chown=appuser:appuser . .
# 切换到非 root 用户
USER appuser
CMD ["python", "app.py"]
运行时指定用户:
bash
# 使用 UID
docker run --user 1000:1000 myapp
# 使用用户名
docker run --user appuser myapp
# docker-compose.yml
services:
app:
image: myapp
user: "1000:1000"
6.3.2 限制容器 Capabilities
什么是 Capabilities?
- Linux 将 root 权限细分为多个独立能力
- Docker 默认授予容器部分 capabilities
- 应该遵循最小权限原则
查看容器的 capabilities:
bash
docker run --rm -it alpine sh -c 'apk add -U libcap; capsh --print'
移除不必要的 capabilities:
bash
# 移除所有 capabilities,只添加需要的
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
# docker-compose.yml
services:
web:
image: nginx
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- CHOWN
常用 Capabilities:
NET_BIND_SERVICE - 绑定小于 1024 的端口
CHOWN - 更改文件所有权
DAC_OVERRIDE - 绕过文件权限检查
SETUID/SETGID - 设置 UID/GID
SYS_ADMIN - 执行系统管理任务(危险!避免使用)
6.3.3 使用只读文件系统
bash
# 只读根文件系统
docker run --read-only nginx
# 需要写入的目录使用 tmpfs
docker run --read-only --tmpfs /tmp --tmpfs /run nginx
# docker-compose.yml
services:
app:
image: myapp
read_only: true
tmpfs:
- /tmp
- /var/cache
6.3.4 限制系统调用(Seccomp)
默认 Seccomp 配置文件已经很安全,通常不需要修改。
自定义 Seccomp 配置:
json
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "open", "close", "stat"],
"action": "SCMP_ACT_ALLOW"
}
]
}
bash
# 使用自定义配置
docker run --security-opt seccomp=custom-seccomp.json myapp
# 禁用 seccomp(不推荐)
docker run --security-opt seccomp=unconfined myapp
6.3.5 AppArmor / SELinux
AppArmor(Ubuntu/Debian):
bash
# 查看 AppArmor 状态
sudo aa-status
# 使用自定义 AppArmor 配置文件
docker run --security-opt apparmor=docker-default nginx
# 加载自定义配置文件
sudo apparmor_parser -r -W /path/to/profile
SELinux(RHEL/CentOS):
bash
# 启用 SELinux
docker run --security-opt label=type:container_t myapp
# 禁用 SELinux(不推荐)
docker run --security-opt label=disable myapp
6.4 网络安全
6.4.1 网络隔离
yaml
# docker-compose.yml
version: '3.8'
services:
# 前端 - 只在前端网络
web:
image: nginx
networks:
- frontend
ports:
- "80:80"
# API - 连接两个网络
api:
image: myapi
networks:
- frontend
- backend
# 数据库 - 只在后端网络(外部无法访问)
db:
image: postgres
networks:
- backend
# 不暴露端口到主机
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 内部网络,无法访问外网
6.4.2 限制容器间通信
bash
# 创建禁止容器间通信的网络
docker network create --driver bridge --opt com.docker.network.bridge.enable_icc=false isolated-network
# 使用该网络
docker run --network isolated-network myapp
6.4.3 使用加密通信
dockerfile
# 配置 HTTPS
FROM nginx:alpine
# 复制 SSL 证书
COPY ssl/cert.pem /etc/nginx/ssl/
COPY ssl/key.pem /etc/nginx/ssl/
# NGINX 配置
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 443
nginx.conf:
nginx
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://app:8080;
}
}
6.5 资源限制
6.5.1 CPU 限制
bash
# 限制 CPU 使用率为 50%
docker run --cpus="0.5" myapp
# 限制到特定 CPU 核心
docker run --cpuset-cpus="0,1" myapp
# CPU shares(相对权重,默认 1024)
docker run --cpu-shares=512 myapp
# docker-compose.yml
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '0.5'
reservations:
cpus: '0.25'
6.5.2 内存限制
bash
# 限制内存为 512MB
docker run -m 512m myapp
# 内存 + Swap 限制
docker run -m 512m --memory-swap 1g myapp
# 禁用 Swap
docker run -m 512m --memory-swap 512m myapp
# OOM 时不杀死容器(不推荐)
docker run -m 512m --oom-kill-disable myapp
# docker-compose.yml
services:
app:
image: myapp
mem_limit: 512m
memswap_limit: 1g
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
6.5.3 存储限制
bash
# 限制容器可写层大小
docker run --storage-opt size=10G myapp
# 限制 tmpfs 大小
docker run --tmpfs /tmp:rw,size=100m,mode=1777 myapp
6.5.4 进程数限制
bash
# 限制容器内最大进程数
docker run --pids-limit 100 myapp
# docker-compose.yml
services:
app:
image: myapp
pids_limit: 100
6.5.5 I/O 限制
bash
# 限制读写速率(bytes/second)
docker run --device-read-bps /dev/sda:1mb myapp
docker run --device-write-bps /dev/sda:1mb myapp
# 限制 IOPS
docker run --device-read-iops /dev/sda:1000 myapp
docker run --device-write-iops /dev/sda:1000 myapp
6.6 性能优化
6.6.1 镜像优化
减小镜像体积:
dockerfile
# 1. 使用 Alpine 基础镜像
FROM node:18-alpine # 而不是 node:18
# 2. 合并 RUN 命令
RUN apt-get update && \
apt-get install -y package1 package2 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 3. 使用 .dockerignore
# .dockerignore:
node_modules
*.log
.git
.env
README.md
# 4. 清理缓存
RUN pip install --no-cache-dir -r requirements.txt
RUN npm ci --only=production && npm cache clean --force
# 5. 多阶段构建
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
镜像体积对比:
ubuntu:latest 77MB
alpine:latest 7MB
scratch 0MB (空镜像)
node:18 1GB
node:18-alpine 170MB
python:3.11 1GB
python:3.11-slim 130MB
python:3.11-alpine 50MB
6.6.2 构建缓存优化
dockerfile
# ✅ 好的顺序(依赖先复制)
FROM python:3.11-slim
WORKDIR /app
# 1. 先复制依赖文件(不常变化)
COPY requirements.txt .
RUN pip install -r requirements.txt
# 2. 再复制代码(经常变化)
COPY . .
CMD ["python", "app.py"]
# ❌ 差的顺序
FROM python:3.11-slim
WORKDIR /app
COPY . . # 代码变化导致后续都要重新构建
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
6.6.3 容器启动优化
dockerfile
# 1. 使用 ENTRYPOINT + CMD
ENTRYPOINT ["python"]
CMD ["app.py"]
# 2. 使用 exec 形式(不启动 shell,减少进程)
CMD ["nginx", "-g", "daemon off;"] # ✅
CMD nginx -g "daemon off;" # ❌
# 3. 预热应用
RUN python -c "import django; django.setup()"
# 4. 编译 Python 字节码
RUN python -m compileall .
6.6.4 网络性能优化
bash
# 使用 host 网络(性能最佳,但失去隔离)
docker run --network host myapp
# 调整 MTU
docker network create --opt com.docker.network.driver.mtu=1450 my-network
# 使用 macvlan(接近原生性能)
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 macvlan-network
6.6.5 存储性能优化
bash
# 使用更快的存储驱动
docker info | grep "Storage Driver"
# 对于 Linux,推荐 overlay2
# 编辑 /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
# Volume 性能优化
# 1. 使用命名卷而不是绑定挂载
docker run -v my-volume:/data myapp # ✅ 快
docker run -v /host/path:/data myapp # ❌ 慢
# 2. 使用 tmpfs 存储临时数据
docker run --tmpfs /tmp:rw,size=1g myapp
# 3. 使用 :cached 选项(macOS)
docker run -v /host/path:/data:cached myapp
6.6.6 日志优化
json
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"compress": "true"
}
}
yaml
# docker-compose.yml
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
6.7 监控和审计
6.7.1 资源监控
bash
# 实时监控容器资源
docker stats
# 监控特定容器
docker stats container-name
# 格式化输出
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# 只显示一次
docker stats --no-stream
6.7.2 事件监控
bash
# 实时查看 Docker 事件
docker events
# 过滤特定事件
docker events --filter event=start
docker events --filter container=myapp
# 查看历史事件
docker events --since '2024-01-01T00:00:00' --until '2024-01-02T00:00:00'
6.7.3 审计日志
bash
# 启用审计
# 编辑 /etc/audit/rules.d/docker.rules
-w /usr/bin/docker -k docker
-w /var/lib/docker -k docker
-w /etc/docker -k docker
-w /usr/lib/systemd/system/docker.service -k docker
-w /etc/default/docker -k docker
# 重启审计服务
sudo service auditd restart
# 查看审计日志
sudo ausearch -k docker | aureport
6.8 Docker Bench Security
自动化安全检查工具:
bash
# 运行 Docker Bench Security
docker run -it --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security
# 检查项包括:
# - 主机配置
# - Docker daemon 配置
# - Docker daemon 文件权限
# - 容器镜像和构建文件
# - 容器运行时
# - Docker 安全操作
# - Docker Swarm 配置