VaultとExternal Secrets Operatorを連携する

External Secrets OperatorHashiCorp Vault など
様々なシークレット管理システム上のシークレットから
Kubernetesの Secret を作成してくれるソフトウェアです。
Gitにsecretをアップしたくない時に使えるものですね。

前回 手元のラズパイk8sにvault serverをインストールしたので、
それとExternal Secrets Operatorと連携したいと思います。

環境

確認環境はこちら

ubuntu@k8s1:~$ k get node -o wide
NAME   STATUS   ROLES                       AGE    VERSION           INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
k8s1   Ready    control-plane,etcd,master   326d   v1.28.10+rke2r1   192.168.0.51   <none>        Ubuntu 24.04 LTS     6.8.0-1028-raspi   containerd://1.7.11-k3s2
k8s2   Ready    worker                      203d   v1.28.10+rke2r1   192.168.0.52   <none>        Ubuntu 24.04.1 LTS   6.8.0-1013-raspi   containerd://1.7.11-k3s2
k8s3   Ready    worker                      326d   v1.28.10+rke2r1   192.168.0.53   <none>        Ubuntu 24.04 LTS     6.8.0-1013-raspi   containerd://1.7.11-k3s2

vaultとExternal Secrets Operatorは同じクラスタで起動しますが、
vaultはingressで外部公開しているのでそちらを通してアクセスします。

ubuntu@k8s1:~$ k get pod,ingress -n vault
NAME                                        READY   STATUS    RESTARTS   AGE
pod/vault-0                                 1/1     Running   0          5d11h
pod/vault-1                                 1/1     Running   0          5d11h
pod/vault-agent-injector-6665f9cbc6-8k8sh   1/1     Running   0          5d11h

NAME                              CLASS    HOSTS                       ADDRESS                     PORTS   AGE
ingress.networking.k8s.io/vault   <none>   vault.tsuchinokometal.com   192.168.0.52,192.168.0.53   80      5d11h

helmでExternal Secrets Operatorをインストール

こちら を参考にExternal Secrets Operatorをインストールします。

ubuntu@k8s1:~$ helm repo add external-secrets https://charts.external-secrets.io
ubuntu@k8s1:~$ helm repo update
ubuntu@k8s1:~$ helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace
NAME: external-secrets
LAST DEPLOYED: Thu Jun  5 20:54:48 2025
NAMESPACE: external-secrets
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
external-secrets has been deployed successfully in namespace external-secrets!

In order to begin using ExternalSecrets, you will need to set up a SecretStore
or ClusterSecretStore resource (for example, by creating a 'vault' SecretStore).

More information on the different types of SecretStores and how to configure them
can be found in our Github: https://github.com/external-secrets/external-secrets

ubuntu@k8s1:~$ helm list -n external-secrets
NAME            	NAMESPACE       	REVISION	UPDATED                               	STATUS  	CHART                  	APP VERSION
external-secrets	external-secrets	1       	2025-06-05 20:54:48.13443726 +0900 JST	deployed	external-secrets-0.17.0	v0.17.0 

ubuntu@k8s1:~$ k get pod -n external-secrets
NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets-75ccf7ffd-xh9lw                    1/1     Running   0          86s
external-secrets-cert-controller-6cdbf89fff-l7dbr   1/1     Running   0          86s
external-secrets-webhook-db4d9b557-rp9rf            1/1     Running   0          86s

特に設定を変更することなく問題なく起動しました。

vaultと連携

vaultとの連携は こちら を参考に進めます。

まずはvaultと認証するためのリソースを作成します。
認証方法は こちら で試したkubernetes認証を流用します。

apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "http://vault.tsuchinokometal.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "my-role"
          serviceAccountRef:
            name: "my-sa"
          secretRef:
            name: "my-app"
            key: "password"

vault側の設定は以下の通りですので、そちらに合わせてSecretStoreを作成しました。

ubuntu@k8s1:~$ vault kv get secret/my-app
=== Secret Path ===
secret/data/my-app

======= Metadata =======
Key                Value
---                -----
created_time       2025-05-31T09:14:45.104709193Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    supersecret!

ubuntu@k8s1:~$ vault read auth/kubernetes/role/my-role
Key                                         Value
---                                         -----
alias_name_source                           serviceaccount_uid
bound_service_account_names                 [my-sa]
bound_service_account_namespace_selector    n/a
bound_service_account_namespaces            [default]
policies                                    [my-policy]
token_bound_cidrs                           []
token_explicit_max_ttl                      0s
token_max_ttl                               0s
token_no_default_policy                     false
token_num_uses                              0
token_period                                0s
token_policies                              [my-policy]
token_ttl                                   20m
token_type                                  default
ttl                                         20m

作成すると以下の状態になりました。

ubuntu@k8s1:~$ k get SecretStore
NAME            AGE   STATUS   CAPABILITIES   READY
vault-backend   23s   Valid    ReadWrite      True

次にこのSecretStoreを使ってkubernetes secretを作成します。

マニフェストは以下のように作成してみました。

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: "15s"
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: my-secret
  data:
  - secretKey: password
    remoteRef:
      key: my-app
      property: password

target.nameでsecretの名前が指定できるようですね。

secretが作成できたら確認してみます。

ubuntu@k8s1:~$ k get secret my-secret -o jsonpath={.data.password} | base64 -d
supersecret!

連携できたようですね!

シークレットの更新

新規追加した時と同じコマンドで上書きしちゃえばいいようです。

ubuntu@k8s1:~$ vault kv put secret/my-app password=ultrasecret!
=== Secret Path ===
secret/data/my-app

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-07T03:41:41.705997865Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

ubuntu@k8s1:~$ vault kv get secret/my-app
=== Secret Path ===
secret/data/my-app

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-07T03:41:41.705997865Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

====== Data ======
Key         Value
---         -----
password    ultrasecret!

自動でsecretが更新されました。

ubuntu@k8s1:~$ k get secret my-secret -o jsonpath={.data.password} | base64 -d
ultrasecret!

シークレットのロールバック

ロールバックコマンド で更新したシークレットを復元できます。

ubuntu@k8s1:~$ vault kv rollback -version=1 secret/my-app
Key                Value
---                -----
created_time       2025-06-07T04:03:59.520397652Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            3

ubuntu@k8s1:~$ vault kv get secret/my-app
=== Secret Path ===
secret/data/my-app

======= Metadata =======
Key                Value
---                -----
created_time       2025-06-07T04:03:59.520397652Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            3

====== Data ======
Key         Value
---         -----
password    supersecret!

バージョンが加算されて復元されるようですね。

問題なくsecret側もロールバックしています。

ubuntu@k8s1:~$ k get secret my-secret -o jsonpath={.data.password} | base64 -d
supersecret!