為什麼 Docker 容器中的進程不應該以 root 身份運行

已發表: 2022-08-19

顯示 Docker 徽標的圖形

Docker 容器中的進程不應以 root 身份運行。 以您指定為 Dockerfile 的一部分或使用 docker docker run的非 root 用戶身份運行應用程序會更安全。 這通過減少對容器中任何威脅的攻擊面來最大限度地降低風險。

在本文中,您將了解以 root 身份運行容器化應用程序的危險。 您還將看到如何創建一個非 root 用戶並在不可能的情況下設置命名空間。

為什麼以 root 身份運行是危險的?

默認情況下,容器以 root 身份運行。 Docker 守護程序在您的主機上以 root 身份執行,並且運行的容器也將以 root 身份運行。

雖然看起來容器內的 root 是一個獨立的用戶,但實際上它與您主機上的 root 帳戶相同。 分離僅由 Docker 的容器隔離機制提供。 沒有強大的物理邊界; 您的容器的另一個進程由您的主機內核上的 root 用戶運行。 這意味著您的應用程序、Docker 運行時或 Linux 內核中的漏洞可能允許攻擊者突破容器並在您的計算機上執行 root 特權操作。

有一些內置的保護措施可以降低發生這種情況的風險。 容器內的根是非特權的並且具有受限的能力。 這會阻止容器使用系統管理命令,除非您在啟動容器時手動添加功能或使用特權模式。

儘管有這種緩解措施,但允許應用程序以 root 身份運行仍然存在危險。 就像您在傳統環境中限制使用 root 一樣,在容器中不必要地使用它是不明智的。 您正在提供一個特權過高的環境,以便在發生違規事件時為攻擊者提供更多立足點。

以非 root 用戶身份運行容器化應用程序

容器化應用程序以普通用戶身份運行是最佳實踐。 大多數軟件不需要 root 訪問權限,因此更改用戶提供了針對容器突破的即時防禦層。

作為 Dockerfile 的最後階段之一,您應該創建一個新的用戶帳戶。 您可以使用USER指令實現此目的:

 來自基本映像:最新
運行 apt install 演示包
用戶演示用戶:演示組
入口點 [“演示二進制”]

從此鏡像啟動的容器將以demo-user身份運行。 用戶將是demo-group組的成員。 如果您不需要用戶在組中,則可以省略組名:

 USER 演示用戶

您可以指定用戶 ID (UID) 和組 ID (GID) 而不是名稱:

 用戶 950:950

分配已知的 UID 和 GID 通常是最安全的方法。 它可以防止容器中的用戶被映射到特權過高的主機帳戶。

USER通常被指定為 Dockerfile 中的倒數第二個階段。 這意味著您仍然可以在映像構建的早期運行需要 root 的操作。 上面示例中的apt install指令對 root 有合法的需求。 如果USER指令放在它上面, apt將作為缺少必要權限的demo-user運行。 由於 Dockerfile 說明僅適用於映像構建,而不適用於運行容器,因此在 Dockerfile 中稍後更改用戶是安全的。

更改您的容器運行的用戶可能需要您更新它訪問的文件和文件夾的權限。 在您的應用程序將使用的任何路徑上設置所有權:

 複製初始配置.yaml /data/config.yaml

用戶演示用戶:演示組
運行 chown demo-user:demo-group /data

在此示例中, /data目錄需要由demo-user擁有,以便應用程序可以更改其配置文件。 較早的COPY語句將以 root 身份複製文件。 使用帶有copy--chown標誌可以使用速記:

 複製 --chown=demo-user:demo-group initial-config.yaml /data/config.yaml

啟動容器時更改用戶

雖然您可以在自己的 Dockerfile 中輕鬆更改用戶,但許多第三方應用程序仍以 root 身份運行。 您可以通過在每次調用docker run時設置--user標誌來降低與使用這些相關的風險。 這將覆蓋圖像的 Dockerfile 中的用戶集。

 $ docker run -d --user demo-user:demo-group demo-image:latest
