從 Day 12 到 Day 17,我們把 Docker 容器的安全邊界基本介紹過了一遍:搞懂容器到底隔離了什麼,看它是怎麼獲得 host root。今天回頭把這六天整理成一張地圖,看看這些攻擊為什麼成立,以及可以從中學到哪些防禦觀念。
| Day | 主題 | 跨過/理解的邊界 | 成因關鍵 |
|---|---|---|---|
| Day 12 | Docker 隔離了什麼 | 建立基準:容器=共享 kernel 的程序隔離,邊界由 namespace+capabilities+cgroups 構成 | 先看清楚「牆是用什麼砌的」 |
| Day 13 | 拿到 shell 後找什麼 | — | 落地後先枚舉:我在哪、是誰、碰得到什麼 |
| Day 14 | docker.sock | Docker daemon 的控制面 | socket 可存取+daemon 以 root 執行 |
| Day 15 | Bind Mount | mount namespace(檔案系統邊界) | 掛載範圍過大+可寫+無 user namespace |
| Day 16 | privileged | capabilities+device+seccomp 一次全開 | 一個參數掀掉整疊防線 |
| Day 17 | 單一 capability | 單一能力+PID namespace(--pid=host) |
每個 cap 都是獨立攻擊面 |
可以看出,容器逃逸不是只有一種破法:有的是打控制面,有的是打檔案系統邊界,有的是把 root 整碗端回來,有的則是只多拿一個能力。但它們底下其實共用了幾個成因。
這是 Day 14、15、16 都成立的隱藏前提。因為多數容器沒有開 user namespace,容器裡的 uid=0 對映到的就是 host 的 uid=0。所以一旦你能寫到 host 的檔案(Bind Mount)、或能叫 daemon 幫你做事(docker.sock),那個「root」是貨真價實的 host root,不是被關在容器裡的假 root。
Day 12 就講過:容器是共享 kernel 的程序隔離。這代表只要拿到 kernel 層的能力(例如 Day 16 的 privileged、Day 17 的某些 cap),就有機會直接作用到整台主機——因為那個 kernel 本來就是 host 的。VM 有獨立 kernel,容器沒有,這是兩者安全模型最根本的差別。
把成因講白了,其實很接近 Day 11 對 Linux 提權的結論——授權範圍過大:
/ 可寫掛進來。--privileged。CAP_SYS_PTRACE 又配 --pid=host。docker.sock 掛進去。每一個單獨看都「有它的用途」,但只要超出實際需要,就變成攻擊面。
Day 13 其實是這整條線的基礎。上面那些邊界再怎麼脆弱,你得先枚舉到它們才有辦法開始計畫滲透。而且 Day 13 也強調過:沒有任何單一指標能百分之百確定你在容器還是主機(/.dockerenv、/proc/1/cgroup、mount… 都只是線索),一定要多方交叉驗證。
這六天是一天拆一個單點,但真實攻擊往往是把幾個不算致命的錯誤設定串起來。舉例來說:就算一個容器沒有 privileged、沒有 docker.sock、也沒有多的 cap,只要它可寫掛了一個會被 host root 排程執行的目錄,攻擊者一樣能透過「枚舉→ 可寫掛載→ host root 排程」串成一條通往 host root 的路。
總結一句話:
防守不能只檢查「有沒有 privileged、有沒有掛 docker.sock」這種明顯項,要問的是「這個容器碰得到的每一樣東西,串起來會怎樣」。
好消息是,上面的成因大多可以用同一組原則收斂掉:
| 面向 | 做法 |
|---|---|
| 掛載 | 最小化、盡量 :ro;/、/etc、/root、/var/run 等敏感路徑絕不掛進容器(Day 15) |
| 控制面 | 不要把 docker.sock 掛進容器;真要給,用受限的代理介面(Day 14) |
| 權限 | 預設 --cap-drop=ALL,只加回真正需要的那一個;不要 --privileged(Day 16/17) |
| 身分 | 開 user namespace remap,讓容器 root 對映 host 非 root,斷開「容器 root = host root」 |
| 命名空間 | 避免 --pid=host、--net=host 這類鬆綁(Day 17) |
| 審查/監控 | 用 docker inspect 檢查 Mounts、CapAdd、Privileged、PidMode,在部署前就擋下來(Day 29 會再展開) |
核心原則只有一條:最小權限。給容器它真正需要的,而不是「先全開比較省事」。
這六天從「容器隔離了什麼」一路走到「怎麼從容器逃到 host」,我自己整理下來最受用的三點是:
容器的內容就先到這裡告一段落,接下來這些觀念會延伸到 Kubernetes 。
我們會在明天先介紹一下甚麼是 Kubernetes ? 具體的架構有哪些? 接著再做 Kubernetes 常見的錯誤設定造成的資安風險。所以如果讀完這篇還不懂 Kubernetes 的讀者可以不用擔心!
謝謝大家的收看,我們明天見!