0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HashiCorp VaultとK8sの外部連携チュートリアルをやってみた

0
Posted at

こんにちは、この記事は HashiCorp Japan Advent Calendar 2025 の2日目の記事です。

この記事では下記のチュートリアル (Vault Agent injector を使って pod にシークレットをインジェクト) をやってみた時の内容を書きたいと思います。

スムーズにいっていると油断していたら、最後の方になってバージョンの違いで少しつまづいたので共有記事になります。

環境は Windows 11 WSL (Ubuntu 24.04)です。

チュートリアル: Integrate Kubernetes with an external Vault cluster

チュートリアルで使用しているレポ: https://github.com/hashicorp-education/learn-vault-external-kubernetes

Start Vault

まず、ローカル PC で Vault サーバ (0.0.0.0:8200 でリッスン、ルートトークンがrootdevモード) を起動します。以降使用する Minikube と Pod からのアクセスのためになります。

vault server -dev -dev-root-token-id root -dev-listen-address 0.0.0.0:8200

別のコンソールから Vault へアクセスします。

$ export VAULT_ADDR=http://0.0.0.0:8200

$ vault login
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                root
token_accessor       ZwYgP4joLX31R3KjHH9SiE3V
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

テスト用の Key-Value (KV) シークレットを作成します。

$ vault kv put secret/devwebapp/config username='giraffe' password='salsa'
======== Secret Path ========
secret/data/devwebapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-15T05:32:02.758046563Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

$  vault kv get -format=json secret/devwebapp/config | jq ".data.data"
{
  "password": "salsa",
  "username": "giraffe"
}

Start minikube

Minikube を起動します。

$ curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  133M  100  133M    0     0  14.7M      0  0:00:09  0:00:09 --:--:-- 15.9M

$ sudo install minikube-linux-amd64 /usr/local/bin/minikube && rm minikube-linux-amd64
[sudo] password for ubuntu:
$ minikube start
😄  minikube v1.37.0 on Ubuntu 24.04 (kvm/amd64)
✨  Automatically selected the docker driver. Other choices: none, ssh
📌  Using Docker driver with root privileges
👍  Starting "minikube" primary control-plane node in "minikube" cluster
🚜  Pulling base image v0.0.48 ...
💾  Downloading Kubernetes v1.34.0 preload ...
    > preloaded-images-k8s-v18-v1...:  337.07 MiB / 337.07 MiB  100.00% 9.45 Mi
    > gcr.io/k8s-minikube/kicbase...:  488.51 MiB / 488.52 MiB  100.00% 10.38 M
🔥  Creating docker container (CPUs=2, Memory=3900MB) ...
🐳  Preparing Kubernetes v1.34.0 on Docker 28.4.0 ...
🔗  Configuring bridge CNI (Container Networking Interface) ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

起動確認します。

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

$ kubectl get node
NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   34s   v1.34.0
$ docker ps
CONTAINER ID   IMAGE                                 COMMAND                  CREATED         STATUS         PORTS                                                                                                                                  NAMES
111ffca9dd5f   gcr.io/k8s-minikube/kicbase:v0.0.48   "/usr/local/bin/entr…"   3 minutes ago   Up 3 minutes   127.0.0.1:32768->22/tcp, 127.0.0.1:32769->2376/tcp, 127.0.0.1:32770->5000/tcp, 127.0.0.1:32771->8443/tcp, 127.0.0.1:32772->32443/tcp   minikube

ローカルで起動しているため、アクセス先のIPを確認します。

$ docker network inspect minikube
[
    {
        "Name": "minikube",
        "Id": "aa9a1f5e2ff8481e1d0c8e739e38c5f14c44733040d5f9cf5574e2e889a6f7a0",
        "Created": "2025-12-15T14:37:37.121399021+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.49.0/24",
                    "Gateway": "192.168.49.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "111ffca9dd5f242fd82e5e05fb9f0c6251d3a2b55ce6ebcc7e9597e86dc7d1fa": {
                "Name": "minikube",
                "EndpointID": "b100fec7121f6a79eae14ba79df7bd2c3c18fc2aea3fbd507acc821b04c40a36",
                "MacAddress": "46:3f:56:56:4c:43",
                "IPv4Address": "192.168.49.2/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "--icc": "",
            "--ip-masq": "",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {
            "created_by.minikube.sigs.k8s.io": "true",
            "name.minikube.sigs.k8s.io": "minikube"
        }
    }
]

