ECI 的高階配置選項
Docker socket 掛載許可權
預設情況下,當啟用增強型容器隔離 (ECI) 時,Docker Desktop 不允許將 Docker Engine socket 繫結掛載到容器中。
$ docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock docker:cli
docker: Error response from daemon: enhanced container isolation: docker socket mount denied for container with image "docker.io/library/docker"; image is not in the allowed list; if you wish to allow it, configure the docker socket image list in the Docker Desktop settings.
這可以防止惡意容器訪問 Docker Engine,因為此類訪問可能允許它們執行供應鏈攻擊。例如,構建並將惡意映象推送到組織的倉庫中,或執行類似操作。
然而,某些合法用例需要容器訪問 Docker Engine socket。例如,流行的 Testcontainers 框架有時會將 Docker Engine socket 繫結掛載到容器中,以便管理它們或執行測試後清理。類似地,某些 Buildpack 框架,例如 Paketo,需要將 Docker socket 繫結掛載到容器中。
管理員可以選擇配置 ECI,以允許在受控的方式下將 Docker Engine socket 繫結掛載到容器中。
這可以透過 admin-settings.json
檔案中的 Docker Socket 掛載許可權部分來完成。例如:
{
"configurationFileVersion": 2,
"enhancedContainerIsolation": {
"locked": true,
"value": true,
"dockerSocketMount": {
"imageList": {
"images": [
"docker.io/localstack/localstack:*",
"docker.io/testcontainers/ryuk:*",
"docker:cli"
],
"allowDerivedImages": true
},
"commandList": {
"type": "deny",
"commands": ["push"]
}
}
}
}
提示
現在,您也可以在Docker 管理員控制檯中配置這些設定。
如上所示,將 Docker socket 繫結掛載到容器中有兩種配置:imageList
和 commandList
。下面將對此進行描述。
映象列表
imageList
是允許繫結掛載 Docker socket 的容器映象列表。預設情況下,此列表為空,當啟用 ECI 時,不允許任何容器繫結掛載 Docker socket。但是,管理員可以將映象新增到列表中,可以使用以下任意格式:
映象引用格式 | 描述 |
---|---|
<image_name>[:<tag>] | 映象名稱,帶可選的標籤。如果省略標籤,則使用 :latest 標籤。如果標籤是萬用字元 * ,則表示“該映象的任意標籤”。 |
<image_name>@<digest> | 映象名稱,帶特定的倉庫摘要(例如,由 docker buildx imagetools inspect <image> 報告)。這意味著只允許名稱和摘要匹配的映象。 |
映象名稱遵循標準約定,因此它可以指向任何映象倉庫和倉庫。
在前面的示例中,imageList
配置了三個映象:
"imageList": {
"images": [
"docker.io/localstack/localstack:*",
"docker.io/testcontainers/ryuk:*",
"docker:cli"
]
}
這意味著使用 docker.io/localstack/localstack
或 docker.io/testcontainers/ryuk
映象(帶任意標籤),或使用 docker:cli
映象的容器,在啟用 ECI 時被允許繫結掛載 Docker socket。因此,以下操作有效:
$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker:cli sh
/ #
提示
對允許的映象要嚴格限制,如建議中所述。
通常,使用標籤萬用字元格式(例如 <image-name>:*
)指定映象會更容易,因為這樣就不需要在每次使用新版本映象時更新 imageList
。或者,您可以使用不可變標籤(例如 :latest
),但這並不總是像萬用字元那樣有效,因為例如 Testcontainers 使用的是特定版本的映象,不一定是最新版本。
啟用 ECI 後,Docker Desktop 會定期從相應的映象倉庫下載允許的映象摘要,並將其儲存在記憶體中。然後,當容器透過 Docker socket 繫結掛載啟動時,Docker Desktop 會檢查容器的映象摘要是否與允許的摘要之一匹配。如果匹配,則允許容器啟動;否則,將被阻止。
由於進行了摘要比較,透過將不允許的映象重新標記為允許的映象名稱來繞過 Docker socket 掛載許可權是不可能的。換句話說,如果使用者執行以下操作:
$ docker image rm <allowed_image>
$ docker tag <disallowed_image> <allowed_image>
$ docker run -v /var/run/docker.sock:/var/run/docker.sock <allowed_image>
那麼標記操作會成功,但 docker run
命令會失敗,因為不允許的映象的摘要與倉庫中允許的映象的摘要不匹配。
派生映象的 Docker Socket 掛載許可權
如前一節所述,管理員可以透過 imageList
配置允許掛載 Docker socket 的容器映象列表。
這適用於大多數情況,但並非總是如此,因為它要求事先知道應允許掛載 Docker socket 的映象名稱。一些容器工具,例如 Paketo buildpacks,會構建需要 Docker socket 繫結掛載的臨時本地映象。由於這些臨時映象的名稱無法事先得知,imageList
就不夠用了。
為了解決這個問題,從 Docker Desktop 4.34 版本開始,Docker Socket 掛載許可權不僅適用於 imageList
中列出的映象,也適用於從 imageList
中映象派生(即構建自)的任何本地映象。
也就是說,如果一個名為“myLocalImage”的本地映象構建自“myBaseImage”(即其 Dockerfile 包含 FROM myBaseImage
指令),那麼如果“myBaseImage”在 imageList
中,則“myBaseImage”和“myLocalImage”都將被允許掛載 Docker socket。
例如,要使 Paketo buildpacks 能夠在 Docker Desktop 和 ECI 下工作,只需將以下映象新增到 imageList
中:
"imageList": {
"images": [
"paketobuildpacks/builder:base"
],
"allowDerivedImages": true
}
當 buildpack 執行時,它將建立一個派生自 paketobuildpacks/builder:base
的臨時映象,並將 Docker socket 掛載到其中。ECI 將允許這樣做,因為它會注意到該臨時映象派生自一個允許的映象。
此行為預設是停用的,必須透過如上所示設定 "allowDerivedImages": true
來顯式啟用。通常建議您除非確定需要,否則停用此設定。
一些注意事項
設定
"allowedDerivedImages" :true
會影響容器的啟動時間,最長可能增加 1 秒,因為 Docker Desktop 需要對容器映象執行更多檢查。allowDerivedImages
設定僅適用於從允許的映象構建的純本地映象。也就是說,派生映象不能存在於遠端倉庫中,因為它如果存在,您只需將其名稱列在imageList
中即可。要使派生映象檢查生效,父映象(即
imageList
中的映象)必須在本地存在(即,必須已從倉庫顯式拉取)。這通常不是問題,因為需要此功能的工具(例如 Paketo buildpacks)會預先拉取父映象。僅適用於 Docker Desktop 4.34 和 4.35 版本:
allowDerivedImages
設定適用於imageList
中使用顯式標籤(例如<name>:<tag>
)指定的所有映象。它不適用於使用前一節所述的標籤萬用字元(例如<name>:*
)指定的映象。在 Docker Desktop 4.36 及更高版本中,此注意事項不再適用,這意味著allowDerivedImages
設定適用於使用或不使用萬用字元標籤指定的映象。這使得管理 ECI Docker socket 映象列表更加容易。
允許所有容器掛載 Docker socket
在 Docker Desktop 4.36 及更高版本中,可以將 imageList
配置為允許任何容器掛載 Docker socket。您可以透過將 "*"
新增到 imageList
來實現:
"imageList": {
"images": [
"*"
]
}
這會告訴 Docker Desktop 允許所有容器掛載 Docker socket,從而提高了靈活性,但降低了安全性。在使用增強型容器隔離時,它還會縮短容器啟動時間。
建議僅在明確列出允許的容器映象不夠靈活的情況下使用此選項。
命令列表
除了前幾節描述的 imageList
之外,ECI 還可以進一步限制容器透過繫結掛載的 Docker socket 發出的命令。這透過 Docker socket 掛載許可權 commandList
來實現,它作為 imageList
的補充安全機制(即,類似於第二道防線)。
例如,假設 imageList
配置為允許 docker:cli
映象掛載 Docker socket,並且使用該映象啟動了一個容器:
$ docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock sh
/ #
預設情況下,這允許容器透過該 Docker socket 發出任何命令(例如,構建映象並推送到組織的倉庫),這通常是不希望的。
為了提高安全性,可以配置 commandList
來限制容器內程序可以在繫結掛載的 Docker socket 上發出的命令。commandList
可以配置為“拒絕”列表(預設)或“允許”列表,具體取決於您的偏好。
列表中的每個命令都透過其名稱指定,該名稱與 docker --help
報告的名稱一致(例如,“ps”、“build”、“pull”、“push”等)。此外,允許使用以下命令萬用字元來阻止整個命令組:
命令萬用字元 | 描述 |
---|---|
"container*" | 指所有“docker container …”命令 |
"image*" | 指所有“docker image …”命令 |
"volume*" | 指所有“docker volume …”命令 |
"network*" | 指所有“docker network …”命令 |
"build*" | 指所有“docker build …”命令 |
"system*" | 指所有“docker system …”命令 |
例如,以下配置會阻止 Docker socket 上的 build
和 push
命令:
"commandList": {
"type": "deny",
"commands": ["build", "push"]
}
因此,如果在容器內部,您在繫結掛載的 Docker socket 上發出這些命令中的任何一個,它們都將被阻止:
/ # docker push myimage
Error response from daemon: enhanced container isolation: docker command "/v1.43/images/myimage/push?tag=latest" is blocked; if you wish to allow it, configure the docker socket command list in the Docker Desktop settings or admin-settings.
類似地:
/ # curl --unix-socket /var/run/docker.sock -XPOST https:///v1.43/images/myimage/push?tag=latest
Error response from daemon: enhanced container isolation: docker command "/v1.43/images/myimage/push?tag=latest" is blocked; if you wish to allow it, configure the docker socket command list in the Docker Desktop settings or admin-settings.
請注意,如果 `commandList` 被配置為“允許”列表,則效果將相反:只允許列出的命令。是將列表配置為允許列表還是拒絕列表取決於用例。
建議
對您允許繫結掛載 Docker socket 的容器映象列表(即
imageList
)要嚴格限制。通常,只允許那些絕對必要且您信任的映象執行此操作。如果可能,請在
imageList
中使用標籤萬用字元格式(例如,<image_name>:*
),這可以避免因映象標籤更改而需要更新admin-settings.json
檔案。在
commandList
中,阻止您不期望容器執行的命令。例如,對於本地測試(例如 Testcontainers),繫結掛載 Docker socket 的容器通常會建立 / 執行 / 刪除容器、卷和網路,但通常不會構建映象或將其推送到倉庫(儘管有些可能確實會這樣做)。允許或阻止哪些命令取決於具體用例。- 請注意,容器透過繫結掛載的 Docker socket 發出的所有“docker”命令也將增強的容器隔離下執行(即,由此產生的容器使用 Linux 使用者名稱空間,敏感的系統呼叫會經過審查等)。
注意事項和限制
當 Docker Desktop 重啟時,允許掛載 Docker socket 的映象可能會意外地被阻止這樣做。這可能發生在遠端倉庫中的映象摘要(digest)發生變化(例如,":latest" 映象已更新),並且該映象的本地副本(例如,之前透過
docker pull
獲取的)不再與遠端倉庫中的摘要匹配時。在這種情況下,請刪除本地映象並再次拉取(例如,執行docker rm <image>
和docker pull <image>
)。對於使用僅本地映象(即不在登錄檔上的映象)的容器,除非它們派生自允許的映象或您已允許所有容器掛載 Docker socket,否則無法允許 Docker socket 繫結掛載。這是因為 Docker Desktop 從登錄檔拉取允許映象的摘要,然後用它來與映象的本地副本進行比較。
commandList
配置適用於所有允許繫結掛載 Docker socket 的容器。因此,無法為每個容器單獨配置它。commandList
中尚不支援以下命令
不支援的命令 | 描述 |
---|---|
compose | Docker Compose |
dev | 開發環境 |
extension | 管理 Docker Extensions |
feedback | 向 Docker 傳送反饋 |
init | 建立 Docker 相關的啟動檔案 |
manifest | 管理 Docker 映象清單 |
plugin | 管理外掛 |
sbom | 檢視軟體物料清單 (SBOM) |
scout | Docker Scout |
trust | 管理 Docker 映象的信任 |
注意
當執行“真正”的 Docker-in-Docker(即在容器內執行 Docker Engine)時,Docker socket 掛載許可權不適用。在這種情況下,沒有將主機的 Docker socket 繫結掛載到容器中,因此容器不存在利用主機 Docker Engine 的配置和憑據執行惡意活動的風險。增強容器隔離能夠在不賦予外部容器在 Docker Desktop VM 中真正的 root 許可權的情況下,安全地執行 Docker-in-Docker。