Docker安全加固:给容器加把锁

2862 字
14 分钟
Docker安全加固:给容器加把锁

为什么容器安全很重要?#

Docker容器虽然提供了隔离,但并非坚不可摧。常见的容器安全风险包括:

  • 镜像漏洞 - 包含已知漏洞的软件包
  • 权限过大 - 容器以root运行,可能逃逸到宿主机
  • 敏感信息泄露 - 密码、密钥硬编码在镜像中
  • 供应链攻击 - 恶意镜像、被污染的基础镜像
  • 运行时攻击 - 容器被入侵后威胁整个系统

安全不是可选项,而是必选项。 特别是当容器运行在公网环境或存储重要数据时。

安全加固框架#

Docker安全应该遵循纵深防御原则:

┌─────────────────────────────────────────┐
│ 运行时监控 │
├─────────────────────────────────────────┤
│ 网络隔离 │
├─────────────────────────────────────────┤
│ 资源限制 │
├─────────────────────────────────────────┤
│ 最小权限原则 │
├─────────────────────────────────────────┤
│ 镜像安全 │
└─────────────────────────────────────────┘

1. 镜像安全加固#

1.1 使用官方镜像#

Terminal window
# ✅ 推荐:使用官方镜像
docker pull nginx:latest
docker pull postgres:14-alpine
# ❌ 避免:使用不明来源的镜像
docker pull random-user/nginx-custom

验证镜像:

Terminal window
# 查看镜像详细信息
docker image inspect nginx:latest
# 查看镜像历史
docker history nginx:latest
# 查看镜像签名(如果支持)
docker trust inspect nginx:latest

1.2 使用Alpine Linux基础镜像#

Alpine是一个只有5MB的安全轻量级Linux发行版。

# ❌ 不推荐:Ubuntu基础镜像(~100MB)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
# ✅ 推荐:Alpine基础镜像(~5MB)
FROM alpine:3.19
RUN apk add --no-cache nginx

Alpine的优势:

  • ✅ 体积小(减少攻击面)
  • ✅ 包管理简单(apk)
  • ✅ 安全更新及时
  • ✅ 默认配置安全

1.3 最小化镜像层数#