Determine the Vault address

A service bound to all networks on the host, as you configured Vault, is addressable by pods in minikube's cluster by sending requests to the gateway address of the Kubernetes cluster.

$ minikube ssh
docker@minikube:~$ dig +short host.docker.internal
docker@minikube:~$

私の環境では何も出力がなかったため、以下の通りゲートウェイのIPを確認します。

$ ip route show default | awk '{print $3}'
192.168.49.1

こちらの IP に対して、Vault へコマンドを投げて通ることを確認します。

$ curl http://192.168.49.1:8200/v1/sys/seal-status | python3 -m json.t
ool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   302  100   302    0     0   212k      0 --:--:-- --:--:-- --:--:--  294k
{
    "type": "shamir",
    "initialized": true,
    "sealed": false,
    "t": 1,
    "n": 1,
    "progress": 0,
    "nonce": "",
    "version": "1.20.3+ent",
    "build_date": "2025-08-27T10:53:07Z",
    "migration": false,
    "cluster_name": "vault-cluster-d0a810a0",
    "cluster_id": "cbc7b744-3d0f-0dc3-4021-f1b271966a75",
    "recovery_seal": false,
    "storage_type": "inmem"
}

環境変数へ格納します。

EXTERNAL_VAULT_ADDR=$(minikube ssh "ip route show default" | awk '{print $3}' | tr -d '\r')
$ echo $EXTERNAL_VAULT_ADDR
192.168.49.1

Deploy application with hard-coded Vault address

以上の環境で、この章では Pod から Vault へシークレットの取得ができることを確認します。

image.png

Minikube 側で Service Account を作成します。

$ kubectl create sa internal-app
serviceaccount/internal-app created

テスト用 Pod を作成します。

$ cat > devwebapp.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: devwebapp
  labels:
    app: devwebapp
spec:
  serviceAccountName: internal-app
  containers:
    - name: app
      image: burtlo/devwebapp-ruby:k8s
      env:
      - name: VAULT_ADDR
        value: "http://$EXTERNAL_VAULT_ADDR:8200"
      - name: VAULT_TOKEN
        value: root
EOF
$ kubectl apply --filename devwebapp.yaml
pod/devwebapp created

$ kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
devwebapp   1/1     Running   0          49s

Pod のローカルホストへ curl し、アプリ側から Vault へのシークレット取得ができました。

$ kubectl exec devwebapp -- curl -s localhost:8080 ; echo
{"password"=>"salsa", "username"=>"giraffe"}

Deploy service and endpoints to address an external Vault

この章では Service と EndpointSlice リソースを経由して、外部の Vault へ接続することで、シークレットの取得をします。
image.png

$ cat > external-vault.yaml <<EOF
---
apiVersion: v1
kind: Service
metadata:
  name: external-vault
  namespace: default
spec:
  ports:
  - protocol: TCP
    port: 8200
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-vault
subsets:
  - addresses:
      - ip: $EXTERNAL_VAULT_ADDR
    ports:
      - port: 8200
EOF
$ kubectl apply --filename external-vault.yaml
service/external-vault created
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
endpoints/external-vault created

ドキュメント記載のマニュフェストでは Endpoints は deprecated in v1.33+ と出力されました (2025年12月時点)。そのため、以下ではEndpointSliceを使用しています。

$ cat > external-vault-ep-slice.yaml <<EOF
---
apiVersion: v1
kind: Service
metadata:
  name: external-vault
  namespace: default
