Docker 安全最佳实践——从镜像到运行时的加固指南

Docker 安全最佳实践——从镜像到运行时的加固指南

作者: CaoZH
日期: 2025-10-15
本文为原创教程


Docker 极大地方便了应用部署,但「容器 ≠ 安全沙箱」。如果不注意安全配置,容器可能成为攻击者的突破口。

2025 年,云原生安全已经是运维的必修课。本文从镜像构建、容器运行、网络隔离三个维度,系统性地梳理 Docker 安全最佳实践。

一、镜像安全

1. 选择基础镜像

1
2
3
4
5
6
7
8
9
10
11
# ❌ 不推荐:大而全,攻击面多
FROM ubuntu:latest
FROM node:latest

# ✅ 推荐:Alpine(5MB,最小攻击面)
FROM node:20-alpine
FROM python:3.12-alpine
FROM openjdk:17-jdk-slim

# ✅ 推荐:Distroless(仅包含应用和运行时依赖)
FROM gcr.io/distroless/java17-debian12

镜像大小对比(以 Node.js 为例):

基础镜像 大小 漏洞数(平均)
node:latest ~1GB 200+
node:slim ~200MB 50+
node:alpine ~120MB 10+
Distroless ~100MB <5

2. 多阶段构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 运行阶段(只拷贝必要产物)
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node # 不要用 root 运行

EXPOSE 3000
CMD ["node", "dist/server.js"]

3. 镜像扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用 Trivy 扫描镜像漏洞
# 安装
sudo apt install -y trivy # Ubuntu
brew install trivy # macOS

# 扫描镜像
trivy image node:20-alpine

# 扫描 Dockerfile
trivy config .

# 扫描项目依赖
trivy fs .

# 集成到 CI
trivy image --severity CRITICAL,HIGH --exit-code 1 myapp:latest

4. 最小化安装

1
2
3
4
5
6
7
8
# ❌ 不要安装不必要的包
RUN apt-get update && apt-get install -y \
curl vim git net-tools wget # 这些在生产容器中不需要

# ✅ 只安装必要的
RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

二、容器运行时安全

1. 非 root 运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ 默认用 root
FROM node:20-alpine
COPY . .
RUN npm install
CMD ["node", "app.js"]
# 容器内进程以 root 运行!

# ✅ 创建专用用户
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser # 切换到非 root 用户
CMD ["node", "app.js"]

2. 只读文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 运行容器时启用只读文件系统
docker run --read-only \
--tmpfs /tmp \
--tmpfs /var/run \
myapp:latest

# 配合 docker-compose
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
- /var/run

3. 资源限制

1
2
3
4
5
6
7
# 限制 CPU 和内存
docker run -d \
--memory="512m" \
--memory-swap="512m" \
--cpus="1.5" \
--pids-limit=100 \
myapp:latest
1
2
3
4
5
6
7
8
9
10
11
12
# docker-compose.yml
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '1.5'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M

4. 安全配置参数

1
2
3
4
5
6
7
8
9
# 推荐的安全运行参数
docker run -d \
--security-opt=no-new-privileges:true \ # 禁止提权
--security-opt=seccomp=default.json \ # 系统调用过滤
--cap-drop=ALL \ # 移除所有特权
--cap-add=NET_BIND_SERVICE \ # 只添加需要的
--read-only \ # 只读文件系统
--user 1000:1000 \ # 非 root 用户
myapp:latest

三、网络隔离

1. 网络隔离策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# docker-compose.yml
services:
app:
networks:
- internal

web:
ports:
- "80:80"
networks:
- internal
- frontend

db:
networks:
- internal # 数据库只连接内部网络

networks:
frontend: # 对外网络
internal: # 内部网络(数据库不对外暴露)
internal: true

2. 限制网络能力

1
2
3
4
5
6
7
8
# 不需要网络的容器
docker run --network none my-batch-job

# 只连接指定容器
docker run --network container:db my-app

# 使用自定义网络,不用默认 bridge
docker network create --driver bridge --internal secure-net

四、日志与审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# docker-compose.yml
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

# 启用 Docker 审计
# /etc/docker/daemon.json
# {
# "log-driver": "awslogs", # 或 syslog, fluentd
# "log-opts": {
# "tag": "{{.Name}}/{{.ID}}"
# }
# }

五、Docker daemon 安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// /etc/docker/daemon.json
{
"icc": false, // 禁止容器间通信(默认 bridge)
"live-restore": true, // daemon 重启时保持容器运行
"log-level": "info",
"userland-proxy": false, // 使用 iptables 而非用户态代理
"no-new-privileges": true, // 禁止容器内提权

// 镜像加速和安全
"registry-mirrors": ["..."],
"insecure-registries": [],

// 存储驱动
"storage-driver": "overlay2",

// 最大日志文件
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

六、安全检查清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
## Docker 安全检查清单

### 构建阶段
□ 使用 Alpine 或 Distroless 基础镜像
□ 多阶段构建,最小化产物
□ CI 中集成 Trivy 镜像扫描
□ 定期更新基础镜像
□ 不在镜像中硬编码密钥

### 运行阶段
□ 容器以非 root 用户运行
□ 启用 --read-only 只读文件系统
□ 限制 CPU 和内存
□ 使用 --cap-drop=ALL 移除特权
□ 启用 seccomp 系统调用过滤
□ 使用 --security-opt=no-new-privileges

### 网络
□ 数据库不对外暴露端口
□ 使用自定义网络
□ 不需要网络的容器用 --network none
□ 启用 TLS 加密(远程 API)

### 监控
□ 启用日志轮转
□ 监控容器资源使用
□ 定期扫描镜像漏洞
□ 审计 Docker daemon 配置

七、总结

Docker 安全的核心原则:

1
2
3
4
5
6
7
8
9
10
## 最小权限原则

给容器刚好够用的权限,不多给一分一毫。

1. 镜像 → 最小化(Alpine / Distroless)
2. 用户 → 非 root
3. 文件系统 → 只读
4. 网络 → 最小连接
5. 能力 → 全部移除,按需添加
6. 资源 → 设置上限

首发于 CaoZH 的笔记