强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Docker 完全指南 / 12 - 安全加固

12 - 安全加固

理解容器安全机制:namespace、cgroup、seccomp、AppArmor,以及 Rootless 模式与安全最佳实践。


12.1 容器安全概述

容器安全是一个多层次的防御体系,涵盖从内核到应用的各个层面。

容器安全层次:
  ┌─────────────────────────────────────┐
  │  应用层安全: 代码审计、依赖扫描       │
  ├─────────────────────────────────────┤
  │  镜像安全: 最小镜像、漏洞扫描、签名   │
  ├─────────────────────────────────────┤
  │  运行时安全: seccomp、AppArmor/SELinux│
  ├─────────────────────────────────────┤
  │  容器隔离: namespace、cgroup          │
  ├─────────────────────────────────────┤
  │  宿主机安全: 内核加固、Rootless       │
  ├─────────────────────────────────────┤
  │  网络安全: 网络策略、TLS、防火墙      │
  └─────────────────────────────────────┘

容器 vs 虚拟机安全对比

维度 容器 虚拟机
隔离级别 进程级 (namespace) 硬件级 (hypervisor)
内核共享 ✅ 共享宿主机内核 ❌ 独立内核
攻击面 较大(共享内核) 较小
逃逸风险 较高 较低
启动安全 快速销毁重建 迁移成本高

12.2 Linux Namespace 安全

Namespace 隔离类型

Namespace 隔离内容 安全影响
PID 进程 ID 看不到宿主机其他进程
Network 网络栈 独立的网络接口和端口
Mount 文件系统挂载 无法访问宿主机文件系统
UTS 主机名 独立的 hostname
IPC 进程间通信 隔离信号量和消息队列
User 用户 ID 非特权用户映射

User Namespace 实验

# 查看当前用户的 UID 映射
cat /proc/self/uid_map

# 使用 User Namespace 运行容器
docker run --rm --userns=host alpine id

# 在容器内查看 UID 映射
docker run --rm alpine cat /proc/self/uid_map

PID Namespace 验证

# 容器内只能看到自己的进程
docker run --rm alpine ps aux
# PID   USER     TIME  COMMAND
#     1 root      0:00 ps aux

# 对比宿主机
ps aux | wc -l  # 可能有上百个进程

12.3 Cgroup 资源限制

内存限制

# 限制内存 256MB,swap 512MB
docker run -d --name mem-limited \
    --memory=256m \
    --memory-swap=512m \
    --memory-reservation=128m \
    --oom-kill-disable=false \
    nginx:alpine

# 查看内存使用
docker stats mem-limited --no-stream

# 查看 cgroup 内存限制
docker exec mem-limited cat /sys/fs/cgroup/memory.max

CPU 限制

# 限制使用 1.5 个 CPU
docker run -d --name cpu-limited --cpus=1.5 nginx:alpine

# 绑定到特定 CPU 核心
docker run -d --name cpu-pinned --cpuset-cpus="0,1" nginx:alpine

# CPU 权重(相对权重,默认 1024)
docker run -d --name high-priority --cpu-shares=2048 nginx:alpine
docker run -d --name low-priority --cpu-shares=512 nginx:alpine

# 查看 CPU 使用
docker stats cpu-limited --no-stream

I/O 限制

# 限制块设备读写速度
docker run -d --name io-limited \
    --device-read-bps /dev/sda:10mb \
    --device-write-bps /dev/sda:5mb \
    --device-read-iops /dev/sda:1000 \
    --device-write-iops /dev/sda:500 \
    nginx:alpine

进程数限制

# 限制最大进程数
docker run -d --name pid-limited --pids-limit=50 nginx:alpine

# 防止 fork bomb
docker run -d --pids-limit=100 nginx:alpine

资源限制最佳实践

