Belajar Vault - Integrasi Vault dengan Kubernetes (K8s Auth & Agent Injector)
Episode 17 of 26

Belajar Vault - Integrasi Vault dengan Kubernetes (K8s Auth & Agent Injector)

Otentikasikan Pod Kubernetes ke Vault lewat Kubernetes Auth Method, lalu otomatiskan penyediaan secret dengan Vault Agent Sidecar Injector dan Vault Secrets Operator (VSO) yang menyinkronkan secret ke native Kubernetes Secrets.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Pendahuluan

Setelah di episode 16 sebelumnya kita membahas integrasi langsung aplikasi ke Vault lewat SDK — lengkap dengan pola retry, renewal, dan fallback — pada episode kali ini kita akan membahas integrasi yang paling banyak dipakai di dunia cloud-native: Vault dengan Kubernetes.

Mengapa topik ini penting? Hampir semua perusahaan yang memakai Vault di produksi menjalankan aplikasinya di Kubernetes. Masalahnya: cara tradisional memberikan secret ke Pod — yaitu menulis nilai rahasia langsung ke Secret Kubernetes — masih menyimpan secret dalam bentuk statis di etcd, mudah terbaca siapa saja yang punya akses, dan tidak pernah terotentikasi ke Vault. Di episode ini kita akan menyelesaikan masalah tersebut dengan tiga mekanisme utama: Kubernetes Auth Method untuk otentikasi, Vault Agent Sidecar Injector untuk injeksi secret otomatis ke Pod, dan Vault Secrets Operator (VSO) untuk sinkronisasi secret ke native Kubernetes Secret. Mari kita bedah satu per satu.

Pembahasan Utama

Kubernetes Auth Method (auth/kubernetes)

Kubernetes Auth Method adalah cara Pod berotentikasi ke Vault tanpa perlu menyimpan token atau secret ID. Konsepnya: setiap Pod di Kubernetes otomatis memiliki ServiceAccount dan setiap ServiceAccount memiliki JWT token yang melekat. JWT inilah yang menjadi "bukti identitas" Pod ke Vault.

Alur autentikasinya seperti ini:

Alur Kubernetes Auth Method
Pod (SA: web-sa)
   │  1. kirim ServiceAccount JWT ke Vault: auth/kubernetes/login

Vault  ──2. verify JWT──▶ Kubernetes API (TokenReview)
   ▲                          │ 3. valid / invalid + claims SA & namespace
   └──4. issue Vault token ───┘

Vault tidak menebak-nebak apakah JWT itu asli — ia meminta Kubernetes API Server untuk memverifikasi token lewat endpoint TokenReview. Dengan begitu, Vault percaya penuh bahwa token tersebut memang milik ServiceAccount tertentu di namespace tertentu.

Mengaktifkan & Mengonfigurasi K8s Auth

Langkah pertama mengaktifkan auth method dan menghubungkannya ke cluster. Vault membutuhkan tiga hal: alamat Kubernetes API, CA certificate cluster, dan JWT dari service account yang berhak melakukan TokenReview:

Aktifkan & konfigurasi Kubernetes Auth
vault auth enable kubernetes
 
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc" \
  token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

Important

Perintah konfigurasi di atas dijalankan dari dalam cluster (misal via kubectl exec atau sebuah Job bootstrap), sehingga token_reviewer_jwt diambil dari service account aktif. Di setup yang lebih aman, token_reviewer_jwt di-generate sekali lalu disimpan sebagai secret — atau gunakan tooling seperti vault-kubernetes-auth helper yang dijalankan saat cluster pertama kali dibangun.

Membuat Role yang Terikat ServiceAccount

Setelah konfigurasi, kita buat role yang mengikat ServiceAccount tertentu dengan policy Vault. Ini adalah titik kontrol akses yang krusial: hanya Pod dengan SA bernama web-sa di namespace default yang boleh login lewat role web:

Buat role terikat SA & namespace
vault write auth/kubernetes/role/web \
  bound_service_account_names="web-sa" \
  bound_service_account_namespaces="default" \
  policies="web-app" \
  ttl="1h"

Perhatikan juga policy Vault yang dilekatkan ke role tersebut harus mengizinkan akses ke path yang dibutuhkan aplikasi:

