Mengelola secrets sebagai produk platform: Vault/OpenBao untuk storage dan rotasi, pola inject secrets yang benar di Kubernetes, policy engine OPA, serta prinsip secrets management yang mencegah kebocoran

Setelah di episode 9 kita membangun observability, saatnya mengamankan hal paling bernilai di platform: secrets. Database credentials, API keys, sertifikat TLS, dan service account tokens — setiap kali secret bocor, insidennya selalu parah dan biayanya tinggi. Ironisnya, di banyak organisasi, secrets management masih berupa file .env yang dikirim lewat chat dan disimpan di repo.
Mengapa secrets harus jadi platform? Karena secrets adalah masalah yang sama untuk semua tim — dan solusi yang terpisah-pisah justru menciptakan lebih banyak permukaan kebocoran. Platform engineer menyediakan satu mekanisme secrets yang aman, mudah dipakai, dan dirotasi otomatis, sehingga developer tidak punya alasan untuk menempel secret di kode.
Vault (HashiCorp) adalah standar industri pengelolaan secrets. Di 2025-2026 muncul OpenBao — fork open-source Vault yang dimulai setelah perubahan lisensi HashiCorp (BUSL). Konsep dan API-nya kompatibel luas; untuk organisasi yang menolak lisensi bisnis, OpenBao adalah pilihan utama.
Prinsip kerja Vault/OpenBao:
Ada beberapa pola menghubungkan Kubernetes dengan Vault/OpenBao. Yang paling populer: Vault Agent Injector (menggunakan mutating webhook) atau External Secrets Operator (mirroring secret ke Kubernetes Secret).
External Secrets Operator (ESO) mengambil secrets dari Vault dan me-mirror-nya sebagai Kubernetes Secret. Sederhana, familiar bagi developer (mereka tetap memakai Secret biasa), dan mendukung banyak provider.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: payments-api-db
namespace: team-payments-prod
spec:
secretStoreRef:
name: vault-store
kind: SecretStore
target:
name: payments-api-db
data:
- secretKey: DB_PASSWORD
remoteRef:
key: secrets/data/payments/db
property: passwordDengan ESO, developer cukup memakai Secret biasa (payments-api-db) — mereka tidak perlu tahu dari mana asalnya. Rotasi terjadi di Vault, ESO menyinkronkan perubahannya.
Injector menaruh secrets langsung ke filesystem container (di /vault/secrets/) saat Pod start. Cocok untuk aplikasi yang tidak bisa diubah untuk memakai SDK Vault.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
namespace: team-payments-prod
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "payments-api"
vault.hashicorp.com/agent-inject-secret-db: "secrets/data/payments/db"
vault.hashicorp.com/agent-inject-template-db: |
{{- with secret "secrets/data/payments/db" -}}
export DB_PASSWORD={{ .Data.data.password }}
{{- end }}
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "payments-api"
vault.hashicorp.com/agent-inject-secret-db: "secrets/data/payments/db"
spec:
serviceAccountName: payments-api
containers:
- name: app
image: registry.example.com/payments-api:latestNote
Pilih ESO untuk sebagian besar kasus (developer tetap memakai Secret biasa, sederhana) dan Agent Injector untuk kasus spesifik (TTL per-Pod, aplikasi yang butuh secret sebagai file). Hindari menjalankan keduanya untuk hal yang sama — dua mekanisme secrets aktif berarti dua permukaan salah konfigurasi.
Secrets management yang baik dimulai dari kebijakan akses yang ketat. Contoh policy di OpenBao:
path "secrets/data/payments/*" {
capabilities = ["read"]
}
path "secrets/metadata/payments/*" {
capabilities = ["list"]
}Kebijakan ini mengikuti least privilege: tim payments hanya bisa membaca secrets miliknya sendiri. Pola penting:
| Praktik | Aturan |
|---|---|
| Path convention | secrets/data/{team}/{service} |
| Kebijakan per tim | Satu policy per tim, binding ke identity (K8s service account) |
| Audit | Log semua akses via audit device |
| Versioning | Aktifkan versioning secrets (rollback saat salah tulis) |
Kubernetes auth method menghubungkan service account di cluster dengan kebijakan di Vault:
path "auth/kubernetes/login" {
capabilities = ["create", "update"]
}
path "auth/kubernetes/role/payments-api" {
capabilities = ["read"]
}Dan role di Vault memetakan service account ke policy:
vault write auth/kubernetes/role/payments-api \
bound_service_account_names=payments-api \
bound_service_account_namespaces=team-payments-prod \
policies=payments-api \
ttl=1hDengan ini, tidak ada secret statis yang disimpan lama — setiap Pod meminta token ke Vault dengan masa hidup 1 jam, dan identitasnya diverifikasi dari service account.
Selain secrets, platform juga butuh kebijakan keamanan yang konsisten. Open Policy Agent (OPA) adalah policy engine yang bisa menolak atau mengizinkan permintaan berdasarkan kebijakan yang ditulis sebagai kode (Rego). Di Kubernetes ia berjalan sebagai Gatekeeper.
Contoh kebijakan: menolak container yang minta privilege berlebih.
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
container.securityContext.privileged
msg := sprintf("Container %v dilarang privileged", [container.name])
}Policy-as-code ini melengkapi story keamanan: Vault mengamankan secrets, OPA mengamankan konfigurasi. Keduanya bersama-sama menutup celah yang sering dieksploitasi. Kita akan memperdalam policy-as-code dan compliance di episode 16.
Rangkuman aturan yang seharusnya menjadi kebijakan platform:
LOG_LEVEL sanitization; alert jika pola secret terdeteksi di log.Kekuatan Vault yang paling dicari: dynamic database credentials — setiap aplikasi dapat kredensial sementara, bukan password statis.
vault secrets enable database
vault write database/config/payments-db \
plugin_name=postgresql-database-plugin \
allowed_roles="payments-ro" \
connection_url="postgresql://{{username}}:{{password}}@db:5432/payments"
vault write database/roles/payments-ro \
db_name=payments-db \
creation_statements="CREATE ROLE \"{{name}}\" LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"Setiap Pod meminta kredensial yang valid 1 jam, lalu hilang. Tidak ada lagi password database yang bocor dari file .env berusia 3 tahun.
Warning
Hati-hati dengan "security theater": memasang Vault tapi tetap menaruh secret di repo bukan keamanan — hanya kepatuhan tampilan. Kebijakan nyata dimulai dari: tidak ada secret di repo, dan ada cara otomatis mendeteksi pelanggaran (misal gitleaks di pipeline CI).
.env terenkripsi.Inti yang harus dibawa pulang:
Di episode 11 selanjutnya kita mengelola sumber daya yang semakin penting di 2026: cost platform & FinOps — dashboards biaya, showback/chargeback, dan optimasi yang membuat platform tidak hanya hebat secara teknis, tapi juga hemat secara finansial!