$ docker run -d --user demo-user demo-image:latest
$ docker run -d --user 950:950 demo-image:latest

--user標誌以指定用戶身份運行容器的進程。 它不如 Dockerfile USER指令安全,因為您必須將它單獨應用於每個docker run命令。 對於經常使用的圖像,一個更好的選擇是創建自己的衍生圖像,可以設置一個新的用戶帳戶:

 FROM 以 root 身份運行的映像:最新
USER 演示用戶
$碼頭工人建造。 -t image-that-now-runs-as-non-root:latest

更改第三方映像的用戶可能會導致問題:如果容器希望以 root 身份運行,或者需要訪問 root 擁有的文件系統路徑,則在使用應用程序時會看到錯誤。 您可以嘗試手動更改導致問題的路徑的權限。 或者,檢查供應商是否支持使用非特權用戶帳戶運行應用程序的方法。

處理必須以 root 身份運行的應用程序

用戶命名空間是一種處理需要一些 root 權限的應用程序的技術。 它允許您將容器內的 root 映射到主機上的非 root 用戶。 容器內的模擬 root 具有它需要的權限,但突破不會提供對主機的 root 訪問權限。

通過向/etc/docker/daemon.json文件添加userns-remap字段來激活命名空間重新映射:

 {
    “用戶重新映射”:“默認”
}

使用default作為userns-remap的值會指示 Docker 在您的主機上自動創建一個名為dockremap的新用戶。 容器中的根將映射回主機上的dockremap 。 您可以選擇使用 UID/GID 或用戶名/組名稱組合指定現有用戶和組:

 {
    “用戶重新映射”:“演示用戶”
}

應用更改後重新啟動 Docker 守護程序:

 $ sudo 服務碼頭重新啟動

如果您使用的是nsuser-remap: default ,則dockremap用戶現在應該存在於您的主機上:

 $ id 碼頭地圖

uid=140(dockremap) gid=119(dockremap) 組=119(dockremap)

用戶還應該出現在/etc/subuid/etc/subgid從屬 ID 文件中:

 $碼頭重映射:231500:65535

從 231500 開始,該用戶被分配了 65,535 個從屬 ID。在用戶命名空間中,ID 231500映射到0 ,使其成為容器中的 root 用戶。 作為一個高編號的 UID,231500 對主機沒有特權,因此容器突圍攻擊將無法造成如此大的破壞。

除非您使用docker run --userns=host選擇退出,否則您啟動的所有容器都將使用重新映射的用戶命名空間運行。 該機制通過在/var/lib/docker內創建由命名空間用戶的從屬 UID 和 GID 擁有的命名空間目錄來工作:

 $ sudo ls -l /var/lib/docker/231500.231500

共 14 個
drwx------ 5 231500 231500 13 Jul 22 19:00 aufs
drwx------ 3 231500 231500 13 Jul 22 19:00 容器
...

用戶命名空間是增加容器隔離、避免突破和保持與需要 root 權限的應用程序兼容性的有效方法。 不過有一些權衡:該功能在新的 Docker 實例上效果最好,從主機安裝的捲必須調整其權限,並且一些外部存儲驅動程序根本不支持用戶映射。 在採用此選項之前,您應該查看文檔。

概括

以 root 身份運行容器化應用程序存在安全風險。 雖然容易被忽視,但容器提供的隔離還不足以將內核用戶與容器用戶完全分開。 容器中的 root 與主機上的 root 相同,因此成功的妥協可以提供對您機器的控制。

作為圖像作者,您應該在 Dockerfile 中包含USER指令,這樣您的應用程序就可以在沒有 root 的情況下運行。 映像用戶可以使用docker run --user覆蓋它以分配特定的 UID 和 GID。 這有助於緩解映像通常使用 root 的情況。

您可以通過使用--cap-drop=ALL從容器中刪除所有功能來進一步加強安全性,然後使用--cap-add標誌將所需的功能列入白名單。 結合這些技術,您的應用程序將以非 root 用戶身份運行,並具有所需的最低權限集,從而改善您的安全狀況。