Add more content and fix format

This commit is contained in:
Baohua Yang
2026-02-25 21:06:21 -08:00
parent dd449bc84f
commit ecab788013
119 changed files with 566 additions and 496 deletions

View File

@@ -1,18 +1,85 @@
## 18.1 内核命名空间
命名空间 (Namespace) Linux 容器隔离的基础它确保了容器内的进程无法干扰主机或其他容器
命名空间 (Namespace) Linux 容器隔离的基础它确保了容器内的进程无法直接干扰主机或其他容器虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace但在本节中我们将重点探讨其**安全意义**及相关配置
Docker 容器和 LXC 容器很相似所提供的安全特性也差不多当用 `docker run` 启动一个容器时在后台 Docker 为容器创建了一个独立的命名空间和控制组集合
### 18.1.1 隔离的安全本质
命名空间提供了最基础也是最直接的隔离在容器中运行的进程不会被运行在主机上的进程和其它容器发现和作用
Docker 守护进程在启动容器时会在后台为容器创建一套独立的命名空间命名空间提供了最基础也是最直接的隔离
每个容器都有自己独有的网络栈意味着它们不能访问其他容器的 sockets 或接口不过如果主机系统上做了相应的设置容器可以像跟主机交互一样的和其他容器交互当指定公共端口或使用 links 来连接 2 个容器时容器就可以相互通信了 (可以根据配置来限制通信的策略)
- **PID Namespace**防止容器内的进程查看或终止宿主机或其他容器的进程恶意攻击者即使在容器内获得了 root 权限也无法通过 `kill` 命令影响宿主机上的关键服务
- **NET Namespace**每个容器都有自己独立的网络栈如果没有显式地进行端口映射或将容器连接到同一网络容器之间无法网络互通从而限制了横向移动的能力
- **MNT Namespace**为容器提供独立的文件系统视图这可以防止容器不经意或恶意地修改宿主机的重要系统文件 `/etc/passwd`
从网络架构的角度来看所有的容器通过本地主机的网桥接口相互通信就像物理机器通过物理交换机通信一样
### 18.1.2 命名空间不是绝对安全的护城河
那么内核中实现命名空间和私有网络的代码是否足够成熟
尽管命名空间提供了很好的隔离性但我们必须认识到**所有的容器依然共享同一个宿主机的 Linux 内核**
内核命名空间从 2.6.15 版本 (2006 1 月发布) 之后被引入数年间这些机制的可靠性在诸多大型生产系统中被实践验证
这意味着一旦宿主机的内核存在提权漏洞如著名的 Dirty COW 漏洞攻击者有可能通过突破 Namespace 的限制直接在内核层面执行恶意代码从而实现容器逃逸
实际上命名空间的想法和设计提出的时间要更早最初是为了在内核中引入一种机制来实现 [OpenVZ](https://en.wikipedia.org/wiki/OpenVZ) 的特性。
OpenVZ 项目早在 2005 年就发布了其设计和实现都已经十分成熟
> [!WARNING]
> 为了缓解内核漏洞带来的威胁生产环境务必保持宿主机 Linux 内核的及时修补与更新或者借助诸如 gVisorKata Containers 等提供了独立内核的安全容器技术
通过命名空间Docker 也能限制进程从外部环境获取信息
例如由于进程环境被隔离进程在内部其实是无法感知到外部宿主机的存在的它既不能获取其他容器的进程列表也无法通过网络与其他系统进行交互除非经过配置
### 18.1.1 用户命名空间 与提权防护
在所有的 Namespace **User Namespace** 对安全的影响尤为关键
在默认情况下容器内的 `root` 用户UID=0就是宿主机上的 `root` 用户如果攻击者设法突破了容器的其他隔离机制获取了宿主机的访问权限他将拥有宿主机的最高系统权限
通过启用 **User Namespace Remapping (用户命名空间映射)**我们可以将容器内的 `root` 用户映射到宿主机上的一个无特权普通用户
#### 如何配置 User Namespace
要在 Docker 服务端启用这一特性需要修改 Docker 的配置文件 `/etc/docker/daemon.json`
1. **设置映射策略**
编辑配置文件添加 `userns-remap` 配置项
```json
{
"userns-remap": "default"
}
```
使用 `default` 值时Docker 会自动在宿主机上创建一个名为 `dockremap` 的用户和用户组
2. **验证子 UID 和子 GID 分配**
Docker 会通过 `/etc/subuid` `/etc/subgid` 文件为 `dockremap` 分配一个高位的 UID 范围
```bash
$ cat /etc/subuid
dockremap:165536:65536
```
这意味着容器内的 UID `0`root 用户在宿主机上实际被映射成了 UID `165536`如果是容器内的 UID `1`对应宿主机的 `165537`以此类推
3. **重启 Docker 守护进程**
```bash
$ sudo systemctl restart docker
```
#### 验证映射效果
我们可以运行一个简单的容器并执行 `sleep` 命令同时在宿主机上观察进程的所有者
```bash
## 在容器内以 root 身份运行
$ docker run -d --name userns_test alpine sleep 3600
## 在宿主机上查看该 sleep 进程
$ ps aux | grep sleep
165536 12345 0.0 0.0 1568 4 ? Ss 14:20 0:00 sleep 3600
```
你会发现尽管在容器内该进程是由 root 启动的但在宿主机上它的属主是 `165536`一个完全没有特权的用户
> [!TIP]
> 启用 User Namespace 会对容器共享宿主机数据卷Bind Mount产生权限影响你需要确保映射后的高位 UID 对宿主机上的挂载目录具有合适的读写权限
### 18.1.4 总结
内核命名空间从 Linux 2.6.15 版本 (2006 ) 被引入十余年间这些机制的可靠性在诸多大型生产系统中被实践验证通过合理利用命名空间尤其是 User Namespace可以极大地收窄攻击面显著提升容器部署的安全性