Release v1.5.0: Restructure chapters and update for Docker v30.x

This commit is contained in:
Baohua Yang
2026-02-04 22:12:38 -08:00
parent b4b0d4160a
commit fdb879dcf2
304 changed files with 1314 additions and 364 deletions

344
10_ops/security/README.md Normal file
View File

@@ -0,0 +1,344 @@
# 安全
容器安全是生产环境部署的核心考量本章介绍 Docker 的安全机制和最佳实践
## 容器安全的本质
> **核心问题**容器共享宿主机内核隔离性弱于虚拟机如何在便利性和安全性之间取得平衡
```
虚拟机安全模型: 容器安全模型:
┌─────────────────┐ ┌─────────────────┐
│ Guest OS │ │ 容器进程 │
├─────────────────┤ │ (共享内核) │
│ Hypervisor │◄── 隔离边界└────────┬────────┘
├─────────────────┤ │
│ Host OS │ ┌────────┴────────┐
└─────────────────┘ │ Namespace │◄── 隔离边界
│ Cgroups │
完全隔离(性能损耗) │ Capabilities │
└─────────────────┘
进程隔离(轻量但需加固)
```
---
## 核心安全机制
### 1. 命名空间Namespace
提供进程网络文件系统等资源的隔离
| Namespace | 隔离内容 | 安全作用 |
|-----------|---------|---------|
| PID | 进程 | 容器看不到其他进程 |
| NET | 网络 | 独立网络栈 |
| MNT | 文件系统 | 独立的根目录 |
| USER | 用户 | 容器 root 宿主机 root |
| IPC | 进程通信 | 隔离共享内存 |
| UTS | 主机名 | 独立主机名 |
详见 [命名空间](../13_implementation/namespace.md) 章节
### 2. 控制组Cgroups
限制容器的资源使用防止资源耗尽攻击
```bash
# 限制内存(超出会被 OOM Kill
$ docker run -m 512m myapp
# 限制 CPU
$ docker run --cpus=1.5 myapp
# 限制磁盘 I/O
$ docker run --device-write-bps /dev/sda:10mb myapp
```
### 3. 能力机制Capabilities
Linux root 权限拆分为多个细粒度的能力Docker 默认禁用危险能力
| 能力 | 说明 | 默认状态 |
|------|------|---------|
| `CAP_NET_ADMIN` | 网络管理 | 禁用 |
| `CAP_SYS_ADMIN` | 系统管理 | 禁用 |
| `CAP_SYS_PTRACE` | 进程追踪 | 禁用 |
| `CAP_CHOWN` | 更改文件所有者 | 启用 |
| `CAP_NET_BIND_SERVICE` | 绑定低端口 | 启用 |
```bash
# 删除所有能力,只添加需要的
$ docker run --cap-drop=all --cap-add=NET_BIND_SERVICE myapp
# 查看容器的能力
$ docker exec myapp cat /proc/1/status | grep Cap
```
---
## 镜像安全
### 使用可信镜像
```bash
# ✅ 使用官方镜像
$ docker pull nginx
# ✅ 使用经过验证的镜像
$ docker pull bitnami/nginx
# ⚠️ 谨慎使用未知来源镜像
$ docker pull randomuser/suspicious-image
```
### 漏洞扫描
扫描镜像中的已知安全漏洞
```bash
# Docker Scout官方工具
$ docker scout cves nginx:latest
$ docker scout recommendations nginx:latest
# Trivy开源工具
$ trivy image nginx:latest
# Snyk商业工具
$ snyk container test nginx:latest
```
### 镜像签名验证
使用 Docker Content Trust (DCT) 验证镜像来源
```bash
# 启用镜像签名验证
$ export DOCKER_CONTENT_TRUST=1
# 此后的 pull/push 会验证签名
$ docker pull myregistry/myimage:latest
```
---
## 运行时安全
### 1. root 用户运行
> 笔者强调这是最重要的安全实践之一
```dockerfile
FROM node:22-alpine
# 创建非 root 用户
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
# 设置工作目录权限
WORKDIR /app
COPY --chown=appuser:appgroup . .
# 切换用户
USER appuser
CMD ["node", "server.js"]
```
或在运行时指定
```bash
$ docker run -u 1001:1001 myapp
```
### 2. 只读文件系统
```bash
# 根文件系统只读
$ docker run --read-only myapp
# 需要写入的目录使用 tmpfs
$ docker run --read-only --tmpfs /tmp --tmpfs /var/run myapp
```
### 3. 禁用特权模式
```bash
# ❌ 绝对不要在生产环境使用
$ docker run --privileged myapp
# ✅ 只添加必要的能力
$ docker run --cap-add=SYS_TIME myapp
```
### 4. 限制资源
```bash
$ docker run \
-m 512m \ # 内存限制
--cpus=1 \ # CPU 限制
--pids-limit=100 \ # 进程数限制
--ulimit nofile=1024:1024 \ # 文件描述符限制
myapp
```
### 5. 网络隔离
```bash
# 禁用网络(适用于不需要网络的任务)
$ docker run --network=none myapp
# 使用自定义网络隔离
$ docker network create --internal isolated_net
$ docker run --network=isolated_net myapp
```
---
## Dockerfile 安全实践
### 1. 使用精简基础镜像
```dockerfile
# ✅ 好:使用精简镜像
FROM node:22-alpine # ~50MB
FROM gcr.io/distroless/nodejs # ~20MB
# ❌ 差:使用完整镜像
FROM node:22 # ~1GB
FROM ubuntu:24.04 # ~78MB
```
### 2. 多阶段构建
```dockerfile
# 构建阶段
FROM node:22 AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
# 生产阶段(不包含开发依赖和源码)
FROM node:22-alpine
COPY --from=builder /app/dist /app
USER node
CMD ["node", "/app/server.js"]
```
### 3. 不存储敏感信息
```dockerfile
# ❌ 错误:敏感信息写入镜像
ENV DB_PASSWORD=secret123
COPY .env /app/
# ✅ 正确:运行时传入
# docker run -e DB_PASSWORD=xxx 或使用 Docker Secrets
```
### 4. 固定依赖版本
```dockerfile
# ✅ 固定版本
FROM node:22.12.0-alpine3.21
RUN apk add --no-cache curl=8.5.0-r0
# ❌ 使用 latest
FROM node:latest
RUN apk add curl
```
---
## 安全扫描清单
部署前检查
| 检查项 | 命令/方法 |
|--------|----------|
| 漏洞扫描 | `docker scout cves` `trivy` |
| root 运行 | 检查 Dockerfile 中的 `USER` |
| 资源限制 | 检查 `-m`, `--cpus` 参数 |
| 只读文件系统 | 检查 `--read-only` |
| 无特权模式 | 确认没有 `--privileged` |
| 最小能力 | 检查 `--cap-drop=all` |
| 网络隔离 | 检查网络配置 |
| 敏感信息 | 确认无硬编码密码 |
---
## 高级安全方案
### Seccomp 系统调用过滤
限制容器可以使用的系统调用
```bash
$ docker run --security-opt seccomp=/path/to/profile.json myapp
```
### AppArmor / SELinux
使用强制访问控制
```bash
$ docker run --security-opt apparmor=docker-default myapp
```
### 安全容器gVisor / Kata
需要更强隔离时
```bash
# 使用 gVisor 运行时
$ docker run --runtime=runsc myapp
```
---
## 软件供应链安全
随着软件供应链攻击日益频繁仅保障运行时安全已不足够
### 1. SBOM (软件物料清单)
SBOM 类似于食品的配料表列出了容器镜像中包含的所有软件包及其版本
- **生成 SBOM**: 使用 `docker buildx build --sbom` `docker scout sbom`
- **管理 SBOM**: 确保持续监控 SBOM 中的组件是否存在新披露的漏洞
### 2. 镜像签名 (Sigstore / Notary v2)
确保镜像在构建后未被篡改且确实来自可信的发布者
- **Cosign**: Sigstore 项目的一部分用于签署和验证容器镜像
```bash
# 签署镜像
$ cosign sign --key cosign.key myimage:tag
# 验证镜像
$ cosign verify --key cosign.pub myimage:tag
```
### 3. SLSA (Supply-chain Levels for Software Artifacts)
遵循 SLSA 框架确保构建过程的完整性例如使用 GitHub Actions 等受控环境进行构建而非在开发者本地机器上构建发布
---
## 本章小结
| 安全措施 | 重要程度 | 实现方式 |
|---------|---------|---------|
| root 运行 | | `USER` 指令 |
| 漏洞扫描 | | `docker scout`, `trivy` |
| 资源限制 | | `-m`, `--cpus` |
| 只读文件系统 | | `--read-only` |
| 最小能力 | | `--cap-drop=all` |
| 镜像签名 | | Docker Content Trust |
## 延伸阅读
- [命名空间](../13_implementation/namespace.md)隔离机制详解
- [控制组](../13_implementation/cgroups.md)资源限制详解
- [最佳实践](../15_appendix/best_practices.md)Dockerfile 安全配置

