使用使用者名稱空間隔離容器
Linux 名稱空間為正在執行的程序提供了隔離,限制了它們對系統資源的訪問,而程序本身並不知道這些限制。有關 Linux 名稱空間的更多資訊,請參閱 Linux 名稱空間。
防止容器內部發生許可權提升攻擊的最佳方法是,將容器的應用程式配置為以非特權使用者身份執行。對於那些程序必須在容器內以 root
使用者身份執行的容器,您可以將此使用者重新對映到 Docker 主機上的一個許可權較低的使用者。該對映使用者被分配了一個 UID 範圍,這個範圍在名稱空間內作為從 0 到 65536 的普通 UID 來使用,但在主機本身上沒有任何許可權。
關於重對映和附屬使用者及組 ID
重對映本身由兩個檔案處理:/etc/subuid
和 /etc/subgid
。每個檔案的工作方式相同,但一個涉及使用者 ID 範圍,另一個涉及組 ID 範圍。請看 /etc/subuid
中的以下條目
testuser:231072:65536
這意味著 testuser
被分配了一個從屬使用者 ID 範圍,起始於 231072
,幷包含其後連續的 65536 個整數。UID 231072
在名稱空間內(在本例中是容器內)被對映為 UID 0
(root
)。UID 231073
被對映為 UID 1
,以此類推。如果一個程序試圖在名稱空間之外提升許可權,該程序在主機上將以一個非特權的高數值 UID 執行,這個 UID 甚至不對應任何真實使用者。這意味著該程序在主機系統上完全沒有任何許可權。
注意透過在
/etc/subuid
或/etc/subgid
檔案中為同一使用者或組新增多個不重疊的對映,可以為給定的使用者或組分配多個從屬範圍。在這種情況下,Docker 僅使用前五個對映,這符合核心對/proc/self/uid_map
和/proc/self/gid_map
中僅有五個條目的限制。
當您配置 Docker 使用 userns-remap
功能時,您可以選擇指定一個現有的使用者和/或組,或者您可以指定 default
。如果您指定 default
,則會為此目的建立並使用一個名為 dockremap
的使用者和組。
警告某些發行版不會自動將新組新增到
/etc/subuid
和/etc/subgid
檔案中。如果是這種情況,您可能需要手動編輯這些檔案並分配不重疊的範圍。這一步在先決條件中有所介紹。
非常重要的一點是,這些範圍不能重疊,這樣程序就無法在不同的名稱空間中獲得訪問許可權。在大多數 Linux 發行版中,當您新增或刪除使用者時,系統工具會為您管理這些範圍。
這種重對映對容器來說是透明的,但在容器需要訪問 Docker 主機上的資源(例如,繫結掛載到系統使用者無法寫入的檔案系統區域)的情況下,會引入一些配置複雜性。從安全形度來看,最好避免這些情況。
先決條件
從屬 UID 和 GID 範圍必須與現有使用者關聯,即使這種關聯是一個實現細節。該使用者擁有
/var/lib/docker/
下的名稱空間儲存目錄。如果您不想使用現有使用者,Docker 可以為您建立一個並使用它。如果您想使用現有的使用者名稱或使用者 ID,它必須已經存在。通常,這意味著相關條目需要存在於/etc/passwd
和/etc/group
中,但如果您使用不同的身份驗證後端,此要求可能會有所不同。要驗證這一點,請使用
id
命令$ id testuser uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)
在主機上處理名稱空間重對映的方式是使用兩個檔案:
/etc/subuid
和/etc/subgid
。這些檔案通常在您新增或刪除使用者或組時自動管理,但在某些發行版上,您可能需要手動管理這些檔案。每個檔案包含三個欄位:使用者的使用者名稱或 ID,後跟一個起始 UID 或 GID(在名稱空間內被視為 UID 或 GID 0)以及該使用者可用的最大 UID 或 GID 數量。例如,給定以下條目:
testuser:231072:65536
這意味著由
testuser
啟動的使用者名稱空間程序由主機 UID231072
(在名稱空間內看起來像 UID0
)到 296607 (231072 + 65536 - 1) 所擁有。這些範圍不應重疊,以確保名稱空間程序無法訪問彼此的名稱空間。新增使用者後,檢查
/etc/subuid
和/etc/subgid
,看您的使用者是否在每個檔案中都有一個條目。如果沒有,您需要新增它,注意避免重疊。如果您想使用由 Docker 自動建立的
dockremap
使用者,請在配置並重啟 Docker 後檢查這些檔案中是否有dockremap
條目。如果 Docker 主機上有任何位置需要非特權使用者寫入,請相應地調整這些位置的許可權。如果您想使用 Docker 自動建立的
dockremap
使用者,情況也是如此,但在配置並重啟 Docker 之後才能修改許可權。啟用
userns-remap
會有效地遮蔽/var/lib/docker/
中現有的映象和容器層以及其他 Docker 物件。這是因為 Docker 需要調整這些資源的所有權,並實際上將它們儲存在/var/lib/docker/
內的一個子目錄中。最好在一個新的 Docker 安裝上啟用此功能,而不是在現有的安裝上。同樣地,如果您停用了
userns-remap
,您將無法訪問在它啟用時建立的任何資源。請檢視使用者名稱空間的限制,以確保您的用例是可行的。
在守護程序上啟用 userns-remap
您可以使用 --userns-remap
標誌啟動 dockerd
,或者按照此過程使用 daemon.json
配置檔案來配置守護程序。推薦使用 daemon.json
方法。如果您使用該標誌,請使用以下命令作為模型
$ dockerd --userns-remap="testuser:testuser"
編輯
/etc/docker/daemon.json
。假設該檔案之前為空,以下條目使用名為testuser
的使用者和組來啟用userns-remap
。您可以透過 ID 或名稱來指定使用者和組。僅當組名或 ID 與使用者名稱或 ID 不同時,才需要指定組名或 ID。如果您同時提供使用者名稱或 ID 和組名或 ID,請用冒號 (:
) 字元分隔它們。以下格式都適用於該值,假設testuser
的 UID 和 GID 都是1001
:testuser
testuser:testuser
1001
1001:1001
testuser:1001
1001:testuser
{ "userns-remap": "testuser" }
注意要使用
dockremap
使用者並讓 Docker 為您建立它,請將值設定為default
而不是testuser
。儲存檔案並重啟 Docker。
如果您正在使用
dockremap
使用者,請使用id
命令驗證 Docker 是否已建立該使用者。$ id dockremap uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)
驗證該條目是否已新增到
/etc/subuid
和/etc/subgid
中$ grep dockremap /etc/subuid dockremap:231072:65536 $ grep dockremap /etc/subgid dockremap:231072:65536
如果這些條目不存在,請以
root
使用者身份編輯檔案,並分配一個起始 UID 和 GID,該值是已分配的最高值加上偏移量(在本例中為65536
)。請注意不要讓範圍有任何重疊。使用
docker image ls
命令驗證以前的映象是否不可用。輸出應該為空。從
hello-world
映象啟動一個容器。$ docker run hello-world
驗證
/var/lib/docker/
中是否存在一個以名稱空間使用者的 UID 和 GID 命名的名稱空間目錄,該目錄由該 UID 和 GID 擁有,並且不可被組或其他使用者讀取。一些子目錄仍然由root
擁有,並具有不同的許可權。$ sudo ls -ld /var/lib/docker/231072.231072/ drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/ $ sudo ls -l /var/lib/docker/231072.231072/ total 14 drwx------ 5 231072 231072 5 Jun 21 21:19 aufs drwx------ 3 231072 231072 3 Jun 21 21:21 containers drwx------ 3 root root 3 Jun 21 21:19 image drwxr-x--- 3 root root 3 Jun 21 21:19 network drwx------ 4 root root 4 Jun 21 21:19 plugins drwx------ 2 root root 2 Jun 21 21:19 swarm drwx------ 2 231072 231072 2 Jun 21 21:21 tmp drwx------ 2 root root 2 Jun 21 21:19 trust drwx------ 2 231072 231072 3 Jun 21 21:19 volumes
您的目錄列表可能會有一些差異,特別是如果您使用的容器儲存驅動程式不是
aufs
。由重對映使用者擁有的目錄將替代
/var/lib/docker/
下的同名目錄,而未使用的版本(例如本例中的/var/lib/docker/tmp/
)可以被移除。在啟用userns-remap
時,Docker 不會使用它們。
為容器停用名稱空間重對映
如果您在守護程序上啟用使用者名稱空間,則所有容器預設都會以啟用使用者名稱空間的方式啟動。在某些情況下,例如特權容器,您可能需要為特定容器停用使用者名稱空間。有關其中一些限制,請參閱使用者名稱空間的已知限制。
要為特定容器停用使用者名稱空間,請向 docker container create
、docker container run
或 docker container exec
命令新增 --userns=host
標誌。
使用此標誌會有一個副作用:使用者重對映不會為該容器啟用,但是,由於只讀(映象)層在容器之間共享,容器檔案系統的所有權仍將被重對映。
這意味著整個容器檔案系統將屬於 --userns-remap
守護程序配置中指定的使用者(在上面的示例中為 231072
)。這可能會導致容器內程式的意外行為。例如 sudo
(它會檢查其二進位制檔案是否屬於使用者 0
)或帶有 setuid
標誌的二進位制檔案。
使用者名稱空間的已知限制
以下標準的 Docker 功能與啟用使用者名稱空間的 Docker 守護程序不相容:
- 與主機共享 PID 或 NET 名稱空間 (
--pid=host
或--network=host
)。 - 不瞭解或無法使用守護程序使用者對映的外部(卷或儲存)驅動程式。
- 在
docker run
上使用--privileged
模式標誌,而沒有同時指定--userns=host
。
使用者名稱空間是一項高階功能,需要與其他功能協調。例如,如果從主機掛載卷,則如果需要對卷內容進行讀或寫訪問,檔案所有權必須預先安排好。
雖然使用者名稱空間容器程序內的 root 使用者擁有許多預期的超級使用者許可權,但 Linux 核心會根據其內部知識(這是一個使用者名稱空間程序)施加限制。一個顯著的限制是無法使用 mknod
命令。當由 root
使用者在容器內執行時,建立裝置的許可權將被拒絕。