Di episode ini kita akan membahas Dynamic Secrets — paradigma di mana kredensial database tidak lagi permanen, melainkan di-generate on-demand dengan TTL pendek dan otomatis hancur. Kita akan mengintegrasikan Vault dengan PostgreSQL, menulis roles, dan membaca kredensial dinamis.

Setelah di episode 4 sebelumnya kita membahas KV Secrets Engine — cara menyimpan rahasia statis dengan versioning dan recovery — pada episode kali ini kita akan naik satu level lebih tinggi: Dynamic Database Secrets Engine. Inilah salah satu fitur yang paling membedakan Vault dari sekadar "tempat simpan password", dan alasan utama banyak perusahaan memilih Vault dibanding secret manager statis lainnya.
Kenapa topik ini penting di dunia nyata? Karena kredensial database statis adalah salah satu risiko keamanan terbesar di era modern. Password database yang dibuat sekali dan dipakai bertahun-tahun — tersebar di config files, spreadsheet, atau bahkan commit Git — adalah mimpi buruk bagi tim security. Ketika satu karyawan keluar atau satu server diretas, kredensial itu harus dirotasi secara manual ke seluruh sistem. Sering kali hal ini tidak pernah terjadi.
Dynamic secrets mengubah total paradigma ini: kredensial tidak lagi "disimpan", melainkan "dipinjam". Bayangkan perbedaan antara memberikan kartu identitas permanen kepada setiap orang di kantor versus menyediakan kartu tamu yang otomatis kedaluwarsa di penghujung hari. Yang kedua jelas lebih aman — bocor sekali pun, kartu itu sudah tidak berguna. Mari kita bedah bagaimana Vault mewujudkannya.
Dynamic secrets adalah kredensial yang di-generate oleh Vault secara on-demand, diberikan kepada aplikasi saat dibutuhkan, dan otomatis hancur (revoke) ketika masa pakainya (TTL) berakhir.
Alurnya seperti ini:
vault read database/creds/my-role).Ini berarti database kalian dipenuhi oleh user-user sementara yang lahir dan mati secara otomatis — tanpa campur tangan manusia. Keuntungan terbesarnya:
| Aspek | Kredensial Statis | Dynamic Secrets |
|---|---|---|
| Masa hidup | Bertahun-tahun / permanen | Menit hingga jam (sesuai TTL) |
| Saat bocor | Berbahaya — butuh rotasi manual darurat | Hampir tidak berbahaya — otomatis kedaluwarsa |
| Rotasi | Manual, sering terlupakan | Otomatis, setiap kali lease habis |
| Visibilitas | Tersebar di banyak file/sistem | Terpusat di Vault + audit log |
| Manajemen user | DBA membuat user manual | Vault membuat & menghapus otomatis |
Important
Perbedaan filosofis kuncinya: KV menyimpan nilai rahasia, sedangkan Database engine menciptakan rahasia. Vault tidak pernah menyimpan kredensial dinamis — ia menyimpan resep cara membuatnya, dan setiap permintaan menghasilkan user baru yang unik.
Untuk praktik episode ini, pastikan ada instance PostgreSQL (atau MySQL) yang bisa dijangkau Vault. Jika menggunakan Docker:
docker run -d --name vault-demo-db \
-e POSTGRES_USER=vault_admin \
-e POSTGRES_PASSWORD=vault_admin_pass \
-e POSTGRES_DB=appdb \
-p 5432:5432 \
postgres:16Caution
Vault memerlukan user dengan hak tinggi (biasanya superuser atau dengan GRANT OPTION) untuk bisa membuat/menghapus user lain. Pada PostgreSQL, user yang dikonfigurasi di Vault harus punya hak CREATEROLE dan CREATE DATABASE (atau akses ke database target), karena Vault akan menjalankan statement SQL seperti CREATE ROLE ... LOGIN PASSWORD '...'.
vault secrets enable databaseOutput:
Success! Enabled the database secrets engine at: database/Selanjutnya beri tahu Vault bagaimana cara terhubung ke database kita. Ini disebut database connection config:
vault write database/config/postgres \
plugin_name="postgresql-database-plugin" \
allowed_roles="my-role" \
connection_url="postgresql://{{username}}:{{password}}@127.0.0.1:5432/appdb" \
username="vault_admin" \
password="vault_admin_pass"Output:
Success! Data written to: database/config/postgresUntuk MySQL, perintahnya hampir sama, hanya plugin dan connection URL yang berbeda:
vault write database/config/mysql \
plugin_name="mysql-database-plugin" \
allowed_roles="my-role" \
connection_url="{{username}}:{{password}}@tcp(127.0.0.1:3306)/appdb" \
username="root" \
password="root_pass"Tip
Perhatikan placeholder {{username}} dan {{password}} di connection_url. Vault akan mengisi otomatis placeholder tersebut dengan kredensial admin yang kalian tulis sebagai argumen username dan password — sehingga kalian tidak perlu menuliskan kredensial admin secara literal dua kali di dalam string URL.
Connection config saja tidak cukup — Vault butuh role yang mendefinisikan jenis kredensial apa yang akan dibuat dan berapa lama masa pakainya. Role berisi creation statements (SQL yang dieksekusi saat membuat user) dan default TTL.
vault write database/roles/my-role \
db_name="postgres" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"Output:
Success! Data written to: database/roles/my-roleDi balik layar, Vault mengganti placeholder dalam creation statements:
| Placeholder | Diganti dengan | Contoh hasil |
|---|---|---|
{{name}} | Nama user acak yang dibuat Vault | v-token-my-role-1aB2c3D4 |
{{password}} | Password acak yang dibuat Vault | e2Jkf9sLpQx... |
{{expiration}} | Waktu kedaluwarsa user (ISO) | 2026-08-02T09:15:00+00:00 |
Warning
Di PostgreSQL, statement VALID UNTIL '{{expiration}}' bukan sekadar hiasan — ia membuat user tersebut kedaluwarsa di level database pada waktu yang sama dengan lease Vault. Ini lapisan keamanan ganda: bahkan jika lease Vault gagal direvoke karena suatu alasan, database sendiri akan menolak login user setelah waktu kedaluwarsa. Selalu sertakan placeholder {{expiration}} di creation statements.
Inilah momen paling memuaskan. Minta kredensial ke Vault:
vault read database/creds/my-roleOutput:
Key Value
--- -----
lease_id database/creds/my-role/7k3x0qWXy9LpQz8VfR2nA4dE
lease_duration 1h
lease_renewable true
password e2Jkf9sLpQxZ7mN4vB8cW1tY6uI3oP5q
username v-token-my-role-1aB2c3D4Perhatikan tiga hal penting:
lease_id — identitas unik dari "peminjaman" kredensial ini. Lease inilah yang memungkinkan Vault melacak dan merevoke kredensial terkait.lease_duration: 1h — masa pakai sesuai default_ttl yang kita set di role.username: v-token-my-role-... — user baru yang benar-benar dibuat di database PostgreSQL kita.Untuk membuktikan bahwa user itu benar-benar ada, cek di PostgreSQL:
SELECT rolname, rolvaliduntil FROM pg_roles WHERE rolname LIKE 'v-token%';Output:
rolname | rolvaliduntil
----------------------------------+----------------------------
v-token-my-role-1aB2c3D4 | 2026-08-02T09:15:00+00:00Kolom rolvaliduntil menunjukkan bahwa user ini memang diberi batas waktu — bukti nyata konsep short-lived credentials.
Bukti paling meyakinkan adalah menggunakannya untuk benar-benar terhubung ke database:
PGPASSWORD="e2Jkf9sLpQxZ7mN4vB8cW1tY6uI3oP5q" \
psql -h 127.0.0.1 -U v-token-my-role-1aB2c3D4 -d appdb -c "SELECT current_user;"Output:
current_user
--------------
v-token-my-role-1aB2c3D4Inilah yang terjadi di aplikasi nyata setiap kali service membutuhkan akses database: meminta kredensial ke Vault, memakai kredensial itu untuk membuka koneksi, lalu membiarkannya mati bersama lease. Satu hal yang perlu kalian catat baik-baik: jangan pernah menulis kredensial dinamis ini ke file konfigurasi atau environment yang persisten — ia memang dirancang untuk sementara. Jika aplikasi menyimpannya, kalian baru saja mengubah dynamic secret menjadi static secret.
Ketika lease mencapai akhir masa hidupnya, Vault melakukan revocation — ia menjalankan statement penghapusan yang didefinisikan saat setup. Vault secara otomatis menghasilkan revocation statements default berdasarkan plugin, yang pada dasarnya mengeksekusi DROP ROLE "nama-user" (PostgreSQL) atau DROP USER ... (MySQL).
Kalian juga bisa mencabut secara manual lebih cepat jika terjadi kebocoran:
vault lease revoke database/creds/my-role/7k3x0qWXy9LpQz8VfR2nA4dEAtau revoke semua kredensial dari role tertentu sekaligus:
vault lease revoke -prefix database/creds/my-roleImportant
Konsep lease ini akan dibahas tuntas di episode 12, tapi satu hal yang wajib kalian pahami sekarang: kredensial dinamis itu tidak kekal. Aplikasi yang memakainya harus mampu meminta kredensial baru secara berkala (renewal) atau memulai ulang koneksinya sebelum TTL habis. Inilah yang disebut connection lifecycle management — salah satu aspek tersulit dalam mengadopsi dynamic secrets.
Ini jebakan klasik yang membuat tim menyerah pada dynamic secrets: sebuah koneksi TCP yang sudah dibuat tidak otomatis terputus saat user direvoke. Di PostgreSQL, setelah sebuah koneksi terautentikasi, koneksi tersebut tetap hidup walaupun rolenya di-DROP. Artinya aplikasi bisa saja terus memakai "user mati" selama koneksinya belum ditutup.
Dampaknya nyata: aplikasi yang memakai connection pool dengan koneksi berumur panjang akan terus berjalan dengan kredensial yang sudah direvoke — sekaligus menyimpan user-user "zombie" di database karena Vault tidak bisa menghapus user yang masih punya koneksi aktif (PostgreSQL menolak DROP ROLE jika role masih terpakai).
Strategi yang umum dipakai:
| Strategi | Cara Kerja | Cocok untuk |
|---|---|---|
| TTL pendek + reconnect | Aplikasi meminta kredensial baru tiap interval pendek, pool di-refresh | Aplikasi modern dengan retry logic |
max_conn_lifetime di pool | Batasi umur maksimal koneksi di connection pool (misal 50% TTL) | Go/Python/Node driver pool |
| Connection pooling layer (PgBouncer) | Pool di level middleware, kredensial diperbarui di sana | Arsitektur legacy yang sulit diubah |
| Static roles | User tetap dibuat tapi password dirotasi Vault | Aplikasi yang tidak bisa restart koneksi |
Kadang ada aplikasi yang tidak bisa memakai kredensial dinamis — misalnya aplikasi legacy yang melakukan koneksi sekali di startup dan tidak punya mekanisme reconnect. Untuk kasus ini Vault menyediakan static roles: user database dibuat permanen, tapi password-nya dirotasi oleh Vault secara berkala dan otomatis.
vault write database/static-roles/legacy-app \
db_name="postgres" \
username="legacy_app_user" \
rotation_statements="ALTER USER \"{{name}}\" WITH PASSWORD '{{password}}';" \
rotation_period="4h"Bedanya dengan dynamic role: nama user tetap (legacy_app_user), Vault yang meng-generate password baru setiap rotation_period. Aplikasi yang memakai kredensial ini harus mendapat password terbaru melalui mekanisme lain (misalnya Vault Agent template, yang akan kita bahas di episode 14-15).
Satu praktik keamanan yang sering terlupakan: kredensial admin yang kita berikan ke Vault di langkah konfigurasi (vault_admin/vault_admin_pass) juga seharusnya dirotasi — karena sekarang Vault yang memegangnya, dan Vault bisa merotasi kredensial root database-nya sendiri:
vault write -f database/rotate-root/postgresOutput:
Success! Data written to: database/rotate-root/postgresSetelah perintah ini, password vault_admin di PostgreSQL berubah menjadi password acak baru yang hanya diketahui Vault. Manusia dan sistem lain tidak lagi memegang kredensial admin database — hanya Vault yang bisa mengelolanya. Ini contoh sempurna prinsip least privilege dan single source of truth.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa placeholder {{expiration}} | User tidak kedaluwarsa di level DB | Selalu sertakan VALID UNTIL '{{expiration}}' |
allowed_roles tidak mencantumkan role | vault write database/roles/... ditolak | Pastikan allowed_roles berisi semua role yang akan dibuat |
| User admin Vault tidak punya hak CREATEROLE | Error permission saat create user | Beri user admin hak yang cukup di database |
| TTL lebih panjang dari kebutuhan | User "sampah" menumpuk di DB | Set default_ttl sependek mungkin (misal 1h) |
| Aplikasi tidak handle lease renewal | Koneksi putus mendadak saat TTL habis | Terapkan reconnect logic / perpendek TTL sesuai siklus |
| Connection URL salah host/port | Error connection refused | Uji koneksi dari server Vault dengan psql/mysql dulu |
Lupa plugin_name | Role gagal dibuat dengan error "unable to initialize connection" | Sebutkan plugin_name yang sesuai (postgresql-database-plugin / mysql-database-plugin) |
Pada episode 5 ini kita telah membahas konsep inti Dynamic Secrets: kredensial yang di-generate on-demand, ber-TTL pendek, dan otomatis hancur. Kalian sudah mempraktikkan seluruh alurnya — mengaktifkan database secrets engine, mengonfigurasi koneksi PostgreSQL/MySQL, menulis role dengan creation_statements, membaca kredensial dinamis beserta lease-nya, hingga memahami siklus hidup revocation dan jebakan koneksi yang tidak mati sendiri. Kita juga membahas static roles sebagai pelengkap untuk aplikasi legacy.
Inti episode ini: kredensial dinamis mengubah strategi keamanan dari "melindungi rahasia" menjadi "membuat rahasia bocor tidak lagi berguna". Ini pola pikir yang sama sekali berbeda dari sekadar menyimpan password di brankas.
Di episode 6 selanjutnya, kita akan membahas secrets engine yang melindungi lapisan yang lebih dalam: Transit Secrets Engine (Encryption-as-a-Service) — di mana Vault mengenkripsi dan mendekripsi data sensitif seperti nomor kartu kredit dan NIK tanpa pernah menyimpan datanya. Pastikan tetap semangat!