K8s Pull Image Authentication

整理 Kubernetes 拉取 image 時的 credential provider 與 imagePullSecret 兩種驗證方式、缺點,以及 v1.33 的 Service Account token 方案。

發佈 ~3 分鐘 #K8s

inspired by Kubernetes v1.33: From Secrets to Service Accounts: Kubernetes Image Pulls Evolved

思考脈絡

K8s v1.33 行為變更(WI for image pull)
       ↓
Argo CD Image Updater 是否會受影響?
       ↓
誰實際拉 image?
       ↓
Argo CD 本體會拉 image 嗎?
       ↓
Kubelet 拉 image 時用什麼認證方式?
       ↓
我沒設 imagePullSecret → 是用 credential provider?
       ↓
我用 EKS + ECR + 沒掛 secret,但能拉 image → 那我是在用 Node IAM Role?

ArgoCD Image Updater 更新 image 的步驟

  1. 查詢 image registry

  2. 修改 ArgoCD Application 下的 k8s manifest,把 image tag 根據規則改成最新的

  3. ArgoCD sync app → 呼叫 K8s API → K8s 產生新的 pod

  4. K8s 為新的 pod 建立 container 時,Kubelet 會拉取新的 image。

    Pull image 的驗證會在第四步驟發生,根據環境會採用以下其中一種驗證方式:

    1. 節點的 IAM Role (+ ECR credential helper):會由 credential provider 自動完成
    2. imagePullSecret:由 pod 或他的 ServiceAccount 提供

Pull Image 驗證方式

  1. Credential Provider

    • 定義:在 AWS 就是 Node IAM role,在其他雲上就會是該雲的權限管理服務

      • 節點的 IAM Role(Node IAM Role)
      • 搭配 AWS 提供的工具:amazon-ecr-credential-helper(或更低層的 SDK 認證流程)
      • 由 Kubelet 在節點上執行,自動取得 ECR token 並完成 image pull
    • 運作方式

      1. Kubelet 接收到新的 pod spec,啟動流程包括:

        1. 驗證 volume 是否存在
        2. 拉取 image
        3. 啟動 container
      2. Kubelet 呼叫 containerd 拉取 image,containerd 嘗試與 registry 認證

      3. containerd 偵測到 ECR,觸發 credential provider (amazon-ecr-credential-helper) 進行驗證

        📎 docker 或 containerd 可以透過 amazon-ecr-credential-helper 來動態取得 login 密碼

        這個 helper 程式會執行以下步驟:

        1. 使用 node instance profile 的權限,透過 AWS SDK 執行 ecr:GetAuthorizationToken
          1. 會獲得 base64 encoded token,格式為 user:password
        2. credential helper 將回傳資訊解開成 docker login 所需的資訊,並使用這組資訊向 registry 驗證
      4. 驗證成功後,image 會被拉取下來,繼續啟動 container

  2. imagePullSecret

    • 定義:在 pod / deployment 中指定 pull image 時使用的 secret

    • 運作方式:

      1. API server 收到包含 imagePullSecret 的 pod spec,將 secret 存入 etcd
      2. Kubelet 接收到新的 pod spec,內容會包含 imagePullSecret
      3. Kubelet 向 API server 請求 imagePullSecret 裡面指定的 secret
      4. API server 回傳 base64 encoded docker config json
      5. Kubelet 呼叫 containerd 透過 docker config json 來登入 registry、並拉取 image

💭 這篇文章比較像是 Kaniko ECR connection Issue & debug 心得 的補充說明。在那篇文章 debug 時,有看到許多關於 ecr-credential-helper 和改用 secret 驗證 registry 的解法,這篇文能幫助理解為什麼其他人會提出那些不同的解法

  1. Image pull secrets stored in the Kubernetes API
    • 這些 secret 通常會比較難 rotate
    • 必須要明確的 attach 到某個 service account 或 pod 上
    • secret 洩漏會導致未授權的存取
  2. Kubelet credential providers
    • 這些 credential 存在 node level,因此只要在同個節點上的 pod 就會有權限可以 pull image
    • 無法以 workload 做區隔,導致安全風險增加

Solution: Service Account Token Integration for Kubelet Credential Providers

  • 這個解法的優點
    1. 依照 workload 切分認證範圍 (workload-specific authentication)
      • 每個 pod 會有自己的 service account token
      • 得到的憑證會限制 workload 範圍,不會再有同個 node 下的 pod 共享憑證的問題
    2. 短期憑證 (Ephemeral credential) 降低外洩風險
    3. 原生整合 Service Account 認證機制
  • 運作方式:
    1. Pod 被建立時,Kubelet 準備拉 image
    2. Kubelet 確認 Credential Provider 是否有宣告「支援 SA token」
    3. 若支援,Kubelet 為該 Pod 所使用的 ServiceAccount 產生一組短期 OIDC 格式的 token
    4. 將此 token 包進 CredentialProviderRequest 中,送給 Credential Provider
    5. Credential Provider 使用該 token 對應的 workload 身份進行驗證
    6. 向外部 Registry(如 ECR / ACR / GCR)換取臨時的 image pull credential(例如 docker login token)
    7. 將這些 image pull 憑證回傳給 Kubelet