Leo

Docker 已啟用 GitHub Actions 與 Docker Hub 之間的 OpenID Connect 驗證,讓工作流程可以交換已簽章的單次執行身分權杖,取得短效的 Docker 憑證,而非從儲存庫密鑰讀取 Personal Access Token。根據 Docker 部落格,此功能適用於 Docker Team、Docker Business 以及 Docker Hardened Images 方案的組織。對平台團隊而言,這將登錄檔憑證從「每 90 天輪替一次,然後祈禱」的堆疊中移除,改採與 AWS、GCP、Azure 和 HashiCorp Vault 相同的聯邦身分模式。

機制簡介

此模式遵循標準的 Actions OIDC 交換流程。當工作流程宣告 permissions: id-token: write 時,GitHub 會為該次執行簽發 JWT,其中包含儲存庫、分支或環境、工作流程名稱及其他宣告。工作流程將此 JWT 呈送給 Docker,Docker 會對照 GitHub 的公開金鑰驗證簽章、比對 Docker 端設定的信任政策,最後回傳短效憑證供後續步驟使用。工作結束後,憑證隨即失效。儲存庫、環境密鑰或 Actions 快取中都不再存在長期有效的 Docker 權杖。Docker 將此變更定位為消除 CI/CD 管線中的靜態憑證,只要使用者事後確實刪除 PAT,此說法即可成立。