spec:
  ports:
  - protocol: TCP
    port: 8200
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: external-vault-1
  labels:
    kubernetes.io/service-name: external-vault
addressType: IPv4
ports:
  - port: 8200
    protocol: TCP
endpoints:
  - addresses:
      - $EXTERNAL_VAULT_ADDR
EOF
$ kubectl apply -f external-vault-ep-slice.yaml
service/external-vault created
endpointslice.discovery.k8s.io/external-vault-1 created

$ kubectl get endpointslice
NAME               ADDRESSTYPE   PORTS   ENDPOINTS      AGE
external-vault-1   IPv4          8200    192.168.49.1   14s
kubernetes         IPv4          8443    192.168.49.2   56m

Pod からサービスを経由し、外部の Vault へアクセスします。

$ kubectl exec devwebapp -- curl -s http://external-vault:8200/v1/sys/seal-status | jq
{
  "type": "shamir",
  "initialized": true,
  "sealed": false,
  "t": 1,
  "n": 1,
  "progress": 0,
  "nonce": "",
  "version": "1.20.3+ent",
  "build_date": "2025-08-27T10:53:07Z",
  "migration": false,
  "cluster_name": "vault-cluster-d0a810a0",
  "cluster_id": "cbc7b744-3d0f-0dc3-4021-f1b271966a75",
  "recovery_seal": false,
  "storage_type": "inmem"
}

Pod 内のアプリから Vault へService経由でアクセス確認をするためPodをたて、動作確認します。

$ kubectl apply --filename=pod-devwebapp-through-service.yaml
pod/devwebapp-through-service created

$ kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
devwebapp                   1/1     Running   0          32m
devwebapp-through-service   1/1     Running   0          6s
$ kubectl exec devwebapp-through-service -- curl -s localhost:8080 ; echo
{"password"=>"salsa", "username"=>"giraffe"}

Install the Vault Helm chart configured to address an external Vault

この章では、Helmを使用してVault Agent Injector サービスを作成します。

$ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" already exists with the same configuration, skipping
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install vault hashicorp/vault \
    --set "global.externalVaultAddr=http://external-vault:8200"
NAME: vault
LAST DEPLOYED: Mon Dec 15 15:49:16 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

https://developer.hashicorp.com/vault/docs

Your release is named vault. To learn more about the release, try:

  $ helm status vault
  $ helm get manifest vault

Default ネームスペースに、Vault Agent Injector のリソースが作成されました。

$ kubectl get all
NAME                                        READY   STATUS    RESTARTS   AGE
pod/devwebapp                               1/1     Running   0          48m
pod/devwebapp-through-service               1/1     Running   0          16m
pod/vault-agent-injector-7c555bc6b8-7hn2d   1/1     Running   0          4m16s

NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/external-vault             ClusterIP   10.106.96.108   <none>        8200/TCP   18m
service/kubernetes                 ClusterIP   10.96.0.1       <none>        443/TCP    75m
service/vault-agent-injector-svc   ClusterIP   10.106.63.132   <none>        443/TCP    4m16s

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/vault-agent-injector   1/1     1            1           4m16s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/vault-agent-injector-7c555bc6b8   1         1         1       4m16s
$ kubectl get sa
NAME                   SECRETS   AGE
default                0         76m
internal-app           0         50m
vault                  0         4m56s
vault-agent-injector   0         4m56s

$ kubectl describe sa vault
Name:                vault
Namespace:           default
Labels:              app.kubernetes.io/instance=vault
                     app.kubernetes.io/managed-by=Helm
                     app.kubernetes.io/name=vault
                     helm.sh/chart=vault-0.31.0
Annotations:         meta.helm.sh/release-name: vault
                     meta.helm.sh/release-namespace: default
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

Kubernetes 1.24+ only: The name of the mountable secret is displayed in Kubernetes 1.23. In Kubernetes 1.24+, the token is not created automatically, and you must create it explicitly.

とあるので、type: kubernetes.io/service-account-tokenを指定した Vault Service Account 用のシークレットを作成します。

