文章

EKS Secrets Store CSI 同步問題:啟用 syncSecret.enabled 解決 Secret 未建立

記錄 AWS Secrets Manager 搭配 Secrets Store CSI Driver 時,SecretProviderClass 已建立卻沒有同步出 Kubernetes Secret 的問題,最後透過更新 Helm 的 syncSecret.enabled 屬性解決。

EKS Secrets Store CSI 同步問題:啟用 syncSecret.enabled 解決 Secret 未建立

這次在 EKS 部署服務,將應用需要的機密資料放在 AWS Secrets Manager,再透過 Secrets Store CSI Driver 提供給 Pod。SecretProviderClass 已經建立,裡面也設定了 secretObjects,但應用啟動時仍然出現 Secret 找不到的錯誤。

原本以為設定套用後,CSI 就會同步建立 Kubernetes Secret。後來才發現,還有一個控制同步功能的屬性沒有開啟:syncSecret.enabled。更新這個設定後,Secret 就能正常同步,問題也隨之解決。

掛載檔案與同步 Secret 是兩件事

Secrets Store CSI Driver 搭配 AWS provider,可以將 AWS Secrets Manager 的資料掛載成 Pod 內的檔案。如果應用透過 secretKeyRef 讀取環境變數,則還需要將資料同步成 Kubernetes Secret。

flowchart TD
    SM["AWS Secrets Manager"] --> Provider["AWS provider"]
    Provider --> CSI["Secrets Store CSI Driver"]
    SPC["SecretProviderClass"] -->|描述取值與同步設定| CSI
    CSI -->|Pod 掛載 CSI volume| Files["Pod 內的機密檔案"]
    CSI -->|啟用同步並依 secretObjects 建立| Secret["Kubernetes Secret"]
    Secret -->|secretKeyRef| Env["應用環境變數"]

這兩種使用方式共用資料來源,但需要的設定不同。SecretProviderClassparameters 描述要從 AWS 取得哪些資料;secretObjects 則描述同步後的 Kubernetes Secret 名稱、類型與 keys。AWS provider 官方說明

此外,必須有 Pod 掛載對應的 CSI volume,才會觸發同步。單獨建立 SecretProviderClass,不會立即產生 Kubernetes Secret。CSI Secret 同步文件

SecretProviderClass 已存在,Secret 卻沒有出現

以下將資源名稱統一替換為範例名稱。當時的 SecretProviderClass 已經包含同步設定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: app-database-secret
  namespace: app
spec:
  provider: aws
  secretObjects:
    - secretName: app-db-secret
      type: Opaque
      data:
        - objectName: db-user
          key: db-user
        - objectName: db-password
          key: db-password
  parameters:
    region: ap-northeast-1
    objects: |
      - objectName: "<SECRETS_MANAGER_SECRET_ARN>"
        objectType: secretsmanager
        jmesPath:
          - path: username
            objectAlias: db-user
          - path: password
            objectAlias: db-password

這份設定會擷取 Secrets Manager JSON 裡的 usernamepassword,透過別名 db-userdb-password 對應掛載內容,再同步到 app-db-secret 的同名 keys。

但查詢資源時,卻出現兩種不同結果:

1
2
kubectl get secretproviderclass -n app
kubectl get secret app-db-secret -n app

SecretProviderClass 已存在,Kubernetes Secret 卻回傳 NotFound。這讓我把注意力轉向 driver 本身的同步設定:SPC 定義了「要同步什麼」,但 driver 是否已啟用這項功能,還是另一個條件。

找到原因:syncSecret.enabled 沒有開啟

這次 CSI driver 是透過 Helm 管理,因此先查實際的 release 名稱:

1
helm list -n kube-system

找到 secrets-store-csi-driver 後,讀取包含預設值的完整設定:

1
2
3
helm get values secrets-store-csi-driver \
  -n kube-system \
  --all

關鍵就在這個屬性:

1
2
syncSecret:
  enabled: false

雖然 SecretProviderClass 已經寫了 secretObjects,但 Helm 設定中的 syncSecret.enabled 仍是 false,沒有啟用同步所需的 RBAC。

syncSecret.enabled 屬於 Secrets Store CSI Driver 的 Helm values,應修改 driver 的設定。它不是 SecretProviderClass 的欄位,也不是 Pod annotation。官方 chart 將這個屬性用於控制同步 Kubernetes Secret 所需的 roles 與 bindings。CSI Helm chart 設定說明

這也解釋了為什麼只查看 driver container 的啟動參數,無法直接判斷同步功能是否開啟。這個設定主要影響 RBAC,不會以一個同名的 --sync-secret 參數出現在 container args 裡。

更新屬性後,Secret 正常同步

找到原因後,處理方式就是更新現有 Helm release,將 syncSecret.enabled 改成 true

先加入官方 chart repository:

1
2
3
helm repo add secrets-store-csi-driver \
  https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo update

接著更新 driver。以下使用當時的 release 名稱、namespace 與 chart 版本 1.6.0;套用到其他環境時,需使用該環境實際的值:

1
2
3
4
5
6
helm upgrade secrets-store-csi-driver \
  secrets-store-csi-driver/secrets-store-csi-driver \
  --version 1.6.0 \
  -n kube-system \
  --reuse-values \
  --set syncSecret.enabled=true

--reuse-values 保留原有 release 設定,再覆寫這次要調整的屬性。更新後的值為:

1
2
syncSecret:
  enabled: true

官方安裝文件也將 syncSecret.enabled=true 列為啟用 Kubernetes Secret 同步的設定。CSI 安裝文件

更新這個屬性後,在 Pod 掛載對應 CSI volume 的情況下,secretObjects 定義的 Kubernetes Secret 就正常建立了,這次同步問題也就解決。

這次漏掉的是 driver 層的同步開關。SecretProviderClass 負責定義同步內容,Pod 掛載觸發資料讀取,而 syncSecret.enabled 則提供同步 Kubernetes Secret 所需的權限設定;三者配合,才能讓 Secrets Manager 的資料順利提供給透過 Kubernetes Secret 取值的應用。

本文章以 CC BY 4.0 授權

熱門標籤