本文档基于 Docker 官方安全指南、CIS Docker Benchmark 以及生产环境运维实践,系统梳理 Docker 容器化部署的安全配置要求与最佳实践。适用于 DevOps 工程师、安全审计人员及架构师参考。
Docker 的镜像、容器层、卷数据默认存放在 /var/lib/docker 目录下。若该目录与系统根分区共享,当镜像膨胀或日志激增时,可能导致根分区空间耗尽,进而影响宿主机稳定性。
安全要求:
/var/lib/docker 挂载到独立的逻辑磁盘或专用分区验证命令:
grep /var/lib/docker /etc/fstab
# 或检查挂载状态
df -h /var/lib/docker
最佳实践:
overlay2 存储驱动时预留 20% 以上的空闲空间Docker 守护进程通过 Unix socket(/var/run/docker.sock)暴露 API,默认该 socket 的所属组为 docker。加入此组的任何用户都能获得与 root 等效的权限——可以挂载宿主机任意目录、访问所有容器、甚至在容器内逃逸到宿主机。
安全要求:
docker 组成员,仅限运维核心人员docker 组验证命令:
getent group docker
风险示例:
# 一个拥有 docker 权限的普通用户即可获取宿主机 root 权限
docker run -v /:/host -it alpine chroot /host
unattended-upgrades)Docker 守护进程默认日志级别为 info,记录关键事件但不过度冗余。生产环境应避免 debug 级别,防止敏感信息泄露到日志中。
安全要求:
info验证命令:
ps -ef | grep dockerd
# 或查看配置文件
cat /etc/docker/daemon.json | grep log-level
推荐配置:
{
"log-level": "info",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
默认情况下,Docker 守护进程通过非加密的 Unix socket 本地通信。若启用远程 API(TCP 2375/2376 端口),必须使用 TLS 加密,防止中间人攻击和未授权访问。
安全要求:
验证命令:
ps -ef | grep dockerd
# 确认存在以下参数:
# --tlsverify --tlscacert=/path/to/ca.pem --tlscert=/path/to/server-cert.pem --tlskey=/path/to/server-key.pem
配置示例:
dockerd \
--tlsverify \
--tlscacert=/etc/docker/ca.pem \
--tlscert=/etc/docker/server-cert.pem \
--tlskey=/etc/docker/server-key.pem \
-H=0.0.0.0:2376
当 Docker 守护进程重启或升级时,默认所有容器会被终止。启用 live-restore 后,容器可以在守护进程不可用时继续运行,保证业务连续性。
安全要求:
live-restore配置方式:
{
"live-restore": true
}
验证命令:
docker info --format '{{ .LiveRestoreEnabled }}'
no-new-privileges 安全选项防止容器内的进程通过 setuid/setgid 二进制文件获取额外权限。即使容器以非 root 用户运行,某些程序仍可能通过文件权限位提升权限,此选项可彻底阻断该路径。
配置方式:
{
"no-new-privileges": true
}
验证命令:
docker info --format '{{ .SecurityOptions }}'
# 确认包含 no-new-privileges
Docker v1 Registry 协议存在安全缺陷,已被 v2 取代。禁用旧版协议可防止客户端意外回退到不安全的通信模式。
配置方式:
{
"disable-legacy-registry": true
}
Docker 默认使用 userland proxy(docker-proxy 进程)处理端口映射。当宿主机内核支持 hairpin NAT(大多数现代内核已支持)时,可直接由 iptables 处理,减少一个潜在的攻击面和性能瓶颈。
配置方式:
{
"userland-proxy": false
}
注意事项:
Docker 实验性功能未经充分验证,可能引入安全漏洞或不稳定行为。生产环境必须禁用。
验证命令:
docker version --format '{{ .Server.Experimental }}'
# 应输出 false
配置方式:
{
"experimental": false
}
默认情况下,容器内进程以 root(UID 0)运行。一旦容器逃逸,攻击者将直接获得宿主机 root 权限。为每个容器创建专用的非特权用户是最基本的安全隔离手段。
安全要求:
USER root 再切换的复杂逻辑正确示例:
FROM node:18-alpine
# 创建专用用户组和用户
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
# 设置工作目录并调整所有权
WORKDIR /app
COPY --chown=appuser:appgroup . /app
# 切换到非特权用户
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
验证命令:
docker run --rm your-image id
# 应输出非 0 的 UID,如 uid=1001(appuser)
镜像体积越小,攻击面越小。移除构建依赖、缓存和调试工具,只保留运行时必需组件。
最佳实践:
alpine、distroless、scratch)多阶段构建示例:
# 构建阶段
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go mod download && CGO_ENABLED=0 go build -o /bin/server
# 运行阶段
FROM gcr.io/distroless/static-debian11
COPY --from=builder /bin/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
ADD 指令功能过于强大(支持自动解压和远程 URL 下载),可能引入不可控的外部依赖。COPY 仅支持本地文件复制,行为更可预测。
对比:
| 特性 | COPY | ADD |
|---|---|---|
| 本地文件复制 | ✅ | ✅ |
| 远程 URL 下载 | ❌ | ✅(不推荐) |
| 自动解压 tar | ❌ | ✅(不可控) |
| 可预测性 | 高 | 低 |
推荐做法:
# ✅ 推荐
COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt
COPY . /app/
# ❌ 避免
ADD https://example.com/package.tar.gz /app/
ADD package.tar.gz /app/ # 自动解压可能不符合预期
docker scan 或 Trivy、Snyk 等工具扫描漏洞Linux 安全模块(LSM)为容器提供额外的强制访问控制层。AppArmor(Ubuntu/Debian 默认)和 SELinux(RHEL/CentOS 默认)可限制容器进程能访问的文件、网络和系统调用。
安全要求:
AppArmor 配置示例:
# 使用自定义 AppArmor 配置文件启动容器
docker run \
--security-opt="apparmor:docker-custom-profile" \
-it centos /bin/bash
验证命令:
docker inspect <container_id> --format '{{ .HostConfig.SecurityOpt }}'
--privileged 标志授予容器几乎与宿主机 root 等同的权限:访问所有设备、绕过 cgroup 限制、修改内核参数等。这是容器逃逸的最短路径。
安全要求:
--privileged--cap-add 精确授予最小权限验证命令:
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: Privileged={{ .HostConfig.Privileged }}'
替代方案(按需授权):
# ❌ 危险
docker run --privileged -it ubuntu
# ✅ 精确授权
docker run --cap-add=NET_ADMIN --cap-add=SYS_TIME -it ubuntu
将宿主机敏感目录(如 /、/etc、/var/run/docker.sock、/root)挂载到容器,相当于向容器开放宿主机的核心数据。攻击者一旦进入容器,即可读取或篡改宿主机关键文件。
高危挂载清单:
| 宿主机路径 | 风险等级 | 风险说明 |
|---|---|---|
/ |
🔴 极高 | 完整宿主机文件系统暴露 |
/etc |
🔴 极高 | 系统配置、密码哈希泄露 |
/var/run/docker.sock |
🔴 极高 | 获得 docker 组等效权限 |
/root |
🔴 极高 | root 用户全部数据暴露 |
/proc |
🟡 高 | 进程信息、内核参数泄露 |
/sys |
🟡 高 | 内核 sysfs 接口暴露 |
安全要求:
:ro)安全示例:
# ✅ 只读挂载日志目录
docker run -v /var/log/app:/app/logs:ro myapp
# ❌ 危险:暴露整个宿主机
docker run -v /:/host -it alpine
在容器内运行 SSHD 违背了容器"单进程"设计哲学,增加了攻击面。Docker 已提供安全的容器交互机制,无需额外开放 SSH 端口。
替代方案:
docker exec -it <container_id> /bin/shdocker logs -f <container_id>docker cp <container_id>:/path /host/path验证命令:
docker exec <instance_id> ps -el
# 确认无 sshd 进程
--network=host 让容器共享宿主机的网络命名空间,容器可直接监听宿主机所有端口、访问网络接口,且无法通过 Docker 网络策略进行隔离。
安全要求:
-p)暴露服务127.0.0.1 防止外部直接访问验证命令:
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: NetworkMode={{ .HostConfig.NetworkMode }}'
未限制资源的容器可能因内存泄漏或 CPU 密集型任务耗尽宿主机资源,引发拒绝服务(DoS)。
安全要求:
--memory)--memory-swap,建议等于 --memory 防止 swap 逃逸)--cpus)和份额(--cpu-shares)配置示例:
docker run -m 512m --memory-swap=512m --cpus=1.0 myapp
验证命令:
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: Memory={{ .HostConfig.Memory }}'
将容器根文件系统设为只读(--read-only),可防止运行时恶意软件修改系统文件、植入后门。配合 --tmpfs 为需要临时写入的目录提供内存文件系统。
安全要求:
--read-only推荐配置:
docker run \
--read-only \
--tmpfs /run:noexec,nosuid,size=10m \
--tmpfs /tmp:noexec,nosuid,size=50m \
-v app-data:/data \
centos /bin/bash
验证命令:
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: ReadonlyRootfs={{ .HostConfig.ReadonlyRootfs }}'
默认情况下,容器端口映射(-p 8080:80)会监听宿主机所有接口(0.0.0.0)。若宿主机有多个网卡(公网 + 内网),可能意外将服务暴露到公网。
安全要求:
配置示例:
# 仅监听内网接口
docker run -p 192.168.1.10:8080:80 myapp
# 仅监听本地(用于本地开发或 sidecar 模式)
docker run -p 127.0.0.1:8080:80 myapp
验证命令:
docker ps --quiet | \
xargs docker inspect --format '{{ .Id }}: Ports={{ .NetworkSettings.Ports }}'
--pid=host 让容器与宿主机共享进程命名空间,容器内可以查看和信号控制宿主机所有进程,严重破坏隔离性。
安全要求:
--pid=host,使用独立的 PID 命名空间验证命令:
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: PidMode={{ .HostConfig.PidMode }}'
使用 docker exec --user 以非容器默认用户执行命令,可能绕过 Dockerfile 中设置的安全上下文,导致权限提升或审计追踪困难。
安全要求:
docker exec 不附加 --user 参数su 或 sudo(需预先配置)设置合理的重启策略,防止故障容器无限重启消耗资源,同时保证临时故障自动恢复。
推荐配置:
# 失败时最多重试 5 次,之后保持停止状态等待人工介入
docker run --detach --restart=on-failure:5 nginx
策略对比:
| 策略 | 行为 | 适用场景 |
|---|---|---|
no |
不自动重启 | 一次性任务、批处理 |
on-failure |
退出码非 0 时重启 | 大多数服务 |
on-failure:5 |
失败最多重启 5 次 | 生产环境推荐 |
always |
总是重启 | 守护进程、关键服务 |
unless-stopped |
除非手动停止,否则总是重启 | 开发环境 |
建立自动化脚本,定期对照本基线检查 Docker 配置和运行容器状态:
#!/bin/bash
# docker-security-audit.sh
echo "=== Docker 安全基线核查 ==="
# 1. 检查特权容器
echo "[1] 特权容器检查:"
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: Privileged={{ .HostConfig.Privileged }}' | \
grep "Privileged=true"
# 2. 检查 host 网络模式
echo "[2] Host 网络模式检查:"
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: NetworkMode={{ .HostConfig.NetworkMode }}' | \
grep "NetworkMode=host"
# 3. 检查 root 用户运行
echo "[3] Root 用户运行检查:"
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: User={{ .Config.User }}' | \
grep "User=$\|User=0:\|User=root"
# 4. 检查敏感目录挂载
echo "[4] 敏感目录挂载检查:"
docker ps --quiet --all | \
xargs docker inspect --format '{{ .Id }}: Binds={{ .HostConfig.Binds }}' | \
grep -E "(/:/|/etc:/|/root:/|/var/run/docker.sock)"
echo "=== 核查完成 ==="
将镜像扫描集成到 CI/CD 流程:
# .gitlab-ci.yml 示例
stages:
- build
- scan
- deploy
build_image:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
scan_image:
stage: scan
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
deploy:
stage: deploy
script:
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
docker exec 操作到审计日志| 安全域 | 关键措施 | 验证命令 |
|---|---|---|
| 主机配置 | /var/lib/docker 独立磁盘 |
df -h /var/lib/docker |
| 主机配置 | docker 组成员管控 |
getent group docker |
| 守护进程 | 日志级别 info |
docker info \| grep 'Logging Driver' |
| 守护进程 | TLS 远程 API | ps -ef \| grep tlsverify |
| 守护进程 | Live Restore 启用 | docker info \| grep 'Live Restore' |
| 守护进程 | 禁用实验性功能 | docker version --format '{{ .Server.Experimental }}' |
| 镜像构建 | 非 root 用户运行 | docker run --rm image id |
| 镜像构建 | 使用 COPY 替代 ADD | 检查 Dockerfile |
| 镜像构建 | 多阶段构建最小化 | 检查 Dockerfile |
| 运行时 | 禁止特权模式 | docker inspect \| grep Privileged |
| 运行时 | 禁止敏感目录挂载 | docker inspect \| grep Binds |
| 运行时 | 禁止容器内 SSH | docker exec ps -el |
| 运行时 | 禁止 host 网络 | docker inspect \| grep NetworkMode |
| 运行时 | 限制内存/CPU | docker inspect \| grep Memory |
| 运行时 | 根文件系统只读 | docker inspect \| grep ReadonlyRootfs |
| 运行时 | 端口绑定限定 IP | docker inspect \| grep Ports |
| 运行时 | 禁止共享 PID | docker inspect \| grep PidMode |
| 运行时 | 合理重启策略 | docker inspect \| grep RestartPolicy |