$ cat > vault-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: vault-token-g955r
  annotations:
    kubernetes.io/service-account.name: vault
type: kubernetes.io/service-account-token
EOF
$ kubectl apply -f vault-secret.yaml
secret/vault-token-g955r created

VAULT_HELM_SECRET_NAME にシークレット名を格納します。

$ VAULT_HELM_SECRET_NAME=$(kubectl get secrets --output=json | jq -r '.items[].metadata | select(.name|startswith("vault-token-")).name')

$ echo $VAULT_HELM_SECRET_NAME
vault-token-g955r

内容を確認します。

$ kubectl describe secret $VAULT_HELM_SECRET_NAME
Name:         vault-token-g955r
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: vault
              kubernetes.io/service-account.uid: 7199ff37-d17e-4586-af5d-ae4228424e6c

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1111 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6Inc4dnBsbG9rcHNJUUpaN1hudkV3eW9FVnpjY0VyX2lZS2hPRndwT1Awb3cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InZhdWx0LXRva2VuLWc5NTVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzE5OWZmMzctZDE3ZS00NTg2LWFmNWQtYWU0MjI4NDI0ZTZjIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6dmF1bHQifQ.HBAQMr2arOOx5iv43jwWoeDptNUpDtZhjhCtLf7Noedjx2nBRb9_vF_2RlpyD3ZZp7y26yRaZ7wUGHGXzTgh4siOYXTcEXP0_Lrucrkm9sGNkOYHpfePo32GN6DeAKudgKcZcSR52dRXEyL46v0VRR81wOnzNPfLVDSAKEs8LYdWkNZV0zc17vDw0VlnYVzDKtPsd-LUVc3VgzRKgtbokVy4jstl4S2y2iHHQFMPKzqY9Lk6s4mJ2ndkTOAGY6w1qCMpdpH9lFcoIx1g1WbUFWrSsf9I2htDa0hC-7MOpZcSDl2ol8EdhPf8riS7PAIlGh_YtS6lGQPHP3yeihoU_g

以下でtokenを使用して Kubernetes authentication を設定します。

Configure Kubernetes authentication

image.png

ごちゃごちゃした図になってしまいましたが、まず認証のために Kubernetes authentication を有効化します。その後、シークレットへのアクセスコントロールをするために、policyroleを定義します。最後に pod 側でアノテーションとして使用するroleなどを指定します。
順番に見ていきます。

$ vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

先ほど作成した、シークレットからtoken部分を取得し、変数へ格納しておきます。

$ TOKEN_REVIEW_JWT=$(kubectl get secret $VAULT_HELM_SECRET_NAME --output='go-template={{ .data.token }}' | base64 --decode)

$ echo $TOKEN_REVIEW_JWT
eyJhbGciOiJSUzI1NiIsImtpZCI6Inc4dnBsbG9rcHNJUUpaN1hudkV3eW9FVnpjY0VyX2lZS2hPRndwT1Awb3cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InZhdWx0LXRva2VuLWc5NTVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzE5OWZmMzctZDE3ZS00NTg2LWFmNWQtYWU0MjI4NDI0ZTZjIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6dmF1bHQifQ.HBAQMr2arOOx5iv43jwWoeDptNUpDtZhjhCtLf7Noedjx2nBRb9_vF_2RlpyD3ZZp7y26yRaZ7wUGHGXzTgh4siOYXTcEXP0_Lrucrkm9sGNkOYHpfePo32GN6DeAKudgKcZcSR52dRXEyL46v0VRR81wOnzNPfLVDSAKEs8LYdWkNZV0zc17vDw0VlnYVzDKtPsd-LUVc3VgzRKgtbokVy4jstl4S2y2iHHQFMPKzqY9Lk6s4mJ2ndkTOAGY6w1qCMpdpH9lFcoIx1g1WbUFWrSsf9I2htDa0hC-7MOpZcSDl2ol8EdhPf8riS7PAIlGh_YtS6lGQPHP3yeihoU_g

