Vault merupakan salah satu solusi yang digunakan untuk menyimpan secret, dengan menggunakan vault kita memiliki banyak opsi untuk mengamankan secret tersebut dengan membuat policy yang mengijinkan hanya akses terhadap secret tertentu, pemberian akses read-only. Beberapa contoh kasus penggunakan vault sebagai authentikasi, password dan juga PKI (Root CA),
Vault memiliki integrasi dengan Kubernetes sehingga mudah untuk digunakan, helm template bawaan dari vault sendiri untuk kebanyakan use case sudah cukup. Bila anda perlu beberapa fitur security seperti seLinuxOptions automountServiceAccountToken bisa di update dihelm chart langsung.
Beberapa tools yang dibutuhkan
– Kubernetes
– helm
– kubectl
– jw
– jq
– vault CLI
Biasakan menggunakan namespace khusus untuk services yang di share dengan berbagai aplikasi, untuk penamaan namespace ini terserah selera masing-masing, disini saya akan menggunakan services
kubectl create namespace services
Install Vault di Kubernetes
Tambahkan repository Vault
helm repo add hashicorp https://helm.releases.hashicorp.com
Download helm chart vault, saat ini versi yang terbaru v0.30.0
helm pull hashicorp/vault # cek file helm chart $ ls -lah vault-*.tgz -rw-r--r-- 1 tommy tommy 49K Jul 19 18:27 vault-0.30.0.tgz
ekstrak file vault-0.30.0.tgz
tar zxvf vault-0.30.0.tgz cd vault
untuk saat ini helm chart dari Vault menggunakan versi 1.19.0, untuk itu kita akan meng-update values.yaml dan menggunakan 1.20.0
buka file values.yaml ubah bagian
repository: "hashicorp/vault" tag: "1.19.0" repository: "hashicorp/vault" tag: "1.19.0"
menjadi
repository: "hashicorp/vault" tag: "1.20.0" repository: "hashicorp/vault" tag: "1.20.0"
Masih pada folder hasil ektrak helm chart, install vault
helm -n services install vault .
bila anda melakukan perubahan pada helm chart, upgrade dengan
helm -n services upgrade --install vault .
Cek status vault yang baru di deploy
kubectl get pods -l app.kubernetes.io/instance=vault -n services # output NAME READY STATUS RESTARTS AGE vault-0 0/1 Running 0 2m vault-agent-injector-7d4b459759-zvdhc 1/1 Running 0 2m1s
Saat ini vault belum berjalan karena kita belum menginisiasi instalasi.
kubectl -n services exec --stdin=true --tty=true vault-0 -- vault operator init # output Unseal Key 1: F1LU1AYu0z3fn4PkewTPucR+SXIVeVFGZwTzZwK7Z2G7 Unseal Key 2: aVUiKc2/zqCI9kNALI27jdYk4AdxILybpV6jp5z2G+dF Unseal Key 3: JEIo3KLr8gclAKZHhSqZK2pXVks+6p6vMbmwB0xLqWVN Unseal Key 4: dYxXeWkEVRfWA3AQN04FXBKN3wthuw58msUSNz0EE4Je Unseal Key 5: wou6Q0XvLmT2PwFn/le0Eu+RpwSbv+uYcJ0gTLkZwiiH Initial Root Token: hvs.KJOWOPnTUEIJ441uKS63q2uO Vault initialized with 5 key shares and a key threshold of 3. Please securely distribute the key shares printed above. When the Vault is re-sealed, restarted, or stopped, you must supply at least 3 of these keys to unseal it before it can start servicing requests. Vault does not store the generated root key. Without at least 3 keys to reconstruct the root key, Vault will remain permanently sealed! It is possible to generate new unseal keys, provided you have a quorum of existing unseal keys shares. See "vault operator rekey" for more information.
simpan Unseal Key dan Initial Root Token, karena kita akan memerlukannya di tahap selanjutnya.
Untuk mengaktifkan Vault kita perlu memasukkan 3 Unseal Key, gunakan Unseal Key 1, Unseal Key 2, Unseal Key 3. Jalankan perintah dibawah ini sebanyak 3 kali
kubectl -n services exec --stdin=true --tty=true vault-0 -- vault operator unseal
Setelah menjalankan perintah diatas, periksa kembali status vault, harusnya sudah berjalan ditandai dengan READY 1/1
kubectl get pods -l app.kubernetes.io/instance=vault -n services # output NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 9m21s vault-agent-injector-7d4b459759-zvdhc 1/1 Running 0 9m22s
pod vault-agent-injector (Vault Agent Injector) akan kita bahas pada bagian deployment app
Cek juga log pod vault
kubectl -n services logs -l app.kubernetes.io/instance=vault # output 2025-07-18T14:41:45.127Z [INFO] core: successfully mounted: type=token version="v1.19.0+builtin.vault" path=token/ namespace="ID: root. Path: " 2025-07-18T14:41:45.127Z [INFO] rollback: Starting the rollback manager with 256 workers 2025-07-18T14:41:45.127Z [INFO] rollback: starting rollback manager 2025-07-18T14:41:45.127Z [INFO] core: restoring leases 2025-07-18T14:41:45.127Z [INFO] identity: entities restored 2025-07-18T14:41:45.127Z [INFO] expiration: lease restore complete 2025-07-18T14:41:45.127Z [INFO] identity: groups restored 2025-07-18T14:41:45.128Z [INFO] core: usage gauge collection is disabled 2025-07-18T14:41:45.128Z [INFO] core: post-unseal setup complete 2025-07-18T14:41:45.128Z [INFO] core: vault is unsealed
dari log diatas vault is unsealed
berarti vault sudah berjalan dan bisa digunakan.
Install Vault CLI
Sesuaikan dengan architectur komputer anda, untuk AMD/Intel gunakan, untuk versi lainnya ambil langsung dari situs Vault
https://releases.hashicorp.com/vault/1.19.0/vault_1.19.0_linux_amd64.zip unzip vault_1.19.0_linux_amd64.zip chmod +x vault sudo mv vault /usr/local/bin/vault
Buat Secret di Vault
Kita akan menggunakan vault cli untuk membuat secret. Kita akan menggunakan port-forward dari Kubernetes agar bisa mengakses vault dari komputer anda
kubectl -n services port-forward services/vault 8200:8200
setting VAULT_ADDR
export VAULT_ADDR=http://127.0.0.1:8200 vault login
untuk Token gunakan token dari Initial Root Token
Aktifkan kv-v2
vault secrets enable -path=secret kv-v2 # output Success! Enabled the kv-v2 secrets engine at: secrets/
sebagai contoh mari kita buat secret
vault kv put secret/database/credential username="db_admin" password="securepassword" # output ========== Secret Path ========== secret/data/database/credential ======= Metadata ======= Key Value --- ----- created_time 2025-07-18T14:57:12.278140946Z custom_metadata <nil> deletion_time n/a destroyed false version 1
buat contoh secret lainnya
vault kv put secret/sftp/password password="securepassword" vault kv put secret/sftp/username username="sftp"
Cek secret yang baru dibuat dengan
vault kv get secret/database/credential # output ========== Secret Path ========== secret/data/database/credential ======= Metadata ======= Key Value --- ----- created_time 2025-07-18T14:57:12.278140946Z custom_metadata <nil> deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- password securepassword username db_admin
Cara yang sama bisa digunakan dengan API
curl -H "X-Vault-Token: hvs.KJOWOPnTUEIJ441uKS63q2uO" http://127.0.0.1:8200/v1/secret/data/database/credential # output { "request_id": "d3967d11-8b9d-6756-0aa7-468661f1f9f1", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "data": { "password": "securepassword", "username": "db_admin" }, "metadata": { "created_time": "2025-07-18T14:57:12.278140946Z", "custom_metadata": null, "deletion_time": "", "destroyed": false, "version": 1 } }, "wrap_info": null, "warnings": null, "auth": null, "mount_type": "kv" }
Autentikasi Kubernetes
Untuk bisa authentikasi ke kubernetes menggunakan vault kita perlu membuat ServiceAccount
, Secret
dan ClusterRoleBinding
Buat file vault-serviceaccount.yaml
yang berisi
apiVersion: v1 kind: ServiceAccount metadata: name: vault-auth namespace: services --- apiVersion: v1 kind: Secret metadata: name: vault-auth namespace: services annotations: kubernetes.io/service-account.name: vault-auth type: kubernetes.io/service-account-token --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: role-vault-auth-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: vault-auth namespace: services
deploy object tersebut ke Kubernetes
kubectl -n services apply -f vault-serviceaccount.yaml # output serviceaccount/vault-auth created secret/vault-auth created clusterrolebinding.rbac.authorization.k8s.io/role-vault-auth-binding created
Disisi vault aktifkan auth untuk Kubernetes
vault auth enable kubernetes # output Success! Enabled kubernetes auth method at: kubernetes/
Update config auth kubernetes
TOKEN_JWT=$(kubectl -n services get secret vault-auth -o go-template='{{ .data.token }}' | base64 --decode) KUBE_CA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 --decode) KUBE_SERVER=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.server}') vault write auth/kubernetes/config \ kubernetes_host="$KUBE_SERVER" \ token_reviewer_jwt="$TOKEN_JWT" \ kubernetes_ca_cert="$KUBE_CA" \ disable_local_ca_jwt="true"
check config tersebut
vault read auth/kubernetes/config # output Key Value --- ----- disable_iss_validation true disable_local_ca_jwt true issuer n/a kubernetes_ca_cert -----BEGIN CERTIFICATE----- MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p a3ViZUNBMB4XDTI0MTAwOTEzMzE1NVoXDTM0MTAwODEzMzE1NVowFTETMBEGA1UE AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGt upTh4XrJrJdTzSpb6GaS+C5K/Q2uG2hueJjzUHVrN77S8S0niPv1o9Bh5xFEWwos ncxcOMnp4IwoIgnnGumBNwkP9WWJExNh6R1iC48I+Rod7IadW6UO0RTeoPDUVUKl UJZa6RyVnr0eSiuxYcywM00CG+NhYvF8fCrzMJ57rYHTacUUGTDM1J3y4UcgYmJl 95EB0VjxKQ5V1KqSeRqbbgGrVdWmXLBFyK6s05N18OeVc+4A0d2jxB60wOgkHx4D P+X8RT+htaw1mFutT3yBcAO+HRiV36qwXQawbuGktPLw0S7+pJnkp2j3cg5CG9YB qUy0Yddr4bSm3BF17SsCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW BBRlJzLvIWzesiihmscds+rLswNZbTANBgkqhkiG9w0BAQsFAAOCAQEAvW3anqlO AhmIQHlci4eFqVU6fBADCVNVG3SpCIywa5pV/+Af2Vk9+BEmxfDoRm7wwqwjPw3X gtfBDduhozxu9MbEN4dkppnKzOQAGipwP7uGQyuqJnJmR1rhdC5n9aHL+HM6x3TK WGDylY3s0cOjM+n3lFKRH4l/En3I9XiBQk9fso2bH++ZOX3e3WiGotvr776dYODR hUV2avTz3rjqwFIISIIItgQS7GbKAVJxvR+pLzhQmIp5QpVhtsdB6ZqKUn5eZ8J1 HGtLdROKWFKFqnB9DGjZ99KjyJvAqY6jhl8+5W63QaDoJDlXY+ABjYEzXle9oVsE 5Jpx2HEuYKowSg== -----END CERTIFICATE----- kubernetes_host https://192.168.49.2:8443 pem_keys [] token_reviewer_jwt_set true use_annotations_as_alias_metadata false
Buat policy baru yang memberikan akses read-only ke path secrets/database/credential
, bila melihat dari format Secret Path
========== Secret Path ========== secret/data/database/credential
maka path yang kita masukkan formatnya secret/data/database/credential
bukan secret/database/credential
, agar lebih memudahkan untuk banyak aplikasi yang mengguanakan vault, berikan akses read-only ke semua file di path secret
vault policy write vault-auth-policy - <<EOF path "secret/data/*" { capabilities = ["read"] } EOF # output Success! Uploaded policy: vault-auth-policy
Mulai vault v1.21, diwajibkan menambahakan audience pada auth kubernetes, untuk itu kita ambil audience/aud yang digunakan dari token di kubernetes
Menggunakan API
/api/v1/namespaces/services/serviceaccounts/vault-auth/token
, karena Kubernetes memiliki namespace bawaan default, kita bisa ubah menjadi
/api/v1/namespaces/default/serviceaccounts/default/token
echo '{"apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest"}' \ | kubectl create -f- --raw /api/v1/namespaces/services/serviceaccounts/vault-auth/token \ | jq -r '.status.token' \ | cut -d . -f2 \ | base64 -d | jq . # output { "aud": [ "https://kubernetes.default.svc.cluster.local" ], "exp": 1752931278, "iat": 1752927678, "iss": "https://kubernetes.default.svc.cluster.local", "jti": "139c84e5-bbb4-41fb-90a6-9e419454b7c9", "kubernetes.io": { "namespace": "services", "serviceaccount": { "name": "vault-auth", "uid": "a85ffc2b-32d6-4573-8d6c-c863425055a1" } }, "nbf": 1752927678, "sub": "system:serviceaccount:services:vault-auth" }
Lewat kubectl kubectl -n services create token vault-auth -o json | jq .
atau kubectl -n services create token vault-auth | jwt decode -
kubectl -n services create token vault-auth | jwt decode - Token header ------------ { "alg": "RS256", "kid": "5IllsgfVC5skYvHmA9Yh7c2WgrzF8rCS4dixhVxiauo" } Token claims ------------ { "aud": [ "https://kubernetes.default.svc.cluster.local" ], "exp": 1752931376, "iat": 1752927776, "iss": "https://kubernetes.default.svc.cluster.local", "jti": "f9c0e7ee-a1a9-48f0-a3c0-a1176abe44e2", "kubernetes.io": { "namespace": "services", "serviceaccount": { "name": "vault-auth", "uid": "a85ffc2b-32d6-4573-8d6c-c863425055a1" } }, "nbf": 1752927776, "sub": "system:serviceaccount:services:vault-auth" }
Setelah membuat policy, sekarang attach policy tersebut ke role vault-auth
. Disini kita memberikan akses hanya ke namespace services
, diluar namespace tersebut akan terjadi error
vault write auth/kubernetes/role/vault-auth \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=services \ policies=vault-auth-policy \ audience="https://kubernetes.default.svc.cluster.local" ttl=24h # output Success! Data written to: auth/kubernetes/role/vault-auth
Deploy Aplikasi di Kubernetes
Untuk bisa mengambil secret dari vault, kita akan menggunakan annotation dari Vault Agent Injector, format annotation
vault.hashicorp.com/agent-inject-secret-<file-name>: vault/secret/path
secret dari vault akan disimpan dalam file sesuai dengan nama pada /vault/secrets
Pada tahap sebelumnya kita sudah membuat secret/database/credential
, annotationnya bisa dibuat menjadi
vault.hashicorp.com/agent-inject-secret-dbcredential: secret/database/credential # lokasi file /vault/secrets/dbcredential vault.hashicorp.com/agent-inject-secret-dbcredential.txt: secret/database/credential # lokasi file /vault/secrets/dbcredential.txt vault.hashicorp.com/agent-inject-secret-db-credential: secret/database/credential # lokasi file /vault/secrets/db-credential
kita bisa menggunakan beberapa annotations tetapi
vault.hashicorp.com/agent-inject-secret-dbcredential: secret/database/credential vault.hashicorp.com/agent-inject-secret-sftp-username: secret/sftp/username vault.hashicorp.com/agent-inject-secret-sftp-password: secret/sftp/password
Buat file baru dengan nama job-app.yaml
yang berisi
apiVersion: batch/v1 kind: Job metadata: name: job-app spec: backoffLimit: 0 template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "vault-auth" vault.hashicorp.com/agent-inject-secret-dbcredential: "secret/database/credential" vault.hashicorp.com/agent-pre-populate-only: "true" spec: restartPolicy: Never serviceAccountName: vault-auth containers: - name: job image: debian:stable-slim command: ["cat", "/vault/secrets/dbcredential"]
Vault Agent Injector bisa dijalankan sebagai Init Cointainer
ataupun sidecar
Khusus untuk Init container
tambahkan annotation vault.hashicorp.com/agent-pre-populate-only: "true"
Init containers
Deploy object tersebut
kubectl -n services apply -f job-app.yaml # output job.batch/job-app created
Cek log
kubectl -n services logs -l "job-name=job-app" # output Defaulted container "job" out of: job, vault-agent-init (init) data: map[password:securepassword username:db_admin] metadata: map[created_time:2025-07-19T11:38:08.887288145Z custom_metadata:<nil> deletion_time: destroyed:false version:1]
Sidecar containers
apiVersion: apps/v1 kind: Deployment metadata: name: app-sidecar spec: selector: matchLabels: app: app-sidecar replicas: 1 template: metadata: annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "vault-auth" vault.hashicorp.com/agent-inject-secret-app-config: "secret/database/credential" labels: app: app-sidecar spec: serviceAccountName: vault-auth containers: - name: app-sidecar image: debian:stable-slim command: ["/bin/sh", "-c"] args: ["cat /vault/secrets/app-config && sleep infinity"]
Cek log dari pod
$ kubectl -n services logs -l "app=app-sidecar" Defaulted container "app-sidecar" out of: app-sidecar, vault-agent, vault-agent-init (init) data: map[password:securepassword username:db_admin] metadata: map[created_time:2025-07-19T11:38:08.887288145Z custom_metadata:<nil> deletion_time: destroyed:false version:1]