Linux中國

Podman 和用戶命名空間:天作之合

Podmanlibpod 庫的一部分,使用戶能夠管理 pod、容器容器鏡像。在我的上一篇文章中,我寫過將 Podman 作為一種更安全的運行容器的方式。在這裡,我將解釋如何使用 Podman 在單獨的用戶命名空間中運行容器。

作為分離容器的一個很棒的功能,我一直在思考 用戶命名空間 user namespace ,它主要是由 Red Hat 的 Eric Biederman 開發的。用戶命名空間允許你指定用於運行容器的用戶標識符(UID)和組標識符(GID)映射。這意味著你可以在容器內以 UID 0 運行,在容器外以 UID 100000 運行。如果容器進程逃逸出了容器,內核會將它們視為以 UID 100000 運行。不僅如此,任何未映射到用戶命名空間的 UID 所擁有的文件對象都將被視為 nobody 所擁有(UID 是 65534, 由 kernel.overflowuid 指定),並且不允許容器進程訪問,除非該對象可由「其他人」訪問(即世界可讀/可寫)。

如果你擁有一個許可權為 660 的屬主為「真實」 root 的文件,而當用戶命名空間中的容器進程嘗試讀取它時,會阻止它們訪問它,並且會將該文件視為 nobody 所擁有。

示例

以下是它是如何工作的。首先,我在 root 擁有的系統中創建一個文件。

$ sudo bash -c "echo Test > /tmp/test"
$ sudo chmod 600 /tmp/test
$ sudo ls -l /tmp/test
-rw-------. 1 root root 5 Dec 17 16:40 /tmp/test

接下來,我將該文件卷掛載到一個使用用戶命名空間映射 0:100000:5000 運行的容器中。

$ sudo podman run -ti -v /tmp/test:/tmp/test:Z --uidmap 0:100000:5000 fedora sh
# id
uid=0(root) gid=0(root) groups=0(root)
# ls -l /tmp/test
-rw-rw----. 1 nobody nobody 8 Nov 30 12:40 /tmp/test
# cat /tmp/test
cat: /tmp/test: Permission denied

上面的 --uidmap 設置告訴 Podman 在容器內映射一系列的 5000 個 UID,從容器外的 UID 100000 開始的範圍(100000-104999)映射到容器內 UID 0 開始的範圍(0-4999)。在容器內部,如果我的進程以 UID 1 運行,則它在主機上為 100001。

由於實際的 UID=0 未映射到容器中,因此 root 擁有的任何文件都將被視為 nobody 所擁有。即使容器內的進程具有 CAP_DAC_OVERRIDE 能力,也無法覆蓋此種保護。DAC_OVERRIDE 能力使得 root 的進程能夠讀/寫系統上的任何文件,即使進程不是 root 用戶擁有的,也不是全局可讀或可寫的。

用戶命名空間的功能與宿主機上的功能不同。它們是命名空間的功能。這意味著我的容器的 root 只在容器內具有功能 —— 實際上只有該範圍內的 UID 映射到內用戶命名空間。如果容器進程逃逸出了容器,則它將沒有任何非映射到用戶命名空間的 UID 之外的功能,這包括 UID=0。即使進程可能以某種方式進入另一個容器,如果容器使用不同範圍的 UID,它們也不具備這些功能。

請注意,SELinux 和其他技術還限制了容器進程破開容器時會發生的情況。

使用 podman top 來顯示用戶名字空間

我們在 podman top 中添加了一些功能,允許你檢查容器內運行的進程的用戶名,並標識它們在宿主機上的真實 UID。

讓我們首先使用我們的 UID 映射運行一個 sleep 容器。

$ sudo podman run --uidmap 0:100000:5000 -d fedora sleep 1000

現在運行 podman top

$ sudo podman top --latest user huser
USER   HUSER
root   100000

$ ps -ef | grep sleep
100000   21821 21809  0 08:04 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000

注意 podman top 報告用戶進程在容器內以 root 身份運行,但在宿主機(HUSER)上以 UID 100000 運行。此外,ps 命令確認 sleep 過程以 UID 100000 運行。

現在讓我們運行第二個容器,但這次我們將選擇一個單獨的 UID 映射,從 200000 開始。

$ sudo podman run --uidmap 0:200000:5000 -d fedora sleep 1000
$ sudo podman top --latest user huser
USER   HUSER
root   200000