こちらは Kubernetes CA 証明書です。

$ KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)

$ echo $KUBE_CA_CERT
-----BEGIN CERTIFICATE----- MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p a3ViZUNBMB4XDTI1MTIxNDA1Mzc1MVoXDTM1MTIxMzA1Mzc1MVowFTETMBEGA1UE AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANim uAlvtMYzklT6LyHMSRSXxbJn60y4Ns16PljOvo/1Mv8zUHcN597mUhuRr2WrwFRw i5lGbZyPRQ2xx6zQCfIhI5pPfmFgioINpZEm+t9b2cU4fwRaSE7BSESRLY14NiNS 9GhJe30sSVzk7ItkQ/cm0PvBpiniZpkItJqG1NMMSIbk/vxIkDp3PEhTyK5opk1h dy+/VPqrD/4n6zug5045Cpt4X0IjS6Cw9KRsP3IlWNR96eEnqcHLdjCFexRoEdpM FL+dcyEtKM6WIUX5GcGtsWHElbz9TpE01vL2w+16uw1nIIn2w0lhJBd5CHWL1fr/ ldg5wQAUXW6EHbbY1XECAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW BBTAireeIjb2hCHa4K4yvziv8YSwDzANBgkqhkiG9w0BAQsFAAOCAQEAk4tfFj4D 0Ly6+w9G4icFjFUM0NqnXlxumopUrX8voLy3ydkwAJC8x76dv+lPUffJBaDrbfjt tLWIk32rtH2JsMpr4QAsf+HPcvPIQ2RtWM8f0cze9zWtHlkhAnMzpiRGiZqOeVbH a/JQsnIH9lrDfZGNVrzLxuLw89ZM+gRCvAwT5pZICHA/TZOOoCR/7WRK+ERZsTT2 9BMQdch8PWhiDqezCxHxXsTyVrCkKpp7ucL5xHK+5iMslrF4XC0yHlfYgBqO+ZRA URYMygs5ecebfZx7owWh3w9JeZ8ljJkTOnDc2upGvB/ZWh5Nqkqo0f3h0Y9VJmd7 +1PVhzqR2bGs8Q== -----END CERTIFICATE-----

ローカル環境での Kubernetes ホスト URL を取得します。

$ KUBE_HOST=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.server}')

$ echo $KUBE_HOST
https://127.0.0.1:32776

以上の情報と、service account issuer name を使って Kubernetes authentication method を設定します。

$ vault write auth/kubernetes/config \
     token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
     kubernetes_host="$KUBE_HOST" \
     kubernetes_ca_cert="$KUBE_CA_CERT" \
     issuer="https://kubernetes.default.svc.cluster.local"
Success! Data written to: auth/kubernetes/config

チュートリアルの最初で作成したシークレット (path: secret/data/devwebapp/config) に対して、Vault クライアントへ Read 権限を設定します。
まず、policy を作成します。

$ vault policy write devwebapp - <<EOF
path "secret/data/devwebapp/config" {
  capabilities = ["read"]
}
EOF
Success! Uploaded policy: devwebapp

devweb-app の Kubernetes authentication role を作成します。
このロールは、Kubernetes のサービスアカウントinternal-appとネームスペースdefaultを先ほどの Vault ポリシーdevwebappに関連付けます。認証後に返されるトークンは24時間有効です。

$ vault write auth/kubernetes/role/devweb-app \
     bound_service_account_names=internal-app \
     bound_service_account_namespaces=default \
     policies=devwebapp \
     ttl=24h
WARNING! The following warnings were returned from Vault:

  * Role devweb-app does not have an audience. In Vault v1.21+, specifying an
  audience on roles will be required.
  • Role devweb-app does not have an audience. In Vault v1.21+, specifying an
    audience on roles will be required.

と出力されましたが、いったん作業は進めそうです。
後ほど確認しますが、vault-agent-initのコンテナを見ると以下のような出力がされていました。

