Files
docker_practice/18_security/18.1_kernel_ns.md
2026-03-09 20:04:13 -07:00

86 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 18.1 内核命名空间
命名空间 (Namespace) Linux 容器隔离的基础它确保了容器内的进程无法直接干扰主机或其他容器虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace但在本节中我们将重点探讨其 **安全意义** 及相关配置
### 18.1.1 隔离的安全本质
Docker 守护进程在启动容器时会在后台为容器创建一套独立的命名空间命名空间提供了最基础也是最直接的隔离
- **PID Namespace**防止容器内的进程查看或终止宿主机或其他容器的进程恶意攻击者即使在容器内获得了 root 权限也无法通过 `kill` 命令影响宿主机上的关键服务
- **NET Namespace**每个容器都有自己独立的网络栈如果没有显式地进行端口映射或将容器连接到同一网络容器之间无法网络互通从而限制了横向移动的能力
- **MNT Namespace**为容器提供独立的文件系统视图这可以防止容器不经意或恶意地修改宿主机的重要系统文件 `/etc/passwd`
### 18.1.2 命名空间不是绝对安全的护城河
尽管命名空间提供了很好的隔离性但我们必须认识到**所有的容器依然共享同一个宿主机的 Linux 内核**
这意味着一旦宿主机的内核存在提权漏洞如著名的 Dirty COW 漏洞攻击者有可能通过突破 Namespace 的限制直接在内核层面执行恶意代码从而实现容器逃逸
> [!WARNING]
> 为了缓解内核漏洞带来的威胁生产环境务必保持宿主机 Linux 内核的及时修补与更新或者借助诸如 gVisorKata Containers 等提供了独立内核的安全容器技术
通过命名空间Docker 也能限制进程从外部环境获取信息
例如由于进程环境被隔离进程在内部其实是无法感知到外部宿主机的存在的它既不能获取其他容器的进程列表也无法通过网络与其他系统进行交互除非经过配置
### 18.1.3 用户命名空间与提权防护
在所有的 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可以极大地收窄攻击面显著提升容器部署的安全性