為何在 Docker 中執行特權容器不是個好主意?

所謂的特權容器 (privileged container),簡而言之就是 Docker 環境中擁有主機完整系統權限 (root) 的容器,它可存取一般容器無法存取的資源。特權容器的用途之一,就是在 Docker 容器當中執行另一個 Docker daemon;另一個用途就是當容器需要對硬體進行直接存取時。原本,在 Docker 中執行 Docker (也就是所謂的 Docker-in-Docker) 的用意是為了開發 Docker 本身。但今天,特權容器已經出現各種不同的使用情境,例如在開放原始碼自動化伺服器 Jenkins 當中將持續整合/持續交付 (CI/CD) 工作自動化。然而,執行特權容器卻可能帶來安全上的疑慮。以下我們將詳細說明,執行一個擁有特權但卻不安全的容器,如何讓網路犯罪集團有機會在企業的系統當中植入後門。

特權容器的問題

一般來說,使用 Docker-in-Docker 是為了在現有容器當中再執行另一個容器。不過,使用一個缺乏安全的特權容器,可能會引發一些嚴重問題。

執行一個擁有特權的容器,固然可讓內部團隊存取主機的一些重要資源,但如果這項機制遭到濫用,很可能讓網路犯罪集團也同樣能取得這些資源。當駭客在攻擊當中使用了一個特權容器時,他們不一定會從遠端執行程式碼。但萬一真的執行,其潛在的影響層面將相當深遠。使用特權容器的目的,通常是為了提高容器的權限,但這樣做會讓駭客有很大機會以系統管理員 (root) 身分來執行程式碼。換句話說,駭客將取得主機的系統管理權限,並具備所有能力,例如 CAP_SYS_ADMIN 這樣的系統能力。值得一提的是,一些其他的隔離機制,例如:cgroups、AppArmor、SECcomp 也都會被拋棄或停用。

一旦特權容器暴露在外並遭到駭客入侵,其後果真是難以想像。駭客可查看主機上所執行的軟體,然後尋找並攻擊可用的漏洞。此外,還可利用容器軟體本身的漏洞與組態設定錯誤,例如:容器的登入密碼太弱,或者根本不需安全認證。由於駭客擁有系統管理員權限,其惡意程式碼或挖礦程式就能在背後暗中執行。

將容器隔離

一個容器基本上就是一個隔離環境,內含應用程式所需的基本元件。容器引擎為了要讓一台主機上的不同執行程序能互相隔離,必須用到許多系統核心功能。既然 Docker 容器是在 Linux 環境上執行,所以就會利用 Linux 核心的一些資源隔離功能來讓容器彼此獨立,其中一個功能就是 Linux 的命名空間 (Namespace)。表 1 顯示各種不同的命名空間。

命名空間 用途
MNT (Mount) 管理檔案系統的掛載點 (mount point)。
PID (Process) 隔離執行程序。
NET (Network) 管理網路介面。
IPC (Inter-process communication) 管理執行程序之間的通訊資源。
UTS (Host name) 隔離系統核心版本與識別碼。
CGROUPS 限制、隔離、累計各執行程序的資源用量。
User ID (User) 提供權限隔離與使用者身分區隔。

表 1:Linux 的各種命名空間。

在預設情況下,Docker daemon (同樣也一個容器執行程序) 會以 root 權限執行。但也可以建立一個使用其他使用者來執行並限縮其權限的容器,而且從安全的角度來看,這是相當推薦的作法。

在特權容器的情況中,在容器內擁有 root 存取權限,也等於對主機擁有 root存取權限。但值得注意的是,在預設情況下,容器執行程序的能力是有限的,但特權容器卻具備所有能力 (如表2)。

