> For the complete documentation index, see [llms.txt](https://philipzheng.gitbook.io/docker_practice/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://philipzheng.gitbook.io/docker_practice/security/kernel_ns.md).

# 核心命名空間

命名空間 (Namespace) 是 Linux 容器隔離的基礎，它確保了容器內的程式無法直接干擾主機或其他容器。雖然在本書第 12 章中我們已經從底層實作的角度介紹了 Namespace，但在本節中，我們將重點探討其 **安全意義** 及相關設定。

## 隔離的安全本質

Docker 守護程式在啟動容器時，會在後台為容器建立一套獨立的命名空間。命名空間提供了最基礎也是最直接的隔離：

* **PID Namespace**：防止容器內的程式查看或終止宿主機或其他容器的程式。惡意攻擊者即使在容器內取得了 root 權限，也無法透過 `kill` 命令影響宿主機上的關鍵服務。
* **NET Namespace**：每個容器都有自己獨立的網路堆疊。如果沒有明確地進行埠號映射或將容器連接到同一網路，容器之間無法網路互通，從而限制了橫向移動的能力。
* **MNT Namespace**：為容器提供獨立的檔案系統視圖。這可以防止容器不經意或惡意地修改宿主機的重要系統檔案（如 `/etc/passwd`）。

## 命名空間不是絕對安全的護城河

儘管命名空間提供了很好的隔離性，但我們必須認識到：**所有的容器依然共享同一個宿主機的 Linux 核心**。

這意味著，一旦宿主機的核心存在提權漏洞（如著名的 Dirty COW 漏洞），攻擊者有可能透過突破 Namespace 的限制，直接在核心層面執行惡意程式碼，從而實作「容器逃逸」。

> \[!WARNING] 為了緩解核心漏洞帶來的威脅，生產環境務必保持宿主機 Linux 核心的及時修補與更新，或者藉助諸如 gVisor、Kata Containers 等提供了獨立核心的安全容器技術。同時，需要及時修補容器執行期（如 runC）的漏洞。2025 年 11 月揭露的一系列 runC 容器逃逸漏洞（CVE-2025-31133、CVE-2025-52565、CVE-2025-52881）就表明，即使核心保持更新，執行期層的缺陷仍然可能導致容器隔離被突破。

透過命名空間，Docker 也能限制程式從外部環境取得資訊。 例如，由於程式環境被隔離，程式在內部通常無法直接感知外部宿主機的程式和掛載命名空間。網路命名空間隔離的是網路堆疊；預設 bridge 網路通常允許容器主動存取外部網路，限制的是外部直接存取容器服務。若要限制出站存取，需要使用 `none` 網路、防火牆、Kubernetes NetworkPolicy 或執行期策略。

## 使用者命名空間與提權防護

在所有的 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 對宿主機上的掛載目錄具有合適的讀寫權限。

## 總結

核心命名空間從 Linux 2.6.15 版本（2006 年）被引入，十餘年間，這些機制的可靠性在諸多大型生產系統中被實踐驗證。透過合理利用命名空間（尤其是 User Namespace），可以極大地收窄攻擊面，顯著提升容器部署的安全性。