$ ps -ef | grep sleep
100000   21821 21809  0 08:04 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000
200000   23644 23632  1 08:08 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000

請注意,podman top 報告第二個容器在容器內以 root 身份運行,但在宿主機上是 UID=200000。

另請參閱 ps 命令,它顯示兩個 sleep 進程都在運行:一個為 100000,另一個為 200000。

這意味著在單獨的用戶命名空間內運行容器可以在進程之間進行傳統的 UID 分離,而這從一開始就是 Linux/Unix 的標準安全工具。

用戶名字空間的問題

幾年來,我一直主張用戶命名空間應該作為每個人應該有的安全工具,但幾乎沒有人使用過。原因是沒有任何文件系統支持,也沒有一個 移動文件系統 shifting file system

在容器中,你希望在許多容器之間共享基本鏡像。上面的每個示例中使用了 Fedora 基本鏡像。Fedora 鏡像中的大多數文件都由真實的 UID=0 擁有。如果我在此鏡像上使用用戶名稱空間 0:100000:5000 運行容器,默認情況下它會將所有這些文件視為 nobody 所擁有,因此我們需要移動所有這些 UID 以匹配用戶名稱空間。多年來,我想要一個掛載選項來告訴內核重新映射這些文件 UID 以匹配用戶命名空間。上游內核存儲開發人員還在繼續研究,在此功能上已經取得一些進展,但這是一個難題。

由於由 Nalin Dahyabhai 領導的團隊開發的自動 chown 內置於容器/存儲中,Podman 可以在同一鏡像上使用不同的用戶名稱空間。當 Podman 使用容器/存儲,並且 Podman 在新的用戶命名空間中首次使用一個容器鏡像時,容器/存儲會 「chown」(如,更改所有權)鏡像中的所有文件到用戶命名空間中映射的 UID 並創建一個新鏡像。可以把它想像成一個 fedora:0:100000:5000 鏡像。

當 Podman 在具有相同 UID 映射的鏡像上運行另一個容器時,它使用「預先 chown」的鏡像。當我在0:200000:5000 上運行第二個容器時,容器/存儲會創建第二個鏡像,我們稱之為 fedora:0:200000:5000

請注意,如果你正在執行 podman buildpodman commit 並將新創建的鏡像推送到容器註冊庫,Podman 將使用容器/存儲來反轉該移動,並將推送所有文件屬主變回真實 UID=0 的鏡像。

這可能會導致在新的 UID 映射中創建容器時出現真正的減速,因為 chown 可能會很慢,具體取決於鏡像中的文件數。此外,在普通的 OverlayFS 上,鏡像中的每個文件都會被複制。普通的 Fedora 鏡像最多可能需要 30 秒才能完成 chown 並啟動容器。

幸運的是,Red Hat 內核存儲團隊(主要是 Vivek Goyal 和 Miklos Szeredi)在內核 4.19 中為 OverlayFS 添加了一項新功能。該功能稱為「僅複製元數據」。如果使用 metacopy=on 選項來掛載層疊文件系統,則在更改文件屬性時,它不會複製較低層的內容;內核會創建新的 inode,其中包含引用指向較低級別數據的屬性。如果內容發生變化,它仍會複製內容。如果你想試用它,可以在 Red Hat Enterprise Linux 8 Beta 中使用此功能。

這意味著容器 chown 可能在兩秒鐘內發生,並且你不會倍增每個容器的存儲空間。

這使得像 Podman 這樣的工具在不同的用戶命名空間中運行容器是可行的,大大提高了系統的安全性。

前瞻

我想向 Podman 添加一個新選項,比如 --userns=auto,它會為你運行的每個容器自動選擇一個唯一的用戶命名空間。這類似於 SELinux 與單獨的多類別安全(MCS)標籤一起使用的方式。如果設置環境變數 PODMAN_USERNS=auto,則甚至不需要設置該選項。

Podman 最終允許用戶在不同的用戶名稱空間中運行容器。像 BuildahCRI-O 這樣的工具也可以利用用戶命名空間。但是,對於 CRI-O,Kubernetes 需要了解哪個用戶命名空間將運行容器引擎,上游正在開發這個功能。

在我的下一篇文章中,我將解釋如何在用戶命名空間中將 Podman 作為非 root 用戶運行。

via: https://opensource.com/article/18/12/podman-and-user-namespaces

作者:Daniel J Walsh 選題:lujun9972 譯者:wxy 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國