Secret Management Best Practice #
Mengelola Secret di Kubernetes production bukan sekadar membuat resource Secret dan berharap yang terbaik. Kubernetes Secret by default hanya di-encode base64 — bukan dienkripsi, tidak ada rotasi otomatis, tidak ada audit log per-akses. Artikel ini membahas praktik yang membuat pengelolaan Secret benar-benar aman di lingkungan production.
Layer Keamanan Secret #
Secret yang aman membutuhkan perlindungan di beberapa lapisan:
Lapisan 1: Enkripsi saat disimpan (at-rest)
→ Data di Etcd dienkripsi, bukan hanya di-encode base64
→ Butuh konfigurasi tambahan di API Server
Lapisan 2: Enkripsi saat transit (in-transit)
→ Semua komunikasi API Server sudah TLS by default
→ Pastikan tidak ada komunikasi non-TLS di cluster
Lapisan 3: Akses control (RBAC)
→ Batasi siapa yang bisa get/list/watch Secret
→ Principle of least privilege
Lapisan 4: Audit log
→ Catat setiap akses ke Secret
→ Deteksi akses yang tidak normal
Lapisan 5: Rotasi
→ Secret yang tidak pernah dirotasi adalah risiko permanen
→ Otomatisasi rotasi, jangan bergantung pada proses manual
Enkripsi at-Rest #
Aktifkan enkripsi Secret di Etcd menggunakan KMS provider dari cloud:
# EncryptionConfiguration untuk cluster self-managed
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
# Enkripsi dengan AWS KMS (via kms plugin)
- kms:
name: aws-kms
endpoint: unix:///var/run/kmsplugin/socket.sock
cachesize: 1000
timeout: 3s
# Fallback untuk membaca Secret lama yang belum terenkripsi
- identity: {}
# Setelah konfigurasi diterapkan, enkripsi semua Secret yang ada
kubectl get secrets -A -o json | kubectl replace -f -
# Ini akan re-write semua Secret ke Etcd dengan enkripsi baru
RBAC untuk Secret: Principle of Least Privilege #
# Role yang hanya bisa baca Secret tertentu
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: api-secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials", "api-keys"] # hanya Secret tertentu
verbs: ["get"] # hanya get, bukan list atau watch
---
# Bind ke ServiceAccount aplikasi
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: api-secret-binding
namespace: production
subjects:
- kind: ServiceAccount
name: api-service-account
namespace: production
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: api-secret-reader
# Audit: siapa yang bisa akses Secret di namespace production?
kubectl auth can-i get secrets -n production --as=system:serviceaccount:production:api-sa
# List semua binding yang memberikan akses ke Secret
kubectl get rolebindings,clusterrolebindings -A -o json | \
jq '.items[] | select(.rules[]?.resources[]? == "secrets")'
External Secret Manager #
Untuk production yang serius, simpan nilai sensitif di luar cluster dan sync ke Kubernetes Secret secara otomatis. Tiga opsi populer:
External Secrets Operator (ESO) #
ESO adalah operator yang mensinkronkan Secret dari berbagai secret manager ke Kubernetes Secret:
# SecretStore — koneksi ke AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-store
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: ap-southeast-1
auth:
serviceAccount:
name: eso-service-account
---
# ExternalSecret — definisikan Secret mana yang disync
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: "1h" # sync setiap 1 jam
secretStoreRef:
name: aws-secrets-store
kind: SecretStore
target:
name: db-credentials # nama Kubernetes Secret yang dibuat
creationPolicy: Owner # hapus Secret jika ExternalSecret dihapus
data:
- secretKey: username # key di Kubernetes Secret
remoteRef:
key: production/db # path di AWS Secrets Manager
property: username # property spesifik dalam secret
- secretKey: password
remoteRef:
key: production/db
property: password
HashiCorp Vault dengan Vault Agent Injector #
Vault menyuntikkan Secret langsung ke filesystem Pod via sidecar:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "api-server"
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/production/db"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "secret/data/production/db" -}}
DATABASE_URL=postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/appdb
{{- end }}
Vault Agent berjalan sebagai init container dan sidecar, mengambil Secret dari Vault dan menulis ke /vault/secrets/ di dalam Pod. Nilai Secret tidak pernah menyentuh Kubernetes etcd.
Rotasi Secret #
# ExternalSecret dengan rotasi otomatis
spec:
refreshInterval: "15m" # cek setiap 15 menit apakah ada versi baru
# Di AWS Secrets Manager, aktifkan automatic rotation:
aws secretsmanager rotate-secret \
--secret-id production/db \
--rotation-lambda-arn arn:aws:lambda:... \
--rotation-rules AutomaticallyAfterDays=30
Untuk rotasi yang tidak disruptif, aplikasi harus mendukung penggunaan credential lama selama periode transisi. Pola yang umum:
Rotasi credential database tanpa downtime:
1. Buat credential baru di database
2. Update Secret di secret manager dengan credential baru
3. ESO sync Secret baru ke Kubernetes
4. Pod yang sudah jalan masih pakai credential lama (dari env var atau mounted file)
5. Rolling restart Pod → Pod baru pakai credential baru
6. Setelah semua Pod restart, hapus credential lama dari database
Pola GitOps Aman untuk Secret #
Menyimpan Secret di Git repository adalah anti-pattern yang umum. Alternatif yang aman:
Opsi 1: Sealed Secrets (Bitnami)
→ Secret di-enkripsi sebelum masuk Git
→ SealedSecret object di-commit ke Git (aman karena terenkripsi)
→ Controller di cluster mendekripsi dan membuat Secret asli
Opsi 2: SOPS (Mozilla)
→ File Secret di-enkripsi dengan KMS, GPG, atau Age
→ File terenkripsi aman di-commit ke Git
→ Saat deploy, file di-dekripsi dan Secret dibuat
Opsi 3: External Secrets (rekomendasi untuk produksi)
→ Nilai Secret TIDAK ada di Git sama sekali
→ Di Git hanya ada ExternalSecret manifest yang menunjuk ke secret manager
→ Nilai aktual hanya ada di secret manager (Vault, AWS SM, dll)
# Contoh: SealedSecret yang aman di-commit ke Git
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
# Nilai ini terenkripsi dengan public key controller — aman di Git
username: AgBy8BbKJk9p8Y8SbOz...
password: AgCN7x2KBFiRFy...
Audit dan Monitoring #
# Aktifkan audit log di API Server untuk track akses Secret
# Tambahkan flag ke API Server:
# --audit-policy-file=/etc/kubernetes/audit-policy.yaml
# --audit-log-path=/var/log/kubernetes/audit.log
# audit-policy.yaml — log semua akses ke Secret
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log semua akses ke Secret dengan level Request (termasuk body)
- level: Request
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Untuk operasi read, cukup log metadata saja
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list"]
Ringkasan #
- Enkripsi at-rest adalah langkah pertama — aktifkan via KMS di cloud atau EncryptionConfiguration untuk cluster self-managed; base64 bukan enkripsi.
- RBAC dengan
resourceNames— batasi akses ke Secret spesifik yang dibutuhkan, bukan semua Secret di namespace; hindari permissionlistdanwatchkecuali benar-benar diperlukan.- External Secrets Operator untuk produksi — nilai sensitif dikelola di AWS Secrets Manager, Vault, atau GCP Secret Manager; Kubernetes Secret hanya berisi nilai yang disync otomatis.
- Rotasi otomatis, bukan manual — credential database dan API key harus dirotasi secara berkala; otomatisasi dengan secret manager rotation + ESO refreshInterval.
- Jangan simpan Secret di Git — gunakan Sealed Secrets, SOPS, atau External Secrets; nilai aktual tidak boleh ada di Git meski dalam bentuk terenkripsi base64.
- Audit log untuk semua akses Secret — konfigurasi API Server audit policy untuk mencatat setiap akses ke Secret; kirim ke SIEM atau log aggregation untuk monitoring.
← Sebelumnya: Environment Variable Pattern Berikutnya: External Secret Manager →