web-app-policy.hcl
path "secret/data/myapp" {
  capabilities = ["read"]
}

Warning

Selalu tentukan bound_service_account_names dan bound_service_account_namespaces. Role tanpa binding ini sama seperti pintu tanpa kunci — semua ServiceAccount di cluster bisa login memakai role tersebut dan mewarisi policy-nya. Ini salah satu misconfiguration paling berbahaya di produksi.

Vault Agent Sidecar Injector

Kubernetes Auth Method sudah membuat Pod bisa berotentikasi, tapi kita masih perlu cara men-deliver secret ke dalam Pod. Di sinilah Vault Agent Sidecar Injector berperan: sebuah mutating admission webhook yang otomatis menambahkan container sidecar Vault Agent ke dalam Pod berdasarkan annotations.

Cara kerjanya:

  1. Kalian menulis Deployment biasa dengan annotations vault.hashicorp.com/....
  2. Saat Pod dibuat, webhook injector menambahkan sidecar container vault-agent dan volume memori /vault/secrets.
  3. Sidecar melakukan auto-auth via Kubernetes Auth Method, membaca secret dari Vault, lalu merender file ke /vault/secrets/config.
  4. Aplikasi membaca file tersebut — tanpa perlu tahu Vault sama sekali.

Menyiapkan Service Account & Menginstal Injector

Sebelum deployment berjalan, ada dua persiapan wajib. Pertama, buat ServiceAccount web-sa — identitas yang akan membuktikan diri ke Vault:

Kubernetesservice-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: web-sa
  namespace: default

Kedua, pasang Vault Agent Injector di cluster. Cara paling umum adalah via Helm chart hashicorp/vault dengan sub-chart injector (atau install mandiri chart injector):

KubernetesInstall Vault Agent Injector via Helm
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault \
  --set injector.enabled=true \
  --set injector.logLevel=info
KubernetesVerifikasi webhook injector aktif
kubectl get mutatingwebhookconfiguration
kubectl get pods -n vault

Tip

Injector hanya memproses Pod yang memiliki annotation vault.hashicorp.com/agent-inject: "true" — Pod lain di cluster tidak terpengaruh. Ini keuntungan desain besar: kalian bisa mengaktifkan Vault per-aplikasi secara bertahap tanpa mengubah seluruh cluster.

Deployment dengan Annotations Injector

Kubernetesdeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "web"
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp"
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/myapp" }}
          DB_HOST={{ .Data.data.DB_HOST }}
          DB_PASSWORD={{ .Data.data.DB_PASSWORD }}
          {{- end }}
    spec:
      serviceAccountName: web-sa
      containers:
        - name: myapp
          image: myapp:1.0.0
          command: ["sh", "-c", "sleep infinity"]

Penjelasan annotation kunci:

  • vault.hashicorp.com/agent-inject: "true" — penanda bahwa injector harus memproses Pod ini.
  • vault.hashicorp.com/role: "web" — role K8s auth yang dipakai sidecar untuk login (harus match dengan bound_service_account_names).
  • vault.hashicorp.com/agent-inject-secret-config — nama file tujuan (config) dipisahkan dari path secret (secret/data/myapp) oleh -. Hasilnya secret di-render ke /vault/secrets/config.
  • agent-inject-template-config — template custom yang mengontrol isi file. Tanpa template ini, injector memakai template bawaan (format YAML) yang sering kurang sesuai kebutuhan.

Hasil Render di Dalam Pod

Setelah Pod berjalan, mari kita lihat apa yang dihasilkan sidecar:

KubernetesCek file secret di dalam Pod
kubectl exec deploy/myapp -- cat /vault/secrets/config
Isi /vault/secrets/config hasil render
DB_HOST=postgres.internal.local
DB_PASSWORD=a9f3!sVx#2kQ

Tip

Volume /vault/secrets adalah emptyDir bertipe memory (medium: Memory) — file secret disimpan di RAM, bukan di disk, sehingga tidak pernah menulis rahasia ke persistent storage node. Inilah salah satu alasan sidecar injector lebih aman daripada Secret statis biasa.

Apa yang Terjadi saat Secret di Vault Dirotasi?

