先日、EKS AddonにAWS Secrets Store CSI Driver providerを追加したというアナウンスがありました。これにより、Kubernetesで外部シークレット管理サービスを利用する方法として、External Secrets Operator(ESO)からSecrets Store CSI Driverへ切り替える選択肢が現実的になりました。
今回は、ESOの現状を踏まえつつ、AWS Secrets Store CSI Driver providerでのシークレット管理方法についてみていきます。
- Kubernetesにおけるシークレット管理の背景
- EKS AddonでAWS Secrets Store CSI Driver providerをインストール
- AWS Secrets Store CSI Driver providerを利用する
- TIPS: Secrets同期されずエラーが出るとき
- まとめ
- 参考資料
Kubernetesにおけるシークレット管理の背景
Kubernetesにおいて、どうしよう...となりやすいのがシークレットです。Kubernetes SecretsはただのBase64エンコードされた平文データなため、KubernetesマニフェストとしてGitリポジトリに保存するのは現代のポリシー的には受け入れられません。かといってシークレットをKMSや公開鍵で暗号化してGitに保存すると、機密情報のアップデートや利用がとても煩雑になります。使いやすさとセキュリティのバランスを取るのが難しいわけです。
そこで、ここ数年はクラウドの機密情報ストア(AWS Secrets Manager, Azure Key Vaultなど)にデータを保持しておき、Kubernetesはそれを読み取ってKubernetes SecretsやPodにマウントする手法が広く利用されています。この方法なら、Gitやコンテナに直接シークレットを含めることなく安全にPodで参照しつつ、シークレットの運用もシンプルです。機密情報ストアをKubernetesから利用する方法としては、コミュニティが提供するExternal Secrets Operator(ESO)と、Kubernetes公式のSecrets Store CSI Driverの実装があります。
External Secrets Operatorとは
External Secrets Operatorは、機密情報ストアからシークレットを取得しKubernetes Secretsへ同期/提供するオープンソースのコントローラーです。ESOはクラウドを含む複数のシークレット管理サービスに対応しており、柔軟な認証方式をサポートしています。また、ESO以前に使われていた同様の機能を提供していたコントローラーKubernetes External Secretsから移行先として提示されたこともあり、現在も広く利用されています。
そんなESOですが、2025年、プロジェクトの利用者に対してコア開発者の少なさが課題となっていることがアナウンスされています。いわゆるOSSで食っていけない状況が続いています。
- 2022年11月3日、GoDaddy提供のkubernetes-extenal-secrets1がメンテナンスされなくなり、同時期に登場したExternal Secrets Operatorへの移行をユーザーにアナウンス
- 2025年8月13日、ユーザーの大きさに対してコアチームの人数不足の現状から、最低5人の長期コミュニティメンバーが揃うまでリリースを一時停止するアナウンス
- 2025年9月12日、リリースの一時停止に関するアナウンスを削除し、リリースが再開
ESOの状況に対して私個人としてできるのはコントリビュートやスポンサーですが、他方でリリースが止まった状況を踏まえると今後の代替手段を探す必要もあります。
Secrets Store CSI Driverとは
Secrets Store CSI Driverの前にCSIについて簡単に説明します。KubernetesのContainer Storage Interface(CSI) Driverは、永続ストレージ(ブロックデバイスやファイルストレージ)をプラグイン方式で追加できるようにする仕組みです。実装例としてAWS EBS CSI Driver、Azure Disk CSI Driver、Google Compute Engine Persistent Disk CSI Driverなどがありストレージ(PV/PVC)を提供します。
一方で、Secrets Store CSI Driverはストレージではなく外部の機密情報ストアに保存された機密情報をCSIボリューム2としてPodにマウントするためのドライバーです。AWSが公式に提供するAWS Secrets Store CSI Driver provider(AWS Secrets and Configuration Provider、ASCPとも呼ばれる)はその実装例の1つで、AWS Secrets ManagerやAWS Systems Manager Parameter Storeに保存されているシークレットをPod内のファイルとしてマウントしたり、オプションでKubernetes Secretへ同期できます。
AWS Secrets Store CSI Driver provider(以降ASCP)はESOと同じような使い方ができますが、AWS公式実装にもかかわらずEKSへの導入にHelm Chartを使う必要があり、あまり機能が変わらないことから、ESOを使っているなら移行するほどの動機付けもありませんでした。しかし今回、EKS AddonにASCPが加わったことで、導入や運用が以前より簡単になりました。何よりESOのメンテナンス問題を考えると、AWS公式がメンテナンスしている点は魅力的です。ここにきて、ESOの代替手段として十分に検討できるようになりました。
EKS AddonでAWS Secrets Store CSI Driver providerをインストール
ASCPは、AWS提供のEKS Addonとして導入できます。EKS Addonを利用することで、Helm Chartやマニフェストを手動で適用する必要がなくなり、AWS Management ConsoleやAWS CLI、IaCから簡単に導入できます。AWS提供のEKS Addonなので、EKSクラスターのバージョンアップに合わせて速やかにバージョンアップが提供される点もメリットです。3
EKS Addonのインストール
EKS Addonとして導入する場合、以下のコマンドでAddonをインストールします。どの方法もHelmチャートを使ってインストールするより圧倒的に簡単です。カスタム設定したい値が、syncSecret.enabled: bool程度しかないので現実的にありって感じです。
eksctl create addon --cluster $CLUSTER_NAME --name aws-secrets-store-csi-driver-provider
あるいは、IaCなら次のように設定します。TerraformとPulumi C#の例を示します。
# terraform resource "aws_eks_addon" "aws_secrets_store_csi_driver_provider" { cluster_name = aws_eks_cluster.example.name addon_name = "aws-secrets-store-csi-driver-provider" addon_version = "v2.1.1-eksbuild.1" resolve_conflicts_on_update = "OVERWRITE", resolve_conflicts_on_create = "OVERWRITE", configuration_values = jsonencode({ "secrets-store-csi-driver" = { "syncSecret" = { "enabled" = true } } }) }
// Pulumi C# new Addon($"{name}-aws-secrets-store-csi-driver-provider", new() { AddonName = "aws-secrets-store-csi-driver-provider", AddonVersion = "v2.1.1-eksbuild.1", ClusterName = cluster.Name, ResolveConflictsOnCreate = "OVERWRITE", ResolveConflictsOnUpdate = "OVERWRITE", ConfigurationValues = """ { "secrets-store-csi-driver": { "syncSecret": { "enabled": true } } } """.Minify(), }); static string Minify(this string value) => value .Replace("\r\n", "\n") .Replace("\n", "") .Replace("\t", "") .Replace(" ", "");
利用できるEKS Addonバージョンは、次のコマンドで確認できます。
aws eks describe-addon-versions --kubernetes-version 1.34 --addon-name aws-secrets-store-csi-driver-provider | jq ".addons[].addonVersions[] | {addonVersion: .addonVersion, clusterVersion: .compatibilities[0].clusterVersion }"
Pod Identityの用意
ESOはESO ControllerでAWS認証しましたが、ASCPは利用するアプリケーションPodのService AccountでAWS認証します。Podごとに異なる権限を与えられるので、より細かい権限管理が可能です。認証方式は、Pod IdentityとIRSAの2つから選べます。Pod Identityはnamespaceが固定できる場合に適しており、IRSAはB/Gデプロイなどでnamespaceが変動する場合に向いています。今回は簡単のためにPod Identityを利用する例を示します。
Pod Identityを利用する場合、EKS Pod Identityを設定し、AssumeRoleポリシーは次のように設定します。
{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "pods.eks.amazonaws.com" }, "Action": [ "sts:AssumeRole", "sts:TagSession" ] } ] }
権限とEKS紐づけですが、Pulumi C#での設定例は次の通りです。default namespaceで、test-appというService AccountをPod Identityに紐づける場合の例です。今回はSSM Parameter Storeからシークレットを取得するので、簡単のためAWSSecretsManagerClientReadOnlyAccessとAmazonSSMReadOnlyAccessポリシーを付与しています。検証用に強い権限がついているので、必要なシークレットだけ取得するようにポリシーをカスタマイズしてください。
// Pulumi C# var test = new IamRoleComponent($"{name}-test-app", new () { Parent = this }, new() { AssumeRolePolicy = IamPolicy.GetAssumeRolePolicyJson("pods.eks.amazonaws.com", ["sts:TagSession"]), IamPolicyArg = new IamPolicyArg { PolicyArns = IamPolicy.GetAwsManagedPolicyArns( [ // 強い権限なので必要に応じてカスタマイズしてください "arn:aws:iam::aws:policy/AWSSecretsManagerClientReadOnlyAccess", "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess", ]), }, RoleName = $"{name}-test-app", }); foreach (var item in new[] { new NamespaceServiceAccountMapping("default", "test-app") }) { new PodIdentityAssociation($"{name}-podidentity-assoc-test-app-{item.Namespace}-{item.ServiceAccount}", new() { ClusterName = cluster.Name, RoleArn = test.Role.Arn, Namespace = item.Namespace, ServiceAccount = item.ServiceAccount, DisableSessionTags = false, }); } private record NamespaceServiceAccountMapping(string Namespace, string ServiceAccount);
SSM Parameter Storeに次のようなシークレットを用意しておきます。
foobar: "foobar-value" piyopiyo: "1234567"
これで準備は整いました。AWS provider for the Secrets Store CSI Driverは、DaemonSetとして各ノードにデプロイされています。
AWS Secrets Store CSI Driver providerを利用する
ASCPの基本的な使い方は2つあります。1はESOではできなかったパターン、2はESOのパターンです。
- シークレットをPodのボリュームへ直接マウントするパターン
- シークレットをKubernetes Secretとして同期しPodからKubernetes Secretを参照するパターン
Secret同期はSecretProviderClass定義するのですが、1,2を踏まえた構造は次のようになります。
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: test-app-secretstore namespace: default spec: provider: aws # 1のパターン (2でもこれは必須) parameters: objects: | - objectName: "piyopiyo" objectType: "ssmparameter" - objectName: "foobar" objectType: "ssmparameter" objectAlias: "my-parameter" usePodIdentity: "true" # 2のパターンが必要なら以下を足す secretObjects: - secretName: test-app-secret type: Opaque data: - objectName: "piyopiyo" key: "PIYO" - objectName: "my-parameter" key: "PARAMETER"
これをベースにそれぞれのパターンをみてみましょう。
Podのボリュームへ直接マウントするパターン
ボリュームマウントパターン (SecretProviderClassでsecretsObjectsを定義しない場合) は、次のような特徴があります。
- Podのボリュームとしてシークレットのマウントパスを指定する
- External Secrets Operatorと違って、Kubernetes Secretが生成されない
シークレットと同期するためにSecretProviderClassを用意します。シークレットの同期設定はspec.parametersに定義します。provider: awsを指定し同期設定をしましょう。
usePodIdentity: true: Pod Identityを利用してAWS認証4objectType: ssmparamter: SSM Parameter Storeからシークレットを取得objectName: string: 取得するシークレット名を指定objectAlias: string: マウント時のファイル名をシークレット名から変えたいときに指定(省略可能)
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: test-app-secretstore namespace: default spec: provider: aws parameters: objects: | - objectName: "piyopiyo" objectType: "ssmparameter" - objectName: "foobar" objectType: "ssmparameter" objectAlias: "my-parameter" usePodIdentity: "true"
次にDeploymentを定義します。Podのボリュームとして、Secrets Store CSI Driverのボリュームをマウントすることを頭に入れておきましょう。また、PodIdentityで紐づけたService Accountを指定することで、Pod起動時にSSM Parameter Storeへアクセス認証され、Podがボリュームマウント、起動します。
--- apiVersion: v1 kind: ServiceAccount metadata: name: test-app namespace: default --- kind: Service apiVersion: v1 metadata: name: test-app-svc namespace: default labels: app: test-app spec: selector: app: test-app ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: test-app namespace: default labels: app: test-app spec: replicas: 1 selector: matchLabels: app: test-app template: metadata: labels: app: test-app spec: serviceAccountName: test-app containers: - name: nginx image: nginx:latest ports: - containerPort: 80 name: http resources: limits: memory: "128Mi" cpu: "250m" requests: memory: "128Mi" cpu: "250m" volumeMounts: - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true volumes: - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "test-app-secretstore"
デプロイして、PodとSecretの状態を確認します。ESOと違ってSecretは生成されていないことがわかります。また、volumeにSecrets Store CSI Driverのボリュームがマウントされていることがわかります。
$ kubectl get po NAME READY STATUS RESTARTS AGE test-app-5989fbd45f-rzk2d 1/1 Running 0 3m38s $ kubectl get secret No resources found in default namespace. $ kubectl get po test-app-5989fbd45f-rzk2d -o yaml | kubectl neat apiVersion: v1 kind: Pod metadata: labels: app: test-app pod-template-hash: 5c74d5fd8b name: test-app-5989fbd45f-rzk2d namespace: default spec: containers: - env: - name: AWS_STS_REGIONAL_ENDPOINTS value: regional // 省略... image: nginx:latest name: nginx // 省略... volumeMounts: - mountPath: /mnt/secrets-store name: secrets-store-inline readOnly: true - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-kwk52 readOnly: true - mountPath: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount name: eks-pod-identity-token readOnly: true preemptionPolicy: PreemptLowerPriority priority: 0 serviceAccountName: test-app // 省略... volumes: - name: eks-pod-identity-token projected: sources: - serviceAccountToken: audience: pods.eks.amazonaws.com expirationSeconds: 81586 path: eks-pod-identity-token - csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: test-app-secretstore name: secrets-store-inline // 省略...
Podにマウントされたボリュームを確認して、期待通りシークレットが取得できていることを確認しましょう。
$ kubectl exec -it test-app-5989fbd45f-rzk2d -- /bin/bash root@test-app-5989fbd45f-rzk2d:/# ls /mnt/secrets-store/ my-parameter piyopiyo root@test-app-5989fbd45f-rzk2d:/# cat /mnt/secrets-store/my-parameter foobar-value root@test-app-5989fbd45f-rzk2d:/# cat /mnt/secrets-store/piyopiyo 1234567
いい感じですね。
ESOと違ってKubernetes Secretを生成しない分、攻撃対象領域が減ります。 一方で、何か設定ミスでPodが起動してこない状況になった時は原因を特定しづらい点に注意です。Secretsが生成されるならSecretsの状態を見ることで原因を特定しやすいです。Secretsが生成されない場合、Podのログやイベントから原因を特定しましょう。
Kubernetes Secretへ同期するパターン
シークレット生成パターン (SecretProviderClassでsecretsObjectsを定義する場合) は、次のような特徴があります。
- External Secrets Operatorと同様に、Kubernetes Secretが生成される
Kubernetes Secretsとシークレットと同期するためにSecretProviderClassを用意します。シークレットの同期設定はspec.parameters定義ですが、これに加えて、Kubernetes Secretの同期設定をspec.secretObjects定義に追加します。secretObjectsの定義は次のようになります。
secretName: string: 生成するKubernetes Secret名を指定type: string: 生成するKubernetes Secretのタイプを指定data: []: 生成するKubernetes Secretのデータを指定。objectNameでSecrets Store CSI Driverから取得するシークレット名を指定し、keyでKubernetes Secret内でのキー名を指定
parameters.objects[].objectAliasでエイリアス指定している場合は、secretObjects[].data.objectNameにもエイリアス名を指定する必要があることに注意してください。
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: test-app-secretstore namespace: default spec: provider: aws parameters: objects: | - objectName: "piyopiyo" objectType: "ssmparameter" - objectName: "foobar" objectType: "ssmparameter" objectAlias: "my-parameter" usePodIdentity: "true" secretObjects: - secretName: test-app-secret type: Opaque data: - objectName: "piyopiyo" key: "PIYO" - objectName: "my-parameter" key: "PARAMETER"
次にDeploymentを定義します。先ほどとの違いとして、Podのボリュームマウントは不要で、Podからは生成されたKubernetes Secretを参照します。今回はわかりやすいように、envでKubernetes Secretを個別に参照しています。
apiVersion: apps/v1 kind: Deployment metadata: name: test-app namespace: default labels: app: test-app spec: replicas: 1 selector: matchLabels: app: test-app template: metadata: labels: app: test-app spec: serviceAccountName: test-app containers: - name: nginx image: nginx:latest ports: - containerPort: 80 name: http resources: limits: memory: "128Mi" cpu: "250m" requests: memory: "128Mi" cpu: "250m" volumeMounts: - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true env: - name: PIYO valueFrom: secretKeyRef: name: test-app-secret key: PIYO - name: PARAMETER valueFrom: secretKeyRef: name: test-app-secret key: PARAMETER volumes: - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "test-app-secretstore"
デプロイして、PodとSecretの状態を確認します。ESOと同様に、Secretが生成されていることがわかります。また、PodのenvがKubernetes Secretを参照していることがわかります。
$ kubectl get secret NAME TYPE DATA AGE test-app-secret Opaque 2 3m51s $ kubectl get po NAME READY STATUS RESTARTS AGE test-app-5c74d5fd8b-g2q9c 1/1 Running 0 5m11s $ kubectl get po test-app-5c74d5fd8b-g2q9c -o yaml | kubectl neat apiVersion: v1 kind: Pod metadata: labels: app: test-app pod-template-hash: 5c74d5fd8b name: test-app-5c74d5fd8b-g2q9c namespace: default spec: containers: - env: - name: PIYO valueFrom: secretKeyRef: key: PIYO name: test-app-secret - name: PARAMETER valueFrom: secretKeyRef: key: PARAMETER name: test-app-secret - name: AWS_STS_REGIONAL_ENDPOINTS value: regional // 省略... image: nginx:latest name: nginx // 省略... volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-kwk52 readOnly: true - mountPath: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount name: eks-pod-identity-token readOnly: true // 省略... serviceAccountName: test-app // 省略... volumes: - name: eks-pod-identity-token projected: sources: - serviceAccountToken: audience: pods.eks.amazonaws.com expirationSeconds: 81586 path: eks-pod-identity-token // 省略...
Podのenvを確認して、期待通りシークレットが取得できていることを確認しましょう。
$ kubectl exec -it test-app-5c74d5fd8b-g2q9c -- /bin/bash root@test-app-5c74d5fd8b-g2q9c:/# env | grep PIYO PIYO=1234567 root@test-app-5c74d5fd8b-g2q9c:/# env | grep PARAMETER PARAMETER=foobar-value
いい感じですね。ESO同様にKubernetes Secretを生成するので、ESOからの移行もスムーズに行えます。
TIPS: Secrets同期されずエラーが出るとき
デフォルトのEKS Addon構成だと、Secretsを作成するオプションsyncSecret: falseがデフォルトであるため、Secretsを生成できません。
この場合、Secrets APIにアクセスできないことがログでわかります。
$ stern secrets-store-csi-driver secrets-store-csi-driver-pqpxz secrets-store I1203 07:17:15.486011 1 reflector.go:424]"pkg/mod/k8s.io/client-go@v0.26.4/tools/cache/reflector.go:169: failed to list *v1.Secret: secrets is forbidden: User \"system:serviceaccount:aws-secrets-manager:secrets-store-csi-driver\" cannot list resource \"secrets\" in API group \"\" at the cluster scope\n" secrets-store-csi-driver-pqpxz secrets-store E1203 07:17:15.486080 1 reflector.go:140]"pkg/mod/k8s.io/client-go@v0.26.4/tools/cache/reflector.go:169: Failed to watch *v1.Secret: failed to list *v1.Secret: secrets is forbidden: User \"system:serviceaccount:aws-secrets-manager:secrets-store-csi-driver\" cannot list resource \"secrets\" in API group \"\" at the cluster scope\n"
原因は、CSIが利用するClusterRoleに権限がついていないためです。
$ kubectl get clusterrole aws-secrets-store-csi-driver-provider-cluster-role -o yaml | kubectl neat apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app: aws-secrets-store-csi-driver-provider app.kubernetes.io/instance: aws-secrets-store-csi-driver-provider app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: aws-secrets-store-csi-driver-provider helm.sh/chart: aws-secrets-store-csi-driver-provider-2.1.1 name: aws-secrets-store-csi-driver-provider-cluster-role rules: - apiGroups: - "" resources: - serviceaccounts/token verbs: - create - apiGroups: - "" resources: - serviceaccounts verbs: - get - apiGroups: - "" resources: - pods verbs: - get - apiGroups: - "" resources: - nodes verbs: - get $ kubectl get clusterrole secretproviderclasses-role -o yaml | kubectl neat apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app: secrets-store-csi-driver app.kubernetes.io/instance: aws-secrets-store-csi-driver-provider app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: secrets-store-csi-driver app.kubernetes.io/version: 1.5.4 helm.sh/chart: secrets-store-csi-driver-1.5.4 name: secretproviderclasses-role rules: - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - "" resources: - pods verbs: - get - list - watch - apiGroups: - secrets-store.csi.x-k8s.io resources: - secretproviderclasses verbs: - get - list - watch - apiGroups: - secrets-store.csi.x-k8s.io resources: - secretproviderclasspodstatuses verbs: - create - delete - get - list - patch - update - watch - apiGroups: - secrets-store.csi.x-k8s.io resources: - secretproviderclasspodstatuses/status verbs: - get - patch - update - apiGroups: - storage.k8s.io resourceNames: - secrets-store.csi.k8s.io resources: - csidrivers verbs: - get - list - watch
対応方法は簡単で、EKS AddonのConfiguration CustomizationにsyncSecret定義を追加します。
{"secrets-store-csi-driver":{"syncSecret":{"enabled":true}}}


