Di episode ini kita akan membahas jantung dari keamanan Vault: policy dalam format HCL yang mengatur siapa boleh melakukan apa terhadap path mana pun. Kalian akan belajar path matching, capabilities, cara menulis, menerapkan, dan menguji policy hingga level produksi.

Setelah di episode 8 sebelumnya kita membahas berbagai secrets engine tambahan seperti TOTP, SSH, dan AWS, kita sudah punya banyak "lemari penyimpan rahasia". Namun ada pertanyaan besar yang belum kita jawab: siapa yang boleh membuka lemari itu, dan sampai sejauh mana? Tanpa lapisan kontrol, semua orang yang punya akses ke Vault bisa membaca semua rahasia — sebuah bencana keamanan yang setara dengan meletakkan password server produksi di spreadsheet publik.
Pada episode kali ini kita akan membahas Vault Policies — mekanisme fine-grained access control berbasis HCL yang menjadi pintu gerbang otorisasi di Vault. Ini adalah fondasi yang paling menentukan keamanan seluruh sistem secret management kalian. Seorang attacker yang tidak punya akses ke Vault mungkin tidak berbahaya; tetapi satu policy yang terlalu permisif justru bisa menjadi celah terbesar yang pernah kalian buat.
Mari kita mulai dari konsepnya, lalu langsung praktik.
Policy di Vault adalah dokumen deklaratif yang mendefinisikan kumpulan capabilities yang diizinkan terhadap satu set path. Bayangkan policy seperti kartu akses karyawan: kartu security bisa membuka pintu server, kartu software engineer hanya bisa membuka pintu lab, dan kartu vendor tidak bisa membuka pintu apa pun kecuali ruang tamu. Setiap token yang diterbitkan Vault dikaitkan dengan satu atau lebih policy, dan setiap request yang masuk dicek terhadap seluruh policy yang dimiliki token tersebut.
Alur otorisasinya kurang lebih seperti ini:
GET /v1/secret/data/app).403 Permission Denied.Client Request --> Auth Method (identitas) --> Policy Check (otorisasi) --> Secrets Engine --> StoragePoin pentingnya: autentikasi menentukan siapa kalian, sedangkan policy menentukan apa yang boleh kalian lakukan. Keduanya tidak boleh dicampur aduk, dan ini yang akan kita bangun di episode ini sampai episode-episode berikutnya.
Policy ditulis dalam format HCL (HashiCorp Configuration Language), bahasa yang sama dengan Terraform. Struktur paling sederhananya terdiri dari blok path dan daftar capabilities:
path "secret/data/*" {
capabilities = ["read", "list"]
}Blok path "secret/data/*" mendefinisikan path apa yang diatur, dan capabilities adalah daftar operasi yang diizinkan. Sebuah policy bisa memuat banyak blok path, dan setiap blok bisa memiliki konfigurasi tambahan seperti allowed_parameters, denied_parameters, required_parameters, serta min_wrapping_ttl dan max_wrapping_ttl (akan kita bahas di episode 13).
Berikut contoh policy yang lebih lengkap untuk aplikasi backend:
path "secret/data/app/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "database/creds/app-db" {
capabilities = ["read"]
}
path "pki_int/issue/app-cert" {
capabilities = ["create", "update"]
}
path "transit/decrypt/app-key" {
capabilities = ["update"]
}Perhatikan bagaimana policy ini hanya memberi akses ke kebutuhan spesifik aplikasi: membaca config di KV, mengambil dynamic credentials database, menerbitkan sertifikat PKI, dan mendekripsi data via Transit. Tidak ada akses ke secret/data/production/* milik tim lain, tidak ada sudo, dan tidak ada akses ke path sys/ (administrasi Vault).
Setiap operasi di Vault memetakan ke satu capability. Berikut tabel lengkapnya:
| Capability | Arti | Operasi HTTP | Contoh CLI |
|---|---|---|---|
create | Membuat data baru di path (tidak ada sebelumnya) | POST | vault kv put (data baru) |
read | Membaca data dari path | GET | vault kv get, vault read |
update | Mengubah data yang sudah ada | POST / PUT | vault kv put (data ada), vault write |
delete | Menghapus data dari path | DELETE | vault kv delete, vault delete |
list | Menampilkan daftar key di path | LIST | vault kv list, vault list |
sudo | Operasi dengan hak istimewa (root-like) | Beragam | vault auth enable, vault secrets enable |
deny | Melarang semua operasi di path (selalu menang) | — | — |
Important
Perhatikan bahwa list tidak otomatis mengizinkan read, dan sebaliknya — keduanya adalah capability terpisah. Sering kali kalian memberi list agar orang bisa melihat nama-nama secret yang ada, tapi tidak boleh membuka isinya. Ini pola yang sangat berguna untuk discovery tanpa ekspos data.
Ada tiga capability yang sering membingungkan pemula:
sudo — bukan berarti "semua bisa". sudo mengizinkan operasi yang membutuhkan hak istimewa (seperti mengaktifkan secrets engine, mengubah kebijakan, atau mengelola audit). Path yang membutuhkan sudo contohnya sys/policies/*, sys/secrets/*, dan sys/audit/*.deny — satu-satunya capability yang selalu menang, bahkan mengalahkan capability lain yang sama-sama diberikan di path yang sama dari policy berbeda. Ingat prinsip ini: deny wins. Kita akan bahas lebih dalam di bagian pitfalls.read + update pada path yang sama adalah kombinasi paling umum untuk aplikasi: membaca config dan memperbarui nilai tertentu tanpa bisa menghapusnya.Path di policy tidak harus persis — Vault mendukung tiga pola pencocokan yang menentukan granularitas akses:
| Pola | Contoh | Cocok dengan | Tidak cocok dengan |
|---|---|---|---|
| Exact path | secret/data/app | secret/data/app saja | secret/data/app/env, secret/data/apps |
Prefix wildcard * | secret/data/app/* | Semua path di bawah prefix | secret/data/app (path induknya sendiri) |
Segment wildcard + | secret/+/app | secret/staging/app, secret/prod/app (satu segmen) | secret/staging/dev/app (dua segmen) |
Satu hal yang wajib kalian pahami: secret/data/app/* TIDAK cocok dengan secret/data/app. Ini kesalahan klasik. Jika kalian ingin mengizinkan akses ke path induk beserta semua turunannya, kalian perlu dua blok:
path "secret/data/app" {
capabilities = ["read", "list"]
}
path "secret/data/app/*" {
capabilities = ["read", "update", "delete", "list"]
}Pola pencocokan di atas berlaku untuk semua path Vault, termasuk path sistem seperti sys/ dan path auth seperti auth/. Kejelian memilih pola ini adalah pembeda antara policy yang aman dan policy yang bocor.
Warning
Hati-hati dengan prefix wildcard yang terlalu luas, seperti secret/data/* atau bahkan * dengan ["read"]. Semakin lebar pola, semakin besar radius ledakan (blast radius) jika token tersebut bocor. Selalu mulai dari path sespesifik mungkin, baru longgarkan jika benar-benar dibutuhkan.
data/Ini salah satu sumber kebingungan terbesar di Vault. Saat kalian mengaktifkan kv-v2 di path secret, data sebenarnya disimpan di bawah secret/data/..., sementara metadata versi berada di secret/metadata/.... Artinya:
secret/data/<path> (capability read)secret/metadata/<path> (capability list)secret/metadata/<path> (capability delete)secret/destroy/<path> (capability update)Untuk vault kv get secret/app bekerja, token kalian butuh read di secret/data/app. Sedangkan vault kv list secret/ butuh list di secret/metadata/. Ini sebabnya banyak orang menulis policy secret/data/* lalu bingung kenapa vault kv list gagal — karena perintah list itu menyentuh path metadata, bukan data.
path "secret/data/app/*" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/metadata/app/*" {
capabilities = ["list", "delete"]
}
path "secret/destroy/app/*" {
capabilities = ["update"]
}Sekarang kita masuk ke bagian praktik. Semua perintah di bawah ini dijalankan dengan token yang memiliki policy root (biasanya token dev mode di episode 0).
1. Menulis policy ke Vault
vault policy write app-backend policy/app-backend.hclSuccess! Uploaded policy: app-backend2. Melihat daftar dan isi policy
vault policy listadmin
app-backend
default
rootUntuk melihat isi policy secara lengkap:
vault policy read app-backend3. Membuat token yang memakai policy tersebut
vault token create -policy=app-backendKey Value
--- -----
token hvs.CAESIHK7...<di-truncate>
token_accessor SH4P2q4VzaCdbXZqGcCfp2A3
token_duration 768h
token_renewable true
token_policies ["app-backend" "default"]
identity_policies []
policies ["app-backend" "default"]Perhatikan token_policies berisi app-backend dan default — Vault selalu melampirkan policy default ke setiap token. Jangan menghapus policy default; ia berisi path-path dasar yang dibutuhkan token untuk fungsi normal (seperti renew token sendiri).
4. Menguji privilege dengan vault token capabilities
Perintah ini adalah "detektif" kalian — ia menunjukkan capability apa yang dimiliki token tertentu terhadap sebuah path, tanpa perlu benar-benar mengirim request:
export VAULT_TOKEN=hvs.CAESIHK7... # token dari output di atas
vault token capabilities secret/data/app
vault token capabilities secret/data/production/dbread list
denyOutput pertama menunjukkan token bisa read dan list di secret/data/app, sedangkan output kedua menunjukkan deny — karena token tidak punya akses ke path tersebut. Dengan perintah ini kalian bisa memverifikasi policy sebelum benar-benar menggunakannya, sangat berguna saat debugging.
5. Uji request sungguhan
vault kv get secret/app
vault kv put secret/app/secret-key value="rahasia"Operasi yang diizinkan policy akan sukses, dan yang tidak akan menghasilkan:
Error writing data to secret/data/app/secret-key: Error making API request.
URL: PUT http://127.0.0.1:8200/v1/secret/data/app/secret-key
Code: 403. Errors:
* 1 error occurred:
* permission deniedAgar lebih mudah diingat, berikut peta operasi umum yang paling sering kalian butuhkan:
| Perintah | Path yang disentuh | Capability dibutuhkan |
|---|---|---|
vault kv get secret/app | secret/data/app | read |
vault kv put secret/app x=y | secret/data/app | create (baru) / update (ada) |
vault kv delete secret/app | secret/data/app | delete |
vault kv list secret/ | secret/metadata/ | list |
vault kv metadata get secret/app | secret/metadata/app | read |
vault kv destroy -versions=1 secret/app | secret/destroy/app | update |
vault read database/creds/app-db | database/creds/app-db | read |
vault write pki_int/issue/app-cert ... | pki_int/issue/app-cert | create, update |
Satu token bisa membawa banyak policy. Ketika Vault memeriksa akses, seluruh policy digabung dengan union semantics — artinya capabilities digabungkan, bukan dibatasi. Jika policy A memberi read dan policy B memberi update di path yang sama, hasilnya token punya read dan update.
Tapi ingat kembali: deny adalah pengecualian. Jika satu policy punya deny di sebuah path, maka deny tersebut berlaku efektif meskipun policy lain memberi read.
Contoh nyata: kalian punya policy app-backend (memberi akses luas ke secret/data/app/*) dan ingin membuat token yang hanya boleh membaca data production, tidak boleh menghapus:
path "secret/data/app/production/*" {
capabilities = ["read", "list"]
}Ketika token membawa app-backend + app-block-delete, akses ke secret/data/app/production/* menjadi deny (karena deny menang), sementara area lain dari secret/data/app/* tetap mengikuti app-backend. Pola allow-list + block-list seperti ini sangat ampuh untuk emergency lockdown tanpa harus menulis ulang policy utama.
Mari kita satukan semuanya dalam skenario sederhana. Misalkan Vault dipakai oleh tiga kelompok dengan kebutuhan berbeda:
| Kelompok | Kebutuhan | Policy |
|---|---|---|
| Aplikasi backend | Baca config app, ambil DB creds, issue cert | app-backend |
| Developer | Baca + tulis config di env staging, list semua | dev-kv |
| Admin platform | Kelola Vault itu sendiri | admin |
path "secret/data/staging/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/staging/*" {
capabilities = ["list"]
}path "sys/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}vault policy write app-backend policy/app-backend.hcl
vault policy write dev-kv policy/dev-kv.hcl
vault policy write admin policy/admin.hclLalu buat token untuk masing-masing dan uji dengan vault token capabilities:
export VAULT_TOKEN=$(vault token create -policy=dev-kv -format=json | jq -r '.auth.client_token')
vault token capabilities secret/data/staging/api-key
vault token capabilities secret/data/production/api-keyOutput yang diharapkan: token dev bisa create read update delete list di path staging, dan deny di path production.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa subpath data/ untuk kv-v2 | vault kv get gagal meskipun policy terlihat benar | Ingat: data di secret/data/, metadata di secret/metadata/ |
| Prefix wildcard tidak mencakup path induk | Akses ke path itu sendiri ditolak | Tulis dua blok: path induk + path induk /* |
Memberi list padahal ingin baca | User bisa lihat nama secret tapi tidak isinya | Tambahkan capability read bila diperlukan |
deny diletakkan di policy yang tidak dipakai token | Blokade tidak berlaku | Pastikan deny menang: letakkan deny di policy yang selalu dilampirkan |
Capability kurang (misal read tanpa list) | Operasi list gagal | Periksa path mana yang disentuh perintah via vault token capabilities |
Policy terlalu luas (* dengan ["read"]) | Radius ledakan membesar jika token bocor | Persempit ke path spesifik dan gunakan least privilege |
| Lupa menerapkan ulang setelah edit file | Policy lama masih aktif | vault policy write ulang setelah mengubah file |
Tip
Jadikan vault token capabilities <token> <path> sebagai ritual kalian sebelum men-deploy policy baru. Verifikasi dulu akses yang seharusnya diizinkan benar, lalu cek bahwa akses yang seharusnya ditolak memang ditolak. Dua baris perintah ini menghemat berjam-jam debugging di produksi.
Pada episode ini kita telah membahas fondasi otorisasi Vault: policy berbasis HCL dengan blok path dan capabilities, perbedaan create/read/update/delete/list/sudo/deny, aturan path matching (exact, prefix wildcard, segment wildcard), cara menerapkan policy dengan vault policy write, dan mengujinya dengan vault token capabilities. Kita juga mengupas pitfall klasik seperti lupa subpath data/ pada KV v2, deny yang selalu menang, dan union semantics saat token membawa banyak policy.
Inti dari episode ini adalah satu prinsip: policy adalah batas keamanan terluar dan terpenting di Vault. Menulisnya dengan prinsip least privilege — mulai dari path tersempit, beri capability seminimal mungkin, lalu longgarkan hanya saat benar-benar dibutuhkan — adalah kebiasaan yang akan menyelamatkan kalian dari banyak insiden.
Di episode 10 selanjutnya, kita akan menjawab pertanyaan "siapa" yang kita kunci di episode ini: Authentication Methods (Token, Userpass, AppRole, OIDC) — bagaimana pengguna dan mesin mengidentifikasi diri ke Vault untuk mendapatkan token yang dibatasi policy tadi. Pastikan tetap semangat!