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.

vault init operator kubernetes
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

unseal vault token kubernetes

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
vault login kubernetes port forward

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 di folder /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 harus unik

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]

k9s pod logs vault

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]

Leave a comment

Your email address will not be published. Required fields are marked *