$ kubectl logs devwebapp-with-annotations -c vault-agent-init
==> Note: Vault Agent version does not match Vault server version. Vault Agent version: 1.20.4, Vault server version: 1.20.3+ent
==> Vault Agent started! Log data will stream in below:

==> Vault Agent configuration:

           Api Address 1: http://bufconn
                     Cgo: disabled
               Log Level: info
                 Version: Vault v1.20.4, built 2025-09-23T13:22:38Z
             Version Sha: 55bd8f18c6c84aa89fdede4850a622c57f03bd7e

2025-12-15T09:52:27.753Z [INFO]  agent.sink.file: creating file sink
2025-12-15T09:52:27.754Z [INFO]  agent.sink.file: file sink configured: path=/home/vault/.vault-token mode=-rw-r----- owner=100 group=1000
2025-12-15T09:52:27.757Z [INFO]  agent.template.server: starting template server
2025-12-15T09:52:27.757Z [INFO]  agent.sink.server: starting sink server
2025-12-15T09:52:27.757Z [INFO]  agent.exec.server: starting exec server
2025-12-15T09:52:27.757Z [INFO]  agent.exec.server: no env templates or exec config, exiting
2025-12-15T09:52:27.758Z [INFO]  agent.auth.handler: starting auth handler
2025-12-15T09:52:27.758Z [INFO]  agent.auth.handler: authenticating
2025-12-15T09:52:27.759Z [INFO]  agent: (runner) creating new runner (dry: false, once: false)
2025-12-15T09:52:27.761Z [ERROR] agent.auth.handler: error authenticating:
  error=
  | Error making API request.
  |
  | URL: PUT http://external-vault:8200/v1/auth/kubernetes/login
  | Code: 403. Errors:
  |
  | * invalid audience (aud) claim: audience claim does not match any expected audience
   backoff=910ms
2025-12-15T09:52:27.762Z [INFO]  agent: (runner) creating watcher

audience が期待されているものと合わないので拒否したとのことです。

Inject secrets into the pod

agent-inject-secret-FILEPATHは、/vault/secretsディレクトリに書き込まれるファイルcredentials.txtのパスにプレフィックスを付けます。値は、Vault で定義されたシークレットへのパスです。

Vault Agent Injector は、Pod や Deployment に特定のアノテーションが設定されている場合のみ、変更を行います。
そのため、以下のアノテーションを付与した Pod を作成します。

pod-devwebapp-with-annotations-up1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: devwebapp-with-annotations
  labels:
    app: devwebapp-with-annotations
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "devweb-app"
    vault.hashicorp.com/agent-inject-secret-credentials.txt: "secret/data/devwebapp/config"
spec:
  serviceAccountName: internal-app
  containers:
    - name: app
      image: burtlo/devwebapp-ruby:k8s
$ kubectl apply -f pod-devwebapp-with-annotations-up1.yaml
pod/devwebapp-with-annotations created

$ kubectl get pod
NAME                                    READY   STATUS    RESTARTS   AGE
devwebapp                               1/1     Running   0          4h14m
devwebapp-through-service               1/1     Running   0          3h42m
devwebapp-with-annotations              2/2     Running   0          3s
vault-agent-injector-7c555bc6b8-7hn2d   1/1     Running   0          3h30m
$ kubectl exec -it devwebapp-with-annotations -c app -- cat /vault/secrets/credentials.txt
data: map[password:salsa username:giraffe]
metadata: map[created_time:2025-12-15T05:32:02.758046563Z custom_metadata:<nil> deletion_time: destroyed:false version:1]

となり、Vault からデータはとれていそうです。

ここからは、Audience を確認して再度やってみて、挙動と手順の確認をしました。
参考記事: https://support.hashicorp.com/hc/en-us/articles/34458984727443-Audience-parameter-usage-with-kube-auth-in-vault-with-caveats

動いている pod から aud を確認してみます。

$ kubectl exec devwebapp-with-annotations -c app -- cat /var/run/secrets/kubernetes.io/serviceaccount/token > current_token