Ini keunggulan utama injector: jika secret di Vault diubah, sidecar agent mendeteksi perubahannya (via polling ke Vault) dan merender ulang file secara otomatis. Aplikasi yang membaca file setiap kali membutuhkan koneksi akan otomatis memakai nilai terbaru. Tapi perhatikan — aplikasi yang sudah menyimpan nilai lama di memori (misal koneksi pool yang dibangun saat startup) tidak akan terpengaruh sampai ia membaca ulang file atau di-restart. Untuk aplikasi yang perlu merespons rotasi cepat, tambahkan reloader seperti restart otomatis saat file berubah.

Vault Secrets Operator (VSO)

Sidecar injector menyelesaikan masalah injection, tapi ada kasus lain: aplikasi (atau tooling seperti Helm chart, ingress, dsb.) memang membutuhkan native Kubernetes Secret — bukan file di dalam Pod. Di sinilah Vault Secrets Operator (VSO) masuk: operator ini menyinkronkan secret dari Vault ke native Secret Kubernetes secara kontinu, lengkap dengan rotasi otomatis.

Arsitekturnya terdiri dari beberapa CRD (Custom Resource Definition):

  • VaultConnection — alamat Vault yang akan dihubungi.
  • VaultAuth — kredensial otentikasi (misal via Kubernetes Auth Method).
  • VaultStaticSecret — sumber secret dan tujuan sinkronisasi (destination).
  • (Ada juga VaultDynamicSecret untuk dynamic credentials, dan VaultPKISecret untuk sertifikat — kita fokus ke static dulu.)
Kubernetesvso.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
  name: vault
  namespace: default
spec:
  address: http://vault:8200
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: vault-auth
  namespace: default
spec:
  method: kubernetes
  mount: kubernetes
  kubernetes:
    role: web
    serviceAccount: web-sa
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
  name: myapp-secret
  namespace: default
spec:
  vaultAuthRef: vault-auth
  mount: secret
  type: kv-v2
  path: myapp
  refreshAfter: 1h
  destination:
    name: myapp-secrets
    create: true
    overwrite: true

Penjelasan bagian penting:

  • vaultAuthRef menunjuk ke VaultAuth yang memakai Kubernetes Auth dengan role web dan service account web-sa.
  • mount: secret + type: kv-v2 + path: myapp berarti sumbernya adalah secret/data/myapp pada secrets engine KV v2.
  • refreshAfter: 1h menentukan interval sinkronisasi — setiap jam operator membandingkan versi secret di Vault dan memperbarui Secret Kubernetes jika ada perubahan.
  • destination mendefinisikan Secret bernama myapp-secrets yang akan dibuat/diperbarui.

VSO juga mendukung dynamic credentials lewat CRD VaultDynamicSecret. Contohnya, mengambil kredensial database sementara dari secrets engine database dan menyinkronkannya ke Secret:

Kubernetesvso-dynamic.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
  name: db-creds
  namespace: default
spec:
  vaultAuthRef: vault-auth
  mount: database
  role: web-role
  destination:
    name: db-creds
    create: true
  revoke: true
  renewalPercent: 60

Important

Pada VaultDynamicSecret, atribut revoke: true memastikan kredensial dinamis di-revoke ketika Secret Kubernetes dihapus atau CRD dihapus — menjaga prinsip short-lived credentials. Sementara renewalPercent: 60 membuat operator memperbarui lease saat sudah mencapai 60% dari TTL-nya, sehingga aplikasi tidak pernah memakai kredensial yang hampir kadaluarsa.

Hasilnya, sebuah Secret native Kubernetes siap dipakai aplikasi:

KubernetesSecret native hasil sinkronisasi
kubectl get secret myapp-secrets -o jsonpath='{.data}'
Nilai secret (base64)
{
  "DB_HOST": "cG9zdGdyZXMuaW50ZXJuYWwubG9jYWw=",
  "DB_PASSWORD": "YTlmMyFzVngjMmtR"
}

Note

VSO menangani rotasi dengan cerdas: saat secret di Vault berubah (versi baru di KV v2), operator otomatis memperbarui Secret Kubernetes dan — jika diaktifkan via refreshAfter/events — memicu rollout deployment yang mengonsumsi secret tersebut. Ini menghilangkan pekerjaan manual "update secret, restart pod".

