先日、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におけるシークレット管理の背景
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で食っていけない状況が続いています。
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#の例を示します。
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
}
}
})
}
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ポリシーを付与しています。検証用に強い権限がついているので、必要なシークレットだけ取得するようにポリシーをカスタマイズしてください。
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
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"
これをベースにそれぞれのパターンをみてみましょう。
Podのボリュームへ直接マウントするパターン
ボリュームマウントパターン (SecretProviderClassでsecretsObjectsを定義しない場合) は、次のような特徴があります。
- Podのボリュームとしてシークレットのマウントパスを指定する
- External Secrets Operatorと違って、Kubernetes Secretが生成されない
シークレットと同期するためにSecretProviderClassを用意します。シークレットの同期設定はspec.parametersに定義します。provider: awsを指定し同期設定をしましょう。
usePodIdentity: true: Pod Identityを利用してAWS認証4
objectType: 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公式ドキュメント、ブログ
GitHubリポジトリ