信任政策有兩點值得注意。首先,範圍是在 Docker 而非 GitHub 中定義。若政策允許 my-org/* 任何分支的工作流程,攻擊者只要能推送分支到任何儲存庫,就能取得 Docker 憑證。較安全的做法是將政策限定在特定儲存庫與特定環境(environment:prod),並在 GitHub 端為該環境設定必要審核者。其次,OIDC 雖然移除憑證,但未改變授權邊界:外洩的權杖雖然短效,但信任政策本身卻成為需要長期稽核的物件。

為何此憑證重要

今年已發生多起供應鏈事件與靜態登錄檔權杖有關。一個能從受損分支推送至公開標籤的 PAT 或 OAT,只需一次 API 呼叫就能造成供應鏈劫持,且不像雲端 IAM 金鑰那樣受工作負載身分邊界保護。輪替雖然有幫助,但成本不低:輪替後的 Docker 權杖必須重新分發給所有推送映像檔的 Actions 儲存庫與自託管執行器,這也是團隊輪替頻率低於政策規定的主因。

OIDC 並未改變受損工作流程在其執行期間能做的事,而是改變了殘留物。工作結束後,密鑰存放區中不再有任何可竊取的物件。外洩權杖的影響範圍從「直到下次輪替週期」縮短為「直到工作結束」,對大多數 Actions 工作而言僅需數分鐘。這是操作上的結論:需輪替、盤點與事件中撤銷的項目大幅減少。

在工作流程中設定

Actions 端的交換流程與其他 OIDC 整合相同。以下為示意碼,省略特定版本的細節:

permissions:
  id-token: write
  contents: read

jobs:
  push:
    runs-on: ubuntu-latest
    environment: prod
    steps:
      - uses: actions/checkout@<full-40-char-sha>
      - name: Log in to Docker Hub via OIDC
        uses: docker/login-action@<full-40-char-sha>
        with:
          # OIDC-mode inputs per the Docker docs for this feature;
          # no username or password is stored in the repo.
          registry: docker.io
          # ...OIDC-specific parameters go here...
      - name: Build and push
        run: |
          docker buildx build \
            --tag docker.io/$REGISTRY_NAMESPACE/app:${{ github.sha }} \
            --push .

Enter fullscreen mode Exit fullscreen mode

來自值班工程師的兩點提醒。請將登入動作鎖定到完整 commit SHA,而非浮動標籤:交給此步驟的憑證雖有時效限制,但其中的程式碼並無此限制。此外,請將 Docker 端的信任政策設定得更精準:包含儲存庫、分支或環境,以及工作流程檔案路徑。使用萬用字元相當於仍在使用你原本想淘汰的 read_write PAT。

此方案無法解決的問題

OIDC 聯邦身分僅是憑證層面的變更。「執行階段擁有有效權杖」之後的所有流程仍需照常處理。

映像檔來源未變。經 OIDC 驗證的工作流程仍是原本指定的工作流程,且仍受觸發條件允許的分支控制。若政策信任 main,則自核准的 PR 合併到 main 後仍可取得 Docker 憑證。分支保護、部署環境的必要審核者,以及 Actions 針對疑似惡意工作流程的核准控制,均位於此機制之上游。

自託管執行器是另一個隱藏的注意事項。GitHub 託管執行器會如文件所述發出 OIDC 權杖。自託管執行器也可發出權杖,但信任邊界變成「控制執行器主機的人可以要求符合信任政策的 Docker 憑證」。若自託管機群未依儲存庫或環境完全隔離,則 Docker 信任政策的審慎程度應至少與執行器註冊權杖相同。

簽章是獨立的。OIDC 移除推送憑證,但未新增已簽章的摘要。若要讓拉取端驗證推送端產生的內容,仍需使用 Cosign 或平台本身的證明流程。

其他 CI 平台如何處理登錄檔驗證

Docker Hub 在此特定整合上較晚推出,但並非晚於此模式。登錄檔驗證的做法會依管線執行位置而異。

GitHub Actions 至 GHCR。 若登錄檔為 ghcr.io,GitHub Actions 早已具備最完整的方案:內建的 GITHUB_TOKEN 已限定於儲存庫,可推送至組織的容器登錄檔,無需額外設定聯邦身分。若 Docker Hub 並非硬性需求,GHCR 仍是 GitHub 原生團隊的較簡便選擇,而 Docker 的 OIDC 變更並未縮小此差距。

GitLab CI ID 權杖。 GitLab 自身的 OIDC(管線中的 id_tokens:)可聯邦至 AWS、GCP、Vault,並透過登錄檔自身的 OIDC 提供者原生聯邦至 GitLab Container Registry。已在使用 GitLab 的團隊,其 ID 權杖工作流程比任何外部登錄檔整合更為原生;若從頭選擇堆疊,這會是較佳選擇。

CircleCI OIDC。 CircleCI 會為工作發出 OIDC 權杖,主要用於雲端 IAM(AWS、GCP)。大多數團隊在 CircleCI 上的登錄檔登入仍依賴儲存的內容密鑰存取 Docker Hub,這正是 Docker 在 GitHub 端所解決的問題。

Buildkite agent OIDC。 Buildkite 的代理端已簽章 OIDC 權杖已廣泛用於 AWS 與 Vault。登錄檔驗證模式依機群而異,而代理機群的信任邊界比權杖類型更為重要。

Buddy。 Buddy 為託管 CI/CD 平台,其 Docker 管線動作包含從 Buddy 範圍密鑰與單次管線短效憑證讀取的登錄檔登入步驟。若希望在同一介面完成 Docker 推送與環境控管,而非在 Actions 與外部政策中組裝,這是一個選項。若登錄檔需求完全在 Docker Hub,且 CI 已全部使用 GitHub,Docker 的新 OIDC 流程可讓你留在既有工具中。

貫穿全文的重點是:聯邦身分已成為登錄檔驗證的預設假設,而非可有可無的功能。CI 中的長期登錄檔權杖已成例外,每個剩下的權杖都是平台團隊待辦清單中的輪替項目。

後續值得關注的事項

有兩件事值得追蹤。首先,Docker 端的信任政策介面是否會出現與早期 AWS-to-Actions OIDC 政策相同的誤設影響範圍——過寬的 audsub 宣告讓組織內任何工作流程都能擔任生產角色。當時的補救措施是收緊宣告,此處亦應從第一天起就採取相同嚴謹度。其次,Docker 是否會將 OIDC 流程延伸至 GitHub Actions 以外的 CI 平台。目前公告指出,此版本僅適用於 GitHub Actions 與付費 Docker 組織層級。其他使用者仍需繼續輪替 PAT。