能力 允許的操作
CAP_AUDIT_WRITE 將資料寫入系統核心的稽核記錄檔。
CAP_CHOWN (Change owner) 對檔案的 UID 和 GID 做任意修改;變更檔案、目錄與連結的擁有者與所屬群組。
CAP_DAC_OVERRIDE (Discretionary access control) 免除檔案讀、寫、執行時的權限檢查。
CAP_FOWNER 免除一般操作時必須確認檔案系統 UID 與執行程序 UID 一致的權限檢查,免除 CAP_DAC_OVERRIDE 和 CAP_DAC_READ_SEARCH 所要求的檢查。
CAP_FSETID 即使檔案受到變更,也不會清除其模式的 set-user-ID 和 set-group-ID 位元。
CAP_KILL 免除發送系統訊號 (Signal) 時的權限檢查。
CAP_MKNOD 建立特殊檔案。
CAP_NET_BIND_SERVICE 將某個網路 Socket 綁定至網際網路網特權連接埠 (編號 1024 以下的連接埠)。
CAP_NET_RAW 使用 RAW Socket 和 PACKET Socket,並綁定至任意位址以執行通透式代理 (Transparent Proxying)。
CAP_SETGID   對執行程序的 GID 與附加 GID (Supplementary GID) 清單做任意變動。
CAP_SETPCAP 若系統核心支援檔案能力 ( Linux 2.6.24 開始的系統核心):將呼叫方執行緒邊界集合 (bounding set) 中的任何能力加入可繼承集合 (inheritable set);拋棄邊界集合當中的能力;修改安全位元 (secure bits) 旗標。  若系統核心「不」支援檔案能力 (Linux 2.6.24 之前的系統核心):將呼叫方准許能力集合 (permitted capability set) 中的任何能力授予給任何其他執行程序,或從任何其他執行程序移除。
CAP_SETUID   對執行程序的 UID 做任意變動;當透過 UNIX 網域 socket 傳遞 socket 憑證時產生 UID;在 User 命名空間內寫入一筆 User ID 對應資料。
CAP_SYS_CHROOT 使用 chroot,並使用 setns 來變更命名空間。

表 2:以 root 身分執行的容器所具備的能力。

為了提升安全性,Docker 提供了一個選項讓您用非 root 身分執行容器,只要在 Dockerfile 當中指定「USER」選項即可。值得注意的是,Docker 在預設情況下並不會使用 User 命名空間,而命名空間可讓主機的 root 與容器的 root 分離。User 命名空間可在 Docker daemon 當中指定,此功能也可用於許多原本需要 root 存取權限的情況。請看圖 1,並請特別留意中括號 [ ] 內的數字。

圖 1:截圖顯示 Docker 在預設情況下不會使用 User 命名空間。

因此,除非直接透過「–userns-remap」旗標來指定,否則 Docker 在預設情況下不會使用 User 命名空間。

在 User 命名空間內,執行程序可被授予完整的操作權限,但在 User 命名空間之外則不行。這表示,在 User 命名空間外部,執行程序需使用一個無特權的 User ID,但在內部,其 User ID 則可以是「0」 (也就是系統管理員)。

換句話說,即使執行程序是在一個新的、提供了 CAP_SYS_ADMIN 能力的 User 命名空間內執行,且所要執行的動作需要較高的權限 (例如安裝一個系統核心模組),那麼其母 User 命名空間 (Parent User Namespace) 也會被檢查是否具備所需的權限 (雖然它不是透過 root 執行,也不具備所需的能力)。如果找不到權限,整個動作就會被駁回。

值得注意的一點是,雖然「–userns-remap」可提升安全性,但它和 Rootless Docker 還是有所差別 (該功能截至本文撰寫為止仍是一項實驗性功能)。由於 Docker daemon 是一個母容器執行程序,因此還是在 root 權限底下執行。

駭客如何利用特權容器?

由於特權容器具備了上述能力,因此駭客會試圖啟動這類容器來取得使用者主機環境的 root 存取權限。

最近,從我們一些誘捕環境當中看到的活動顯示,駭客正試圖透過他們啟動的特權容器來將其自身的 SSH 公開金鑰放置到主機的「/root/authorized_keys」當中。

圖 2:截圖顯示駭客啟動特權容器的程式碼。

經過進一步詳細分析之後,我們發現駭客啟動的容器嘗試將「/mnt」綁定至主機的根目錄「/」。隨後,我們也觀察到駭客執行了下列指令:

  • “Cmd”:[“sh”,”-c”,”mkdir -pv /mnt/root/; mkdir -pv /mnt/root/.ssh/; ls -ld /mnt/root/.ssh/; chattr -i -a /mnt/root/.ssh/ /mnt/root/.ssh/authorized_keys”]
  • 將不可修改 (immutable) 的屬性移除,然後加入來自 /mnt/root/.ssh /mnt/root/.ssh/authorized_keys 的屬性。

圖 3:截圖顯示駭客啟動的特權容器所執行的指令。

請注意,雖然圖 3 我們有看到「Privileged: false」這項設定,但因為新的執行程序是在特權容器環境下執行,因此其能力會與特權容器相同。根據我們的分析,將「/」綁定至「/mnt/root」,效果等同於 Docker 內的「-v /:/mnt/root」指令,如此一來駭客就能存取主機的檔案系統。

此外,駭客也可以嘗試在 SSH 當中覆寫「authorized_keys」,如圖 4 當中的 API 請求所示。