资源 生产建议 说明
内存 必须设置 --memory 防止 OOM 影响宿主机
CPU 推荐设置 --cpus 防止单容器占用全部 CPU
进程数 推荐设置 --pids-limit 防止 fork bomb
I/O 按需设置 适用于 I/O 密集型应用

12.4 Seccomp 安全计算模式

Seccomp (Secure Computing Mode) 限制容器可以调用的系统调用(syscall)。

Docker 默认 Seccomp 配置

Docker 默认禁止约 44 个危险的系统调用:

被禁止的系统调用示例:
  ├── mount / umount:     防止挂载文件系统
  ├── reboot:             防止重启系统
  ├── kexec_load:         防止加载新内核
  ├── open_by_handle_at:  防止绕过文件系统权限
  ├── init_module / finit_module: 防止加载内核模块
  ├── bpf:                防止操作 BPF 程序
  └── ...

使用自定义 Seccomp 配置

# 查看默认 Seccomp 配置
docker info | grep -i seccomp

# 使用自定义 Seccomp 配置
docker run --security-opt seccomp=my-seccomp.json nginx:alpine

# 禁用 Seccomp(不推荐)
docker run --security-opt seccomp=unconfined nginx:alpine

自定义 Seccomp Profile

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "defaultErrnoRet": 1,
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["read", "write", "open", "close", "stat", "fstat",
                "lseek", "mmap", "mprotect", "munmap", "brk",
                "ioctl", "access", "pipe", "select", "sched_yield",
                "dup", "dup2", "nanosleep", "getpid", "clone",
                "execve", "exit", "wait4", "kill", "fcntl",
                "flock", "fsync", "ftruncate", "getdents",
                "getcwd", "chdir", "mkdir", "rmdir", "unlink",
                "readlink", "chmod", "chown", "getuid", "getgid",
                "geteuid", "getegid", "getppid", "getpgrp",
                "setsid", "setuid", "setgid", "getgroups",
                "setgroups", "sigaltstack", "rt_sigaction",
                "rt_sigprocmask", "socket", "connect", "accept",
                "sendto", "recvfrom", "bind", "listen", "getsockname",
                "getpeername", "socketpair", "epoll_create",
                "epoll_ctl", "epoll_wait", "clock_gettime",
                "exit_group", "epoll_create1", "pipe2",
                "pread64", "pwrite64", "readv", "writev",
                "signalfd4", "eventfd2", "epoll_create1",
                "dup3", "prlimit64", "getrandom"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

12.5 AppArmor 与 SELinux

AppArmor

# 查看 Docker 默认 AppArmor 配置
sudo cat /etc/apparmor.d/docker-default

# 使用自定义 AppArmor 配置
docker run --security-opt apparmor=my-profile nginx:alpine

# 禁用 AppArmor(不推荐)
docker run --security-opt apparmor=unconfined nginx:alpine

# 查看容器的 AppArmor 状态
docker inspect --format '{{.AppArmorProfile}}' my-container

SELinux

# 启用 SELinux 标签
docker run --security-opt label=type:my_container_t nginx:alpine

# 禁用 SELinux 标签
docker run --security-opt label=disable nginx:alpine

# 查看 SELinux 状态
getenforce

12.6 Rootless 模式

优势

特性 Root 模式 Rootless 模式
daemon 运行身份 root 普通用户
容器默认用户 root root (在 namespace 中)
宿主机影响 daemon 漏洞可获取 root daemon 漏洞仅获取用户权限
端口 < 1024 ❌(需额外配置)
cgroup v2 完整支持 部分

配置 Rootless

# 安装 Rootless Docker
dockerd-rootless-setuptool.sh install

# 配置环境变量
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock

# 使用 systemd 管理
systemctl --user enable docker
systemctl --user start docker

# 开启 lingering
sudo loginctl enable-linger $(whoami)

Rootless 限制与解决方案

# 问题: 无法使用 < 1024 端口
# 解决: 设置内核参数
sudo sysctl net.ipv4.ip_unprivileged_port_start=80

# 问题: 需要额外的 uid/gid 映射
# 解决: 编辑 /etc/subuid 和 /etc/subgid
username:100000:65536

# 问题: overlay2 需要 fuse-overlayfs
# 解决: 安装 fuse-overlayfs
sudo apt-get install fuse-overlayfs

12.7 容器安全最佳实践

镜像安全

# ✅ 使用最小基础镜像
FROM alpine:3.19

# ✅ 使用非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# ✅ 使用只读文件系统
# (运行时: docker run --read-only)

# ✅ 不安装不必要的包
RUN apk add --no-cache --virtual .build-deps build-base && \
    apk add --no-cache python3 && \
    apk del .build-deps

# ✅ 固定版本
FROM python:3.11.7-slim-bookworm

运行时安全

# ✅ 只读根文件系统
docker run --read-only --tmpfs /tmp nginx:alpine

# ✅ 禁用特权模式
# ❌ docker run --privileged (极不安全)
# ✅ 仅添加所需的能力
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx:alpine

# ✅ 禁止获取新权限
docker run --security-opt=no-new-privileges nginx:alpine

# ✅ 使用自定义 Seccomp
docker run --security-opt seccomp=custom-seccomp.json nginx:alpine

# ✅ 删除 Linux capabilities
docker run --cap-drop=ALL my-app:latest

# ✅ 设置内存和 CPU 限制
docker run -m 512m --cpus=1 my-app:latest

# ✅ 设置 PID 限制
docker run --pids-limit=100 my-app:latest

Linux Capabilities

Capability 说明 是否需要
NET_BIND_SERVICE 绑定 < 1024 端口 按需
CHOWN 修改文件所有者 按需
SETUID/SETGID 切换用户/组 按需
NET_RAW 使用原始套接字 通常不需要
SYS_ADMIN 大量系统管理操作 极不推荐
ALL 所有能力 永远不要使用
# 最小权限运行
docker run \
    --cap-drop=ALL \
    --cap-add=NET_BIND_SERVICE \
    --read-only \
    --tmpfs /tmp:rw,noexec,nosuid,size=100m \
    --security-opt=no-new-privileges \
    --pids-limit=100 \
    -m 256m \
    nginx:alpine

12.8 Docker Content Trust (DCT)

# 启用内容信任(仅拉取签名镜像)
export DOCKER_CONTENT_TRUST=1

# 拉取签名镜像
docker pull my-user/my-app:v1.0

# 推送签名镜像
docker push my-user/my-app:v1.0
# 首次会提示创建签名密钥

# 查看签名信息
docker trust inspect --pretty my-user/my-app

# 禁用内容信任
export DOCKER_CONTENT_TRUST=0

12.9 安全扫描

# Docker Scout
docker scout cves nginx:alpine
docker scout quickview nginx:alpine

# Trivy
trivy image nginx:alpine
trivy image --severity HIGH,CRITICAL nginx:alpine

# 扫描 Dockerfile
trivy config Dockerfile

# 扫描文件系统
trivy fs --security-checks vuln,config .

要点回顾

要点 核心内容
隔离机制 namespace (隔离) + cgroup (限制) + seccomp (系统调用过滤)
最小权限 --cap-drop=ALL + --cap-add 仅添加所需能力
Rootless daemon 以非 root 用户运行,降低攻击面
只读文件系统 --read-only + --tmpfs /tmp 防止文件篡改
镜像扫描 Docker Scout / Trivy 扫描漏洞

注意事项

永远不要使用 --privileged: 这会赋予容器几乎等同于宿主机 root 的权限,是最大的安全隐患。

及时更新基础镜像: 定期重建镜像以获取安全补丁。设置 CI/CD 定时任务自动扫描。

限制容器资源: 生产环境必须设置内存、CPU、PID 限制,防止单容器影响整个宿主机。


下一步

13 - 日志管理:学习 Docker 日志驱动与集中日志方案。