$ cat current_token
eyJhbGciOiJSUzI1NiIsImtpZCI6Inc4dnBsbG9rcHNJUUpaN1hudkV3eW9FVnpjY0VyX2lZS2hPRndwT1Awb3cifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzk3MzMxMzAxLCJpYXQiOjE3NjU3OTUzMDEsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiMmViZWFkMGEtMTE0Ny00ZmZmLWIzYzYtOWUxMGQwNzQ2ZWU1Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoibWluaWt1YmUiLCJ1aWQiOiJiYjVlYTRhYy0xMWJmLTQ2YjAtOWIwZS1lZWEzMzg2MzVkMTgifSwicG9kIjp7Im5hbWUiOiJkZXZ3ZWJhcHAtd2l0aC1hbm5vdGF0aW9ucyIsInVpZCI6IjJkNjAwYWJjLWJhY2YtNDRhNS04NDU1LTMwY2ExZTAxNGZhZiJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiaW50ZXJuYWwtYXBwIiwidWlkIjoiYmJkMzQ3ZDctOWIwMC00NzkzLWFhZGEtMWYxYmNkNWE0YzVmIn0sIndhcm5hZnRlciI6MTc2NTc5ODkwOH0sIm5iZiI6MTc2NTc5NTMwMSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6aW50ZXJuYWwtYXBwIn0.TRX40PSavBV3n6c7NJYq-fzTGuhTgY1Yu7MOvH5xR2tQX6iNepXcfUE9Y1YbpkKFphTGHJe4Ku5AZggU4D3aOOBz9VoZqf4OFyaVrSDbQCL4otsAJsjrRMkbqBue4ek_UaL4ME2zwI4583rl9YFZa_phdwE01GBQJmUak7KHOgK8RSK7M1OkwhNw8riaKScEu29wqa5eiLSUMbLlZ7ZmVNhWLarYgKGpF_uQBBZi6JKItFCadi-rc-chHFEJmdZ-Kbgi2KQ57X_KPOagkbedb4CrVfKc03JrRIpxsMJaWI4yjIl7cIKWz_jEM--3y_uWd_w9XayYZgQ1JxsqErGJEQ

https://www.jwt.io/ でトークンの内容を確認します。

Decoded Header:
JSON

{
  "alg": "RS256",
  "kid": "w8vpllokpsIQJZ7XnvEwyoEVzccEr_iYKhOFwpOP0ow"
}

Decoded Payload

{
  "aud": [
    "https://kubernetes.default.svc.cluster.local"
  ],
  "exp": 1797331301,
  "iat": 1765795301,
  "iss": "https://kubernetes.default.svc.cluster.local",
  "jti": "2ebead0a-1147-4fff-b3c6-9e10d0746ee5",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "minikube",
      "uid": "bb5ea4ac-11bf-46b0-9b0e-eea338635d18"
    },
    "pod": {
      "name": "devwebapp-with-annotations",
      "uid": "2d600abc-bacf-44a5-8455-30ca1e014faf"
    },
    "serviceaccount": {
      "name": "internal-app",
      "uid": "bbd347d7-9b00-4793-aada-1f1bcd5a4c5f"
    },
    "warnafter": 1765798908
  },
  "nbf": 1765795301,
  "sub": "system:serviceaccount:default:internal-app"
}

aud が分かったので、role を再作成します。

$ vault write auth/kubernetes/role/devweb-app \
    bound_service_account_names=internal-app \
    bound_service_account_namespaces=default \
    policies=devwebapp \
    ttl=24h \
    audience="https://kubernetes.default.svc.cluster.local"
Success! Data written to: auth/kubernetes/role/devweb-app
$ kubectl apply -f pod-devwebapp-with-annotations-up1.yaml
pod/devwebapp-with-annotations created