圖 4:截圖顯示駭客試圖覆寫「authorized_keys」。

從這些例子可以明顯看出,儘管存在著一些內部隔離機制,但仍有一些狀況可能會讓網路犯罪集團跳出被隔離的容器來存取主機資源,如此一來,使用者的基礎架構就可能遭到攻擊。

Docker 的「–privileged」旗標基本上會讓所有隔離功能停用。不同的容器或許有不同的 PID 和 MNT 命名空間,並套用了不同的 cgroups 設定檔 (profile)。但如果在執行 Docker 容器時使用了「–privileged」旗標,使用者 (以及駭客) 就能夠存取主機所連接的硬碟。

這個「–privileged」旗標,再加上 root 存取權限,駭客就有許多方式可以逃出隔離的「牢籠」:

  • 掛載「/dev/sda1」或類似作法,就能存取主機的儲存裝置。
    • ls –la /dev/
    • mkdir /hdd & mount /dev/sda1 /hdd
  • 使用 cgroups notify_on_releaserelease_agent 在主機的 root 內產生一個指令列介面 (shell)。
  • 部署一個讓駭客可長期潛伏在主機內的客製化核心模組,如:反向指令列介面 (reverse shell)。

訂閱資安趨勢電子報

有關 Docker 特權容器的資安建議

有趣的是,Tõnis Tiigi 提出了一種實驗性的 Rootless Docker 模式 (免 root模式),目的是要讓使用者無需 root 權限就能執行 Docker daemon。透過這種免 root 模式,就算網路犯罪集團可以滲透 Docker daemon 和容器,但他們仍無法取得主機的 root 存取權限。

目前免 root 模式仍在開發初期階段,尚不支援完整的 Docker 功能。以下是目前不支援的功能:

  • Cgroups (包含需依賴 cgroups 的 docker top)。
  • AppArmor
  • 檢查點 (Checkpoint)
  • 覆蓋網路 (Overlay Network)
  • 發布 SCTP 連接埠

不過,免 root 模式對於許多不會用到上述功能的使用情境來說應該已經足夠,包括 Jenkins 中的建構工作。

對於需要隨時跟上並滿足業務最新需求的企業來說,容器有很大幫助。但隨著採用容器的企業越來越多,想要藉由這類環境的資安漏洞來發財的網路犯罪集團也會越來越多。

雖然特權容器的確有其適用情境,但開發人員在使用時應特別小心謹慎,並盡可能少用。畢竟,特權容器很可能成為駭客入侵的管道,讓惡意程式擴散到主機上。

不過,這並不是說絕對不能使用特權容器。只是企業要在自己的環境中使用這類容器之前,務必先做好適當的防護措施。

以下是針對使用特權容器的一些資安建議:

  1. 採取最低授權原則,能不開放的權限就不要開放。Docker 引擎的重要元件,如輔助容器運作的核心服務,應該受到嚴格的存取管制。此外,網路連線也應加密。
  2. 透過組態設定讓容器只開放給信賴的對象存取,例如內部網路。這包括建置適當的認證程序來保護容器本身。
  3. 落實廠商建議的最佳實務原則。Docker 提供了一份完整的最佳實務原則清單以及許多專業人員可善加利用的內建資安功,例如透過後續安裝 (post-installation) 來設定 Linux 主機,讓 Docker 運作更順暢。
  4. 謹慎評估存取需求:使用情境是否絕對必須在 Docker 內執行?是否有其他不需 root 存取權限來執行的容器引擎也能有效滿足同樣的需求?是否有其他不同的作法?您是否能夠接受這項需求的相關風險?
  5. 定期執行資安稽核來檢查是否有任何可疑的容器和映像。

此外,趨勢科技也提供了一些解決方案來協助 DevOps 團隊建構安全、快速交付、隨處執行。趨勢科技  Hybrid Cloud Security  混合雲防護解決方案可提供強大、簡化、自動化的防護,讓企業將防護融入  DevOps 流程,並藉由多重的 XGen™ 威脅防禦技巧來保障實體、虛擬及雲端工作負載的安全。

此外,還可透過趨勢科技 Deep Security™ 解決方案來加入容器防護,其中的容器控管 (Container Control) 功能可讓使用者根據自己想要的組態設定參數來執行容器。此外,還有  Deep Security Smart Check 的容器映像掃瞄,可在開發流程當中持續定期掃瞄映像內的惡意程式和漏洞,在部署之前預先防範這些威脅。

原文出處:Why Running a Privileged Container in Docker Is a Bad Idea 作者:David Fiser 與 Alfredo Oliveira