ECI 的進階設定選項

Docker 通訊端掛載權限

預設情況下,啟用增強型容器隔離 (ECI) 後,Docker Desktop 不允許將 Docker 引擎通訊端繫結掛載到容器中

$ 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 引擎,因為此類存取可能允許它們執行供應鏈攻擊。例如,建置惡意映像檔並將其推送到組織的儲存庫或類似位置。

但是,某些合法使用案例需要容器存取 Docker 引擎通訊端。例如,熱門的 TestcontainersPaketoadmin-settings.json 檔案中的 Docker 通訊端掛載權限區段來完成。例如

{
  "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 通訊端繫結掛載到容器中有兩種設定:imageListcommandList。 這些說明如下。

映像檔清單

imageList 是允許繫結掛載 Docker 通訊端的容器映像檔清單。 預設情況下,此清單是空的,啟用 ECI 時,不允許任何容器繫結掛載 Docker 通訊端。 但是,管理員可以使用下列任一格式將映像檔新增至清單

映像檔參考格式說明
<image_name>[:<tag>]映像檔的名稱,包含選用標籤。如果省略標籤,則使用 :latest 標籤。如果標籤是萬用字元 *,則表示「該映像檔的任何標籤」。
<image_name>@<digest>映像檔的名稱,以及特定的儲存庫摘要(例如,由 docker buildx imagetools inspect <image> 報告)。這表示只允許符合該名稱和摘要的映像檔。

映像檔名稱遵循標準慣例,因此它可以指向任何儲存庫和儲存機制。

在前一個範例中,映像檔清單設定了三個映像檔

"imageList": {
  "images": [
    "docker.io/localstack/localstack:*",
    "docker.io/testcontainers/ryuk:*",
    "docker:cli"
  ]
}

這表示使用 docker.io/localstack/localstackdocker.io/testcontainers/ryuk 映像檔(具有任何標籤)或 docker:cli 映像檔的容器,在啟用 ECI 時允許繫結掛載 Docker 通訊端。因此,以下操作有效

$ 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 通訊端掛載權限

Docker Desktop 版本 4.34.0 引進

如前一節所述,管理員可以透過 imageList 設定允許掛載 Docker Socket 的容器映像檔清單。

這適用於大多數情況,但並非總是如此,因為它需要事先知道應允許 Docker Socket 掛載的映像檔名稱。一些容器工具,例如 Paketo 建置包,會建置需要 Docker Socket 掛載的臨時本地映像檔。由於這些臨時映像檔的名稱事先未知,因此 imageList 不夠用。

為了克服這個問題,從 Docker Desktop 版本 4.34 開始,Docker Socket 掛載權限不僅適用於 imageList 中列出的映像檔;它們也適用於任何衍生自(即從)imageList 中映像檔建置的本地映像檔。

也就是說,如果一個名為「myLocalImage」的本地映像檔是從「myBaseImage」建置的(即,有一個 Dockerfile 包含 FROM myBaseImage),那麼如果「myBaseImage」在 imageList 中,則允許「myBaseImage」和「myLocalImage」掛載 Docker Socket。

例如,要讓 Paketo 建置包與 Docker Desktop 和 ECI 搭配使用,只需將以下映像檔新增到 imageList

"imageList": {
  "images": [
    "paketobuildpacks/builder:base"
  ],
  "allowDerivedImages": true
}

當建置包運行時,它將建立一個衍生自 paketobuildpacks/builder:base 的臨時映像檔,並將 Docker Socket 掛載到它。ECI 將允許這樣做,因為它會注意到臨時映像檔衍生自允許的映像檔。

預設情況下會停用此行為,必須透過如上所示設定 "allowDerivedImages": true 來明確啟用。一般建議您停用此設定,除非您知道需要它。

一些注意事項

  • 設定 "allowedDerivedImages" :true 將會影響容器的啟動時間,最多會增加 1 秒,因為 Docker Desktop 需要對容器映像檔執行更多檢查。

  • allowDerivedImages 設定僅適用於從允許映像檔建置的僅限本機映像檔。也就是說,衍生的映像檔不得存在於遠端儲存庫中,因為如果存在,您只需將其名稱列在 imageList 中即可。

  • 為了使衍生映像檔檢查正常運作,父映像檔(即 imageList 中的映像檔)必須存在於本機(即必須已從儲存庫中明確提取)。這通常不是問題,因為需要此功能的工具(例如,Paketo 建置包)會預先提取父映像檔。

  • 僅適用於 Docker Desktop 版本 4.34 和 4.35:allowDerivedImages 設定適用於使用明確標籤(例如,<name>:<tag>)指定的 imageList 中的所有映像檔。它不適用於使用前一節中描述的標籤萬用字元(例如,<name>:*)指定的映像檔。在 Docker Desktop 4.36 及更高版本中,此注意事項不再適用,這表示 allowDerivedImages 設定適用於使用或不使用萬用字元標籤指定的映像檔。這使得管理 ECI Docker Socket 映像檔清單更容易。

允許所有容器掛載 Docker 通訊端

在 Docker Desktop 版本 4.36 及更高版本中,可以將映像檔清單設定為允許任何容器掛載 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 上的 buildpush 命令

"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 http://localhost/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 的映像檔可能會意外地被阻止這樣做。當遠端儲存庫中的映像檔摘要發生更改時(例如,":latest" 映像檔已更新),並且該映像檔的本機副本(例如,來自先前的 docker pull)不再與遠端儲存庫中的摘要匹配時,就會發生這種情況。在這種情況下,請移除本地映像檔並再次提取它(例如,docker rm <image>docker pull <image>)。

  • 除非它們是衍生自允許的映像檔或您已允許所有容器掛載 Docker Socket,否則無法允許在使用僅限本機映像檔(即不在登錄檔上的映像檔)的容器上進行 Docker Socket 繫結掛載。這是因為 Docker Desktop 會從登錄檔中提取允許映像檔的摘要,然後使用它與映像檔的本機副本進行比較。

  • commandList 設定適用於所有允許繫結掛載 Docker Socket 的容器。因此,無法針對每個容器進行不同的設定。

  • commandList 中尚不支援以下命令

不支援的命令說明
composeDocker Compose
dev開發環境
extension管理 Docker 擴充功能
feedback將意見反應傳送給 Docker
init建立 Docker 相關的啟動檔案
manifest管理 Docker 映像檔資訊清單
plugin管理外掛程式
sbom檢視軟體物料清單 (SBOM)
scoutDocker Scout
trust管理 Docker 映像檔的信任

**注意**

當運行「真正的」Docker-in-Docker(即在容器內運行 Docker 引擎)時,Docker Socket 掛載權限不適用。在這種情況下,主機的 Docker Socket 沒有繫結掛載到容器中,因此容器沒有利用主機 Docker 引擎的設定和憑證來執行惡意活動的風險。增強型容器隔離能夠安全地運行 Docker-in-Docker,而無需授予外部容器在 Docker Desktop 虛擬機器中的真正 root 權限。