$ kubectl get pod
NAME                                    READY   STATUS    RESTARTS   AGE
devwebapp                               1/1     Running   0          4h42m
devwebapp-through-service               1/1     Running   0          4h9m
devwebapp-with-annotations              2/2     Running   0          4s
vault-agent-injector-7c555bc6b8-7hn2d   1/1     Running   0          3h57m

シークレットも確認できました。

$ kubectl exec -it devwebapp-with-annotations -c app -- cat /vault/secrets/credentials.txt
data: map[password:salsa username:giraffe]
metadata: map[created_time:2025-12-15T05:32:02.758046563Z custom_metadata:<nil> deletion_time: destroyed:false version:1]
$ kubectl logs devwebapp-with-annotations -c vault-agent-init
==> Note: Vault Agent version does not match Vault server version. Vault Agent version: 1.20.4, Vault server version: 1.20.3+ent
==> Vault Agent started! Log data will stream in below:

==> Vault Agent configuration:

           Api Address 1: http://bufconn
                     Cgo: disabled
               Log Level: info
                 Version: Vault v1.20.4, built 2025-09-23T13:22:38Z
             Version Sha: 55bd8f18c6c84aa89fdede4850a622c57f03bd7e

2025-12-15T10:47:04.873Z [INFO]  agent.sink.file: creating file sink
2025-12-15T10:47:04.873Z [INFO]  agent.sink.file: file sink configured: path=/home/vault/.vault-token mode=-rw-r----- owner=100 group=1000
2025-12-15T10:47:04.915Z [INFO]  agent.exec.server: starting exec server
2025-12-15T10:47:04.915Z [INFO]  agent.auth.handler: starting auth handler
2025-12-15T10:47:04.915Z [INFO]  agent.auth.handler: authenticating
2025-12-15T10:47:04.915Z [INFO]  agent.sink.server: starting sink server
2025-12-15T10:47:04.915Z [INFO]  agent.template.server: starting template server
2025-12-15T10:47:04.915Z [INFO]  agent.exec.server: no env templates or exec config, exiting
2025-12-15T10:47:04.918Z [INFO]  agent: (runner) creating new runner (dry: false, once: false)
2025-12-15T10:47:04.922Z [INFO]  agent: (runner) creating watcher
2025-12-15T10:47:04.932Z [INFO]  agent.auth.handler: authentication successful, sending token to sinks
2025-12-15T10:47:04.932Z [INFO]  agent.auth.handler: starting renewal process
2025-12-15T10:47:04.932Z [INFO]  agent.sink.file: token written: path=/home/vault/.vault-token
2025-12-15T10:47:04.932Z [INFO]  agent.sink.server: sink server stopped
2025-12-15T10:47:04.932Z [INFO]  agent: sinks finished, exiting
2025-12-15T10:47:04.932Z [INFO]  agent.template.server: template server received new token
2025-12-15T10:47:04.932Z [INFO]  agent: (runner) stopping
2025-12-15T10:47:04.932Z [INFO]  agent: (runner) creating new runner (dry: false, once: false)
2025-12-15T10:47:04.932Z [INFO]  agent: (runner) creating watcher
2025-12-15T10:47:04.933Z [INFO]  agent: (runner) starting
2025-12-15T10:47:04.934Z [INFO]  agent.auth.handler: renewed auth token
2025-12-15T10:47:04.949Z [INFO]  agent: (runner) rendered "(dynamic)" => "/vault/secrets/credentials.txt"
2025-12-15T10:47:04.949Z [INFO]  agent: (runner) stopping
2025-12-15T10:47:04.949Z [INFO]  agent.template.server: template server stopped
2025-12-15T10:47:04.949Z [INFO]  agent: (runner) received finish
2025-12-15T10:47:04.949Z [INFO]  agent.auth.handler: shutdown triggered, stopping lifetime watcher
2025-12-15T10:47:04.949Z [INFO]  agent.auth.handler: auth handler stopped
2025-12-15T10:47:04.949Z [INFO]  agent.exec.server: exec server stopped

以上です!

最後まで読んでいただきありがとうございました。
メリークリスマス!よいお年をお迎えください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?