View File

@@ -0,0 +1,9 @@
# 控制组
控制组是 Linux 容器机制的另外一个关键组件负责实现资源的审计和限制
它提供了很多有用的特性以及确保各个容器可以公平地分享主机的内存CPU磁盘 IO 等资源当然更重要的是控制组确保了当容器内的资源使用产生压力时不会连累主机系统
尽管控制组不负责隔离容器之间相互访问处理数据和进程它在防止拒绝服务DDOS攻击方面是必不可少的尤其是在多用户的平台比如公有或私有的 PaaS控制组十分重要例如当某些应用程序表现异常的时候可以保证一致地正常运行和性能
控制组机制始于 2006 内核从 2.6.24 版本开始被引入

View File

@@ -0,0 +1,19 @@
# Docker服务端的防护
运行一个容器或应用程序的核心是通过 Docker 服务端Docker 服务的运行目前需要 root 权限因此其安全性十分关键
首先确保只有可信的用户才可以访问 Docker 服务Docker 允许用户在主机和容器间共享文件夹同时不需要限制容器的访问权限这就容易让容器突破资源限制例如恶意用户启动容器的时候将主机的根目录`/`映射到容器的 `/host` 目录中那么容器理论上就可以对主机的文件系统进行任意修改了这听起来很疯狂但是事实上几乎所有虚拟化系统都允许类似的资源共享而没法禁止用户共享主机根文件系统到虚拟机系统
这将会造成很严重的安全后果因此当提供容器创建服务时例如通过一个 web 服务器要更加注意进行参数的安全检查防止恶意的用户用特定参数来创建一些破坏性的容器
为了加强对服务端的保护Docker REST API客户端用来跟服务端通信 0.5.2 之后使用本地的 Unix 套接字机制替代了原先绑定在 127.0.0.1 上的 TCP 套接字因为后者容易遭受跨站脚本攻击现在用户使用 Unix 权限检查来加强套接字的访问安全
用户仍可以利用 HTTP 提供 REST API 访问建议使用安全机制确保只有可信的网络或 VPN或证书保护机制例如受保护的 stunnel ssl 认证下的访问可以进行此外还可以使用 [ HTTPS 和证书](https://docs.docker.com/engine/security/https/) 来加强保护。
最近改进的 Linux 命名空间机制将可以实现使用非 root 用户来运行全功能的容器这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题
终极目标是改进 2 个重要的安全特性
* 将容器的 root 用户 [映射到本地主机上的非 root 用户](https://docs.docker.com/engine/security/userns-remap/),减轻容器和主机之间因权限提升而引起的安全问题;
* 允许 Docker 服务端在 [ root 权限(rootless 模式)](https://docs.docker.com/engine/security/rootless/) 下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程将只允许在限定范围内进行操作,例如仅仅负责虚拟网络设定或文件系统管理、配置操作等。
最后建议采用专用的服务器来运行 Docker 和相关的管理服务例如管理服务比如 ssh 监控和进程监控管理工具 nrpecollectd 其它的业务服务都放到容器中去运行

View File

@@ -0,0 +1,26 @@
# 内核能力机制
[能力机制Capability](https://man7.org/linux/man-pages/man7/capabilities.7.html) 是 Linux 内核一个强大的特性,可以提供细粒度的权限访问控制。
Linux 内核自 2.2 版本起就支持能力机制它将权限划分为更加细粒度的操作能力既可以作用在进程上也可以作用在文件上
例如一个 Web 服务进程只需要绑定一个低于 1024 的端口的权限并不需要 root 权限那么它只需要被授权 `net_bind_service` 能力即可此外还有很多其他的类似能力来避免进程获取 root 权限
默认情况下Docker 启动的容器被严格限制只允许使用内核的一部分能力
使用能力机制对加强 Docker 容器的安全有很多好处通常在服务器上会运行一堆需要特权权限的进程包括有 sshcronsyslogd硬件管理工具模块例如负载模块网络配置工具等等容器跟这些进程是不同的因为几乎所有的特权进程都由容器以外的支持系统来进行管理
* ssh 访问被主机上ssh服务来管理
* cron 通常应该作为用户进程执行权限交给使用它服务的应用来处理
* 日志系统可由 Docker 或第三方服务管理
* 硬件管理无关紧要容器中也就无需执行 udevd 以及类似服务
* 网络管理也都在主机上设置除非特殊需求容器不需要对网络进行配置
从上面的例子可以看出大部分情况下容器并不需要真正的 root 权限容器只需要少数的能力即可为了加强安全容器可以禁用一些没必要的权限
* 完全禁止任何 mount 操作
* 禁止直接访问本地主机的套接字
* 禁止访问一些文件系统的操作比如创建新的设备修改文件属性等
* 禁止模块加载
这样就算攻击者在容器中取得了 root 权限也不能获得本地主机的较高权限能进行的破坏也有限
默认情况下Docker采用 [白名单](https://github.com/moby/moby/blob/master/oci/caps/defaults.go) 机制,禁用必需功能之外的其它权限。
当然用户也可以根据自身需求来为 Docker 容器启用额外的权限

View File

@@ -0,0 +1,16 @@
# 内核命名空间
Docker 容器和 LXC 容器很相似所提供的安全特性也差不多当用 `docker run` 启动一个容器时在后台 Docker 为容器创建了一个独立的命名空间和控制组集合
命名空间提供了最基础也是最直接的隔离在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用
每个容器都有自己独有的网络栈意味着它们不能访问其他容器的 sockets 或接口不过如果主机系统上做了相应的设置容器可以像跟主机交互一样的和其他容器交互当指定公共端口或使用 links 来连接 2 个容器时容器就可以相互通信了可以根据配置来限制通信的策略
从网络架构的角度来看所有的容器通过本地主机的网桥接口相互通信就像物理机器通过物理交换机通信一样
那么内核中实现命名空间和私有网络的代码是否足够成熟
内核命名空间从 2.6.15 版本2008 7 月发布之后被引入数年间这些机制的可靠性在诸多大型生产系统中被实践验证
实际上命名空间的想法和设计提出的时间要更早最初是为了在内核中引入一种机制来实现 [OpenVZ](https://en.wikipedia.org/wiki/OpenVZ) 的特性。
OpenVZ 项目早在 2005 年就发布了其设计和实现都已经十分成熟

View File

@@ -0,0 +1,10 @@
# 其它安全特性
除了能力机制之外还可以利用一些现有的安全机制来增强使用 Docker 的安全性例如 TOMOYO, AppArmor, Seccomp, SELinux, GRSEC
Docker 当前默认只启用了能力机制用户可以采用多种方案来加强 Docker 主机的安全例如
* 在内核中启用 GRSEC PAX这将增加很多编译和运行时的安全检查通过地址随机化避免恶意探测等并且启用该特性不需要 Docker 进行任何配置
* 使用一些有增强安全特性的容器模板比如带 AppArmor 的模板和 Redhat SELinux 策略的模板这些模板提供了额外的安全特性
* 用户可以自定义访问控制机制来定制安全策略
跟其它添加到 Docker 容器的第三方工具一样比如网络拓扑和文件系统共享有很多类似的机制在不改变 Docker 内核情况下就可以加固现有的容器

View File

@@ -0,0 +1,5 @@
# 总结
总体来看Docker 容器还是十分安全的特别是在容器内不使用 root 权限来运行进程的话
另外用户可以使用现有工具比如 [Apparmor](https://docs.docker.com/engine/security/apparmor/), [Seccomp](https://docs.docker.com/engine/security/seccomp/), SELinux, GRSEC 来增强安全性;甚至自己在内核中实现更复杂的安全机制。