ConfigMap vs Secret #
Pertanyaan yang sering muncul saat mulai bekerja dengan Kubernetes: “Data ini masuk ConfigMap atau Secret?” Jawabannya tidak selalu hitam-putih. Artikel ini memberikan framework keputusan yang jelas, lengkap dengan contoh konkret dan anti-pattern yang perlu dihindari.
Framework Keputusan #
Pertanyaan pertama: Apakah nilai ini sensitif?
Sensitif berarti: jika orang yang tidak seharusnya membacanya,
bisa terjadi kerugian (data breach, akses tidak sah, kerugian finansial)
Ya → Gunakan SECRET
Tidak → Pertanyaan kedua
Pertanyaan kedua: Apakah nilai ini berbeda per environment?
Ya → Gunakan CONFIGMAP
Tidak → Bisa hardcode di manifest atau image
Klasifikasi cepat:
CONFIGMAP:
✓ URL database (bukan credentials-nya)
✓ Hostname dan port service lain
✓ Feature flags (dark_mode=true, new_ui=false)
✓ App settings (max_connections=100, timeout=30s)
✓ Log level (debug, info, warn, error)
✓ File konfigurasi (nginx.conf, app.yaml)
✓ Nama environment (production, staging)
✓ Resource limits aplikasi (thread pool size, queue size)
SECRET:
✓ Password database
✓ API key (Stripe, SendGrid, Twilio, dll)
✓ OAuth client secret
✓ JWT signing key
✓ TLS certificate dan private key
✓ SSH private key
✓ Token autentikasi (bearer token, service account token)
✓ Encryption key
✓ Webhook secret
Kasus Abu-abu #
Beberapa nilai tidak jelas masuk mana. Berikut panduan untuk kasus yang sering menimbulkan kebingungan:
Connection string lengkap:
"postgresql://user:password@host:5432/db"
→ SECRET, karena mengandung password
Connection string tanpa credentials:
"postgresql://host:5432/db"
→ CONFIGMAP, karena tidak ada informasi sensitif
Database name:
"production_db"
→ CONFIGMAP — nama database saja tidak sensitif
Encryption key (AES key, HMAC secret):
→ SECRET, meskipun "hanya" kunci enkripsi, nilai ini harus dilindungi
Internal service URL:
"http://payment-service.production:8080"
→ CONFIGMAP — URL internal tidak sensitif
→ Tapi jika URL mengandung API key dalam query string: SECRET
Debug flag:
DEBUG=true
→ CONFIGMAP — tidak ada risiko keamanan
Admin username (tapi bukan password):
ADMIN_USER=admin
→ Bisa CONFIGMAP, tapi pertimbangkan bahwa mengetahui username
bisa membantu brute force. Untuk high-security: Secret.
Perbandingan Teknis #
ConfigMap Secret
────────────────────────────────────────────────────────
Penyimpanan Plaintext di Etcd Base64 di Etcd*
Enkripsi Tidak Opsional (at-rest)**
kubectl describe Tampil nilai Hanya tampil ukuran
Ukuran maks 1 MB 1 MB
Auto-update Ya (volume) Ya (volume)
RBAC terpisah Tidak Ya (lebih granular)
Tipe khusus Tidak Ya (tls, docker, dll)
* Base64 bukan enkripsi — bisa di-decode oleh siapapun
** Aktifkan via EncryptionConfiguration atau cloud KMS
Anti-Pattern yang Sering Ditemui #
Anti-Pattern 1: Password di ConfigMap #
# ANTI-PATTERN: password masuk ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: database-config
data:
DB_HOST: "postgres-service"
DB_PORT: "5432"
DB_NAME: "appdb"
DB_PASSWORD: "SuperSecret123" # ← INI SALAH
Konsekuensinya: siapapun yang bisa kubectl get configmap bisa baca password. ConfigMap tidak punya proteksi RBAC yang sama dengan Secret.
# BENAR: pisahkan sensitif ke Secret
apiVersion: v1
kind: ConfigMap
data:
DB_HOST: "postgres-service"
DB_PORT: "5432"
DB_NAME: "appdb"
# DB_PASSWORD tidak ada di sini
---
apiVersion: v1
kind: Secret
metadata:
name: database-secret
type: Opaque
stringData:
DB_PASSWORD: "SuperSecret123" # ← di Secret
Anti-Pattern 2: Seluruh .env File di ConfigMap
#
# ANTI-PATTERN: file .env berisi semua config termasuk yang sensitif
data:
.env: |
APP_ENV=production
DB_HOST=postgres-service
DB_PASSWORD=secret123 # ← sensitif masuk ConfigMap
API_KEY=sk-abc123 # ← sensitif masuk ConfigMap
LOG_LEVEL=info
Seharusnya dipecah: nilai non-sensitif ke ConfigMap, nilai sensitif ke Secret. Inject keduanya ke dalam Pod secara terpisah.
Anti-Pattern 3: Hardcode Secret di Image #
# ANTI-PATTERN: build time secret masuk image layer
FROM python:3.11
ENV DATABASE_PASSWORD=SuperSecret123 # ← tersimpan permanen di image layer
COPY . /app
Secret dalam image bisa di-extract dari image history dan akan bocor jika image dipush ke registry publik atau registry yang kurang terlindungi.
Contoh Konfigurasi Lengkap yang Benar #
# ConfigMap untuk konfigurasi non-sensitif
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
namespace: production
data:
APP_ENV: "production"
LOG_LEVEL: "info"
DB_HOST: "postgres-service.production.svc.cluster.local"
DB_PORT: "5432"
DB_NAME: "appdb"
REDIS_HOST: "redis-service.production.svc.cluster.local"
REDIS_PORT: "6379"
MAX_CONNECTIONS: "50"
REQUEST_TIMEOUT: "30"
---
# Secret untuk kredensial dan kunci sensitif
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
namespace: production
type: Opaque
stringData:
DB_PASSWORD: "..."
REDIS_PASSWORD: "..."
JWT_SECRET_KEY: "..."
STRIPE_API_KEY: "..."
---
# Pod menggunakan keduanya
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: api
envFrom:
- configMapRef:
name: api-config # inject semua dari ConfigMap
- secretRef:
name: api-secrets # inject semua dari Secret
Ringkasan #
- Pertanyaan kunci: apakah exposure menyebabkan kerugian? — jika ya, gunakan Secret; jika tidak, ConfigMap sudah cukup.
- URL terpisah dari credentials — hostname dan port masuk ConfigMap; password dan API key masuk Secret; jangan gabungkan dalam satu connection string di ConfigMap.
- Base64 bukan alasan untuk taruh password di ConfigMap — encode base64 dan taruh di ConfigMap tidak lebih aman dari plaintext; gunakan Secret yang punya perlindungan RBAC berbeda.
- Jangan taruh secret di image layer —
ENV SECRET=valuedi Dockerfile tersimpan permanen di image history; gunakan ConfigMap/Secret saat runtime, bukan saat build.- Pisahkan file
.envmenjadi dua — nilai non-sensitif ke ConfigMap, nilai sensitif ke Secret; inject keduanya bersama ke Pod viaenvFrom.- Ukuran batas sama (1MB) — keduanya punya batas ukuran yang sama; untuk konfigurasi sangat besar, pertimbangkan split atau external config store.
← Sebelumnya: External Secret Manager Berikutnya: Configuration Hot Reload →