# ❌ 不推荐:多个RUN命令
FROM alpine:3.19
RUN apk add nginx
RUN apk add php
RUN apk add php-fpm
RUN rm -rf /var/cache/apk/*
# ✅ 推荐:合并RUN命令
FROM alpine:3.19
RUN apk add --no-cache nginx php php-fpm && \
rm -rf /var/cache/apk/*

1.4 镜像扫描#

使用Trivy扫描镜像漏洞:

Terminal window
# 安装Trivy
brew install trivy # macOS
# 或
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 nginx:alpine
# 扫描并生成报告
trivy image --format json --output report.json nginx:alpine
# 只显示高危漏洞
trivy image --severity HIGH,CRITICAL nginx:alpine

Docker Desktop内置扫描:

Terminal window
# Docker Desktop 4.15+ 内置漏洞扫描
docker scan nginx:alpine

1.5 签名和验证镜像#

Terminal window
# 启用Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# 现在只能拉取已签名的镜像
docker pull nginx:latest
# 签名你的镜像
docker trust sign my-image:latest
# 验证镜像签名
docker trust inspect my-image:latest

1.6 多阶段构建#

多阶段构建可以减少最终镜像大小,避免泄露敏感信息。

# ❌ 不推荐:单阶段构建(包含源代码和构建工具)
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
FROM alpine:3.19
COPY --from=builder /app/myapp /usr/local/bin/myapp
# 最终镜像不包含源代码和Go编译器
# ✅ 推荐:多阶段构建
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o myapp
FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/myapp
ENTRYPOINT ["/usr/local/bin/myapp"]

2. 运行时安全加固#

2.1 最小权限原则#

不要以root运行容器#

# ❌ 不推荐:以root运行
FROM alpine:3.19
RUN apk add --no-cache nginx
CMD ["nginx", "-g", "daemon off;"]
# ✅ 推荐:创建非root用户
FROM alpine:3.19
RUN apk add --no-cache nginx && \
addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
CMD ["nginx", "-g", "daemon off;"]

运行时指定用户:

Terminal window
docker run -d \
-u 1000:1000 \
--security-opt no-new-privileges \
my-image

只读文件系统#

Terminal window
# 以只读模式运行容器
docker run -d \
--read-only \
--tmpfs /tmp \
--tmpfs /var/run \
nginx:alpine
# Docker Compose
services:
nginx:
image: nginx:alpine
read_only: true
tmpfs:
- /tmp
- /var/run
- /var/cache/nginx

2.2 能力(Capabilities)管理#

Linux capabilities将root权限分解为更细粒度的权限。

Terminal window
# 移除所有能力(最安全)
docker run --rm \
--cap-drop ALL \
--security-opt no-new-privileges \
alpine sleep 100
# 只添加需要的能力
docker run --rm \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
nginx:alpine
# 查看容器能力
docker run --rm \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
alpine \
sh -c "apk add libcap && capsh --print"

常用能力:

  • NET_BIND_SERVICE - 绑定特权端口(<1024)
  • CHOWN - 改变文件所有者
  • SETUID / SETGID - 设置用户/组ID
  • NET_ADMIN - 网络管理配置

2.3 资源限制#

防止容器消耗过多资源导致系统崩溃。

Terminal window
# 限制内存使用
docker run -d \
--memory="512m" \
--memory-swap="1g" \
my-app
# 限制CPU使用
docker run -d \
--cpus="1.5" \
--cpuset-cpus="0,1" \
my-app
# 限制存储
docker run -d \
--storage-opt size=10G \
my-app
# Docker Compose配置
services:
app:
image: my-app
deploy:
resources:
limits:
cpus: '1.5'
memory: 512M
reservations:
cpus: '0.5'
memory: 128M

2.4 禁用特权模式#

Terminal window
# ❌ 危险:特权模式(拥有所有能力)
docker run --privileged my-app
# ✅ 安全:非特权模式
docker run my-app
# 如果需要特定设备,只映射需要的设备
docker run \
--device=/dev/sda \
my-app

2.5 用户命名空间隔离#

Terminal window
# 启用用户命名空间(需要Docker守护进程配置)
docker run -d \
--userns-remap=default \
my-app
# 或在/etc/docker/daemon.json中配置
{
"userns-remap": "default"
}

3. 网络安全加固#

3.1 创建隔离网络#

Terminal window
# 创建隔离网络
docker network create --internal isolated-network
# 容器只能互相访问,无法访问外网
docker run --network isolated-network my-app

3.2 防火墙规则#

Terminal window
# 使用iptables限制容器访问
sudo iptables -A DOCKER-USER -s 10.0.0.0/8 -j DROP
sudo iptables -A DOCKER-USER -d 10.0.0.0/8 -j DROP

3.3 加密通信#

version: '3.8'
services:
postgres:
image: postgres:14
environment:
- POSTGRES_HOST_AUTH_METHOD=scram-sha-256
volumes:
- postgres_certs:/var/lib/postgresql/certs
volumes:
postgres_certs:

4. 敏感信息管理#

4.1 使用Docker Secrets#

version: '3.8'
services:
db:
image: postgres:14
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt

4.2 使用环境变量文件#

Terminal window
# .env文件(不要提交到Git)
DB_PASSWORD=your_secure_password
API_KEY=your_api_key
# docker-compose.yml
version: '3.8'
services:
app:
image: my-app
env_file:
- .env

.gitignore:

.env
secrets/
*.key
*.pem

4.3 使用Docker Configs#

Terminal window
# 创建配置
echo "my_config_value" | docker config create my_config -
# 在服务中使用
docker service create \
--config source=my_config,target=/app/config.txt \
my-app

5. Dockerfile安全最佳实践#

完整的安全Dockerfile示例#

# 1. 使用特定版本的Alpine
FROM alpine:3.19.1
# 2. 添加标签(方便追踪)
LABEL maintainer="security@example.com" \
version="1.0" \
description="Secure base image"
# 3. 安装安全更新
RUN apk update && \
apk upgrade && \
apk add --no-cache \
ca-certificates \
curl \
&& rm -rf /var/cache/apk/*
# 4. 创建非root用户
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
# 5. 切换工作目录
WORKDIR /app
# 6. 复制依赖文件(利用Docker缓存)
COPY package*.json ./
RUN npm ci --only=production
# 7. 复制应用代码
COPY --chown=appuser:appuser . .
# 8. 切换到非root用户
USER appuser
# 9. 暴露端口
EXPOSE 3000
# 10. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 11. 使用exec形式(优雅关闭)
ENTRYPOINT ["node", "server.js"]

Dockerfile安全检查清单#

  • 使用官方或可信的基础镜像
  • 使用特定版本标签(避免latest
  • 最小化镜像层数
  • 不以root用户运行
  • 不存储敏感信息
  • 使用.dockerignore文件
  • 添加健康检查
  • 使用--no-cache构建避免缓存问题
  • 签名和验证镜像

6. Docker Compose安全配置#

完整的安全配置示例#

version: '3.8'
services:
# Web应用
app:
image: my-app:1.0
restart: unless-stopped
# 资源限制
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 128M
# 只读文件系统
read_only: true
tmpfs:
- /tmp
- /app/cache
# 非root用户
user: "1000:1000"
# 能力限制
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# 安全选项
security_opt:
- no-new-privileges:true
# 环境变量
env_file:
- .env
# 网络
networks:
- frontend
- backend
# 健康检查
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
# 数据库
postgres:
image: postgres:14-alpine
restart: unless-stopped
# 资源限制
deploy:
resources:
limits:
cpus: '2'
memory: 1G
# 数据卷
volumes:
- postgres_data:/var/lib/postgresql/data
# 环境变量
env_file:
- .env.db
# 网络
networks:
- backend
# 健康检查
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
# Nginx反向代理
nginx:
image: nginx:alpine
restart: unless-stopped
# 只读文件系统
read_only: true
tmpfs:
- /var/run
- /var/cache/nginx
# 能力限制
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- CHOWN
- SETGID
# 安全选项
security_opt:
- no-new-privileges:true
# 配置文件
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
# 端口
ports:
- "80:80"
- "443:443"
# 网络
networks:
- frontend
depends_on:
- app
# 数据卷
volumes:
postgres_data:
driver: local
# 网络
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 隔离网络,无法访问外网

7. 安全监控和审计#

7.1 容器运行时监控#

使用Falco监控容器行为:

Terminal window
# 安装Falco
curl -s https://falco.org/repo/falcosecurity-packages.asc | \
apt-key add -
echo "deb https://download.falco.org/packages/deb stable main" | \
tee -a /etc/apt/sources.list.d/falcosecurity.list
apt-get update && apt-get install -y falco
# 启动Falco
falco -o json_output=true
# 检测异常行为(如shell在容器中运行)
falco -o json_output=true -o "web.enabled=true"

7.2 日志审计#

version: '3.8'
services:
app:
image: my-app
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "app,production"

集中式日志:

Terminal window
# 使用ELK Stack
docker run -d \
--name elasticsearch \
-p 9200:9200 \
elasticsearch:8.0.0
docker run -d \
--name logstash \
--link elasticsearch \
logstash:8.0.0
docker run -d \
--name kibana \
--link elasticsearch \
-p 5601:5601 \
kibana:8.0.0

7.3 定期安全扫描#

自动化扫描脚本:

security-scan.sh
#!/bin/bash
echo "Starting Docker security scan..."
# 扫描所有镜像
for image in $(docker images --format "{{.Repository}}:{{.Tag}}"); do
echo "Scanning $image..."
trivy image --severity HIGH,CRITICAL --no-progress "$image"
done
# 检查运行中的容器
echo "Checking running containers..."
docker ps --format "{{.Names}}" | while read container; do
echo "Checking $container..."
docker inspect "$container" --format='{{.HostConfig.Privileged}}'
docker inspect "$container" --format='{{.HostConfig.User}}'
done
echo "Security scan completed."

定时任务:

Terminal window
# 每周日凌晨3点扫描
0 3 * * 0 /path/to/security-scan.sh >> /var/log/security-scan.log 2>&1

8. 常见安全问题#

8.1 容器逃逸#

症状: 容器内可以看到宿主机文件系统

原因: 特权模式或挂载敏感目录

预防:

Terminal window
# 避免使用--privileged
docker run --privileged # ❌ 危险
# 避免挂载敏感目录
docker run -v /:/hostfs # ❌ 危险
docker run -v /var/run/docker.sock:/var/run/docker.sock # ⚠️ 谨慎

8.2 密钥泄露#

症状: Docker镜像或日志中包含密码、API密钥

预防:

  • 使用环境变量或secrets
  • 不在Dockerfile中硬编码密钥
  • .dockerignore排除敏感文件
  • 使用私有仓库

8.3 供应链攻击#

症状: 拉取的镜像被植入恶意代码

预防:

  • 只使用官方镜像
  • 验证镜像签名
  • 定期扫描镜像
  • 使用固定版本标签

9. 安全检查清单#

部署前检查#

  • 镜像已扫描,无高危漏洞
  • 不使用latest标签
  • 容器不以root运行
  • 不使用特权模式
  • 资源限制已配置
  • 敏感信息使用secrets
  • 网络隔离已配置
  • 健康检查已启用
  • 日志已配置
  • 只读文件系统(如适用)

运行时检查#

  • 监控容器行为
  • 定期审计日志
  • 及时更新镜像
  • 备份数据
  • 测试灾难恢复

10. 安全工具推荐#

工具用途特点
Trivy镜像扫描开源、快速、全面
Falco运行时监控异常检测、规则灵活
Docker Bench安全检查基于CIS基准
Notary镜像签名内容信任、完整性验证
Clair漏洞扫描开源、API友好

Docker Bench Security:

Terminal window
# 运行Docker安全检查
docker run --rm --net host \
--pid host \
--userns host \
--cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/docker-containerd:/usr/bin/docker-containerd:ro \
-v /usr/bin/docker-runc:/usr/bin/docker-runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib/docker:/var/lib/docker:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security

总结#

Docker安全不是一次性配置,而是持续的过程。

核心原则:

  • ✅ 最小权限原则
  • ✅ 纵深防御
  • ✅ 默认拒绝
  • ✅ 持续监控
  • ✅ 定期审计

快速安全检查:

Terminal window
# 一键扫描所有镜像
docker images --format "{{.Repository}}:{{.Tag}}" | \
xargs -I {} trivy image --severity HIGH,CRITICAL {}
# 检查特权容器
docker ps --quiet | \
xargs docker inspect --format='{{.Id}}: Privileged={{.HostConfig.Privileged}}' | \
grep true

实战建议:

  1. 部署前运行Docker Bench Security检查
  2. 使用Trivy扫描所有镜像
  3. 配置资源限制和只读文件系统
  4. 定期更新镜像和基础操作系统
  5. 建立安全监控和应急响应机制

下一步学习:

  • 容器监控和日志管理
  • Kubernetes安全(如果需要)
  • 零信任架构

记住:安全永远比方便重要! 🛡️


系列文章:

  1. Docker快速入门:5分钟搭建你的第一个容器
  2. Docker Compose实战:一键部署n8n工作流平台
  3. Docker网络完全指南:从bridge到macvlan
  4. Docker数据持久化:volume vs bind mount实战
  5. Docker安全加固:给容器加把锁(本文)

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Docker安全加固:给容器加把锁
https://doc.ebeb.fun/posts/docker-security-hardening/
作者
二冰
发布于
2026-03-02
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
二冰
做有用的自动化,记录没用的碎碎念。
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
43
分类
19
标签
93
总字数
39,142
运行时长
0
最后活动
0 天前

目录