Sidecar Injector vs VSO: Kapan Pakai yang Mana?

AspekAgent Injector (Sidecar)Vault Secrets Operator (VSO)
Bentuk deliverableFile di /vault/secrets/... dalam PodNative Kubernetes Secret
Secret disimpan di etcdTidak (RAM emptyDir)Ya (sebagai Secret biasa)
Akses aplikasiBaca file lokalStandard K8s secret mount / env
Ideal untukAplikasi yang butuh secret sebagai file/configHelm chart, ingress TLS, tooling yang butuh Secret
Keterlibatan Vault APISidecar menanganiOperator menangani (transparan)
RotasiRender ulang file; aplikasi perlu reloadUpdate Secret; bisa trigger rollout

Warning

Karena VSO menyimpan secret sebagai Secret Kubernetes biasa, semua nilai pada akhirnya tersimpan di etcd (dalam bentuk base64, bukan terenkripsi kuat). Jika regulasi kalian mensyaratkan secret tidak pernah mengendap di penyimpanan, gunakan Sidecar Injector. VSO lebih cocok untuk tooling yang memang membutuhkan objek Secret native.

Kesalahan Umum Integrasi Vault-Kubernetes

KesalahanGejalaSolusi
Role tanpa bound_service_account_*Semua SA bisa login — celah keamananSelalu tentukan SA name + namespace
Annotation template salah indentasi (block scalar ``)File hasil render tidak valid / kosong
Policy Vault tidak mengizinkan path secretSidecar error permission deniedPastikan policy role web mencakup secret/data/myapp
SA tidak dibuat atau namespace salahPod gagal login: service account not foundVerifikasi serviceAccountName & buat SA via RBAC
token_reviewer_jwt dari SA tanpa izin TokenReviewLogin selalu ditolak VaultPastikan SA bootstrap punya ClusterRole system:auth-delegator
Lupa serviceAccountName di pod specInjector memakai SA default — gagal boundEksplisit set serviceAccountName
Path KV v2 salah (secret/myapp vs secret/data/myapp)Secret tidak ditemukanUntuk kv-v2 selalu pakai secret/data/...
Mengira VSO sekaligus menyediakan dynamic credentialsHanya VaultStaticSecret yang ter-syncGunakan VaultDynamicSecret untuk creds dinamis
Secret native dari VSO di-mount sebagai envNilai tak ter-update sampai restart podGunakan volume mount + reloader bila butuh rotasi live

Caution

Urutan troubleshooting yang benar saat sidecar gagal: (1) cek kubectl logs <pod> vault-agent untuk error login/policy, (2) verifikasi role binding SA↔namespace↔policy, (3) uji akses path dengan vault read secret/data/myapp menggunakan token dari role tersebut. Jangan langsung mengganti konfigurasi tanpa tahu di layer mana masalahnya.

Penutup

Pada episode 17 ini kita telah membahas integrasi Vault dengan Kubernetes secara menyeluruh: Kubernetes Auth Method yang memverifikasi ServiceAccount JWT melalui TokenReview API, Vault Agent Sidecar Injector yang otomatis menyuntikkan sidecar untuk merender secret ke /vault/secrets/config di dalam RAM, dan Vault Secrets Operator (VSO) yang menyinkronkan secret ke native Kubernetes Secret dengan rotasi otomatis.

Inti pelajaran episode ini: identitas Pod di Kubernetes (ServiceAccount) adalah kunci otentikasi yang kuat ke Vault — tidak perlu lagi menyimpan token di mana pun. Pilih injector ketika kalian ingin secret berakhir di memori Pod, pilih VSO ketika tooling kalian membutuhkan objek Secret native.

Di episode 18 selanjutnya kita akan berpindah ke dunia otomasi pengembangan: integrasi Vault di CI/CD pipeline (GitHub Actions & GitLab CI) — bagaimana pipeline mengambil kredensial sementara dari Vault tanpa menyimpan secret statis di repository. Pastikan tetap semangat!

Belajar Vault - Integrasi Vault dengan Kubernetes (K8s Auth & Agent Injector) | Belajar Secret Management dengan HashiCorp Vault