これで同期用のClusterRolesecretprovidersyncing-roleとClusterRoleBindingssecretprovidersyncing-rolebindingが作られ、ASCPがSecrets APIへアクセスできるようになります。
$ kubectl get clusterrole secretprovidersyncing-role -o yaml | kubectl neat apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: app: secrets-store-csi-driver app.kubernetes.io/instance: aws-secrets-store-csi-driver-provider app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: secrets-store-csi-driver app.kubernetes.io/version: 1.5.4 helm.sh/chart: secrets-store-csi-driver-1.5.4 name: secretprovidersyncing-role rules: - apiGroups: - "" resources: - secrets verbs: - create - delete - get - list - patch - update - watch $ kubectl get clusterrolebinding secretprovidersyncing-rolebinding -o yaml | kubectl neat apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app: secrets-store-csi-driver app.kubernetes.io/instance: aws-secrets-store-csi-driver-provider app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: secrets-store-csi-driver app.kubernetes.io/version: 1.5.4 helm.sh/chart: secrets-store-csi-driver-1.5.4 name: secretprovidersyncing-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: secretprovidersyncing-role subjects: - kind: ServiceAccount name: secrets-store-csi-driver namespace: aws-secrets-manager
まとめ
EKS Addonになったことで導入、運用が劇的に楽になり、ESOからの移行も十分に検討できるレベルになりました。変に抽象化せず、awsならaws用のSecrets CSI Driver、Google Cloudなら、Azureなら... と個別のクラウド別に別リポジトリで管理されているのも好印象です。
認証はPod IdentityとIRSAの両方に対応しているので、namespaceが決め打ちできるコントローラー系はPod IdentityでOK。ブランチをnamespaceで分けてたり、B/Gデプロイなどでnamespaceが固定できないアプリはIRSAで認証すればよいでしょう。懸念は、コントローラー認証ではなくクライアント認証になるので、APIレートリミットに引っかかりやすくはなりそうです。EKS Addonで導入時は以下の追加コンフィグだけ設定すれば、OSS helm版やExternal Secrets Operatorでやりたいパターンが網羅できます。
{"secrets-store-csi-driver":{"syncSecret":{"enabled":true}}}
なお、AWS的にはセキュアのシナリオでは、AWS Secrets Manager Agentから取得してインメモリに保持しろというのは公式見解としてあるようです。
Security best practice recommends caching secrets in memory where possible. If you prefer to adopt the native Kubernetes experience, please follow the steps in this blog post. If you prefer to cache secrets in memory, we recommend using the AWS Secrets Manager Agent.
ASCPはKubernetesが提供するCSI Driverをうまく使っておりESOからの移行もスムーズにできるので、EKSでのシークレット管理はこれに落ち着きそうな気がします。
参考資料
AWS公式ドキュメント、ブログ
- Amazon EKS add-ons now supports the AWS Secrets Store CSI Driver provider | AWS
- Amazon EKS 用に AWS Secrets and Configuration Provider CSI を Pod Identity と使用する | AWS Systems Manager
- How to use the Secrets Store CSI Driver provider Amazon EKS add-on with Secrets Manager | AWS Security Blog
- Amazon EKS アドオンの高度な設定 | Amazon Web Services ブログ
GitHubリポジトリ
- external-secrets/external-secrets | GitHub
- aws/secrets-store-csi-driver-provider-aws | GitHub
- aws-ebs-csi-driver/docs/install.md at master · kubernetes-sigs/aws-ebs-csi-driver | GitHub
- Documentation on how to use a secret as an env var · Issue #286 · aws/secrets-store-csi-driver-provider-aws | GitHub
- Sync AWS Secrets manager with Kubernetes secrets on local not working when using minikube and localstack · aws/secrets-store-csi-driver-provider-aws · Discussion #230 | GitHub
- もともとkubernetes-extenal-secrets自体、itscontained/secret-managerとContainerSolutions/externalsecret-operatorを統合するプロジェクトという由来があります↩
- いわゆるinline volume↩
- コミュニティ提供のEKS Addonは、新クラスターバージョンが提供されてからしばらくたたないと対応しない場合が多いためクラスターバージョンの更新タイミングに影響を与えてしまう↩
-
IRSAを利用する場合、
usePodIdentity: falseに設定します。↩