こんにちは、この記事は 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 でリッスン、ルートトークンがroot、devモード) を起動します。以降使用する 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 へシークレットの取得ができることを確認します。
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 へ接続することで、シークレットの取得をします。

$ 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
ごちゃごちゃした図になってしまいましたが、まず認証のために Kubernetes authentication を有効化します。その後、シークレットへのアクセスコントロールをするために、policyとroleを定義します。最後に 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
以上です!
最後まで読んでいただきありがとうございました。
メリークリスマス!よいお年をお迎えください。

