Di episode ini kita akan mengeksplorasi Kubernetes Secret — mekanisme native untuk menyimpan dan mengonsumsi data sensitif seperti password, API key, dan sertifikat TLS. Kamu akan belajar cara kerja Secret, tipe-tipenya, keterbatasan keamanannya, dan cara menggunakannya dengan aman di production.

Catatan
Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 21.1 di bawah ini
Di episode sebelumnya, kita sudah membahas secara mendalam PersistentVolume dan PersistentVolumeClaim untuk durable storage. Di episode ini (21.2), kita akan fokus pada Secret — objek native Kubernetes untuk mengelola data sensitif seperti password database, API token, sertifikat TLS, dan SSH key.
Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.
Hampir semua aplikasi nyata membutuhkan credential dalam bentuk tertentu. Tanpa mekanisme yang tepat, developer sering kali hardcode secret di dalam container image atau di daftar environment variable, menciptakan risiko keamanan yang serius. Kubernetes Secret memberimu cara yang terstruktur dan berbasis API untuk memisahkan data sensitif dari kode dan konfigurasi aplikasi.
Secret adalah API object Kubernetes yang menyimpan sejumlah kecil data sensitif dalam pasangan key-value. Secret disimpan di etcd dan bisa dikonsumsi oleh Pod sebagai environment variable, file melalui volume, atau digunakan oleh kubelet untuk menarik container image dari private registry.
Bayangkan Secret seperti brankas terkunci yang hanya bisa dibuka oleh orang yang berwenang. Aplikasi tidak membawa vault password-nya sendiri — ia meminta akses ke vault saat runtime.
Karakteristik kunci Secret:
EncryptionConfigurationWarning
Kubernetes Secret secara default hanya di-encode base64, bukan dienkripsi. Siapapun yang memiliki akses kubectl get secret atau akses baca ke etcd bisa men-decode-nya dengan mudah. Selalu aktifkan enkripsi at rest dan gunakan RBAC yang ketat di production.
Baik Secret maupun ConfigMap menyimpan data key-value. Perbedaannya ada pada tujuan dan cara penanganannya:
| Fitur | Secret | ConfigMap |
|---|---|---|
| Dirancang untuk | Data sensitif | Konfigurasi non-sensitif |
| Format penyimpanan | Base64-encoded | Plain text |
| Enkripsi at rest | Didukung (opt-in) | Biasanya tidak dienkripsi |
Di-mount sebagai tmpfs | Ya (in-memory) | Tidak |
| Granularitas RBAC | Resource secrets | Resource configmaps |
Terlihat di kubectl describe pod | Value disembunyikan | Value ditampilkan |
Gunakan ConfigMap untuk konfigurasi aplikasi. Gunakan Secret untuk apapun yang akan berbahaya jika bocor: password, token, sertifikat, private key.
Kubernetes mendukung beberapa tipe Secret bawaan, masing-masing dirancang untuk use case tertentu.
Tipe default untuk data arbitrary yang didefinisikan user.
apiVersion: v1
kind: Secret
metadata:
name: app-credentials
namespace: default
type: Opaque
data:
username: YWRtaW4= # base64 dari "admin"
password: c2VjcmV0cGFzcw== # base64 dari "secretpass"Atau gunakan stringData untuk memberikan value dalam plain text (Kubernetes akan meng-encode-nya secara otomatis):
apiVersion: v1
kind: Secret
metadata:
name: app-credentials
type: Opaque
stringData:
username: admin
password: secretpassTip
Lebih baik gunakan stringData di manifest kamu — lebih mudah dibaca dan menghindari kesalahan encoding base64 secara manual. Kubernetes akan secara otomatis mengonversi value ke base64 saat menyimpannya.
Digunakan untuk sertifikat TLS dan private key. Dikonsumsi oleh Ingress controller dan komponen lain yang mendukung TLS.
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: default
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-private-key>Membuat TLS secret dari file lebih umum dilakukan:
sudo kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.keyDigunakan untuk autentikasi ke private container image registry.
sudo kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=me@example.comReferensikan di spec Pod:
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/myapp:latestDibuat secara otomatis oleh Kubernetes untuk ServiceAccount. Digunakan oleh Pod untuk autentikasi ke API server. Cluster modern menggunakan projected service account token sebagai gantinya.
| Tipe | Use Case |
|---|---|
Opaque | Secret key-value generik |
kubernetes.io/tls | Sertifikat dan key TLS |
kubernetes.io/dockerconfigjson | Autentikasi registry |
kubernetes.io/basic-auth | Username/password basic auth |
kubernetes.io/ssh-auth | SSH private key |
kubernetes.io/service-account-token | Token ServiceAccount |
bootstrap.kubernetes.io/token | Token bootstrap node |
# Dari nilai literal
sudo kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret
# Dari file
sudo kubectl create secret generic app-certs \
--from-file=ca.crt \
--from-file=server.crt \
--from-file=server.keyapiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
labels:
app: myapp
env: production
type: Opaque
stringData:
DB_HOST: postgres.production.svc.cluster.local
DB_PORT: "5432"
DB_NAME: myappdb
DB_USER: appuser
DB_PASSWORD: Sup3rS3cur3P@ssw0rd!Caution
Jangan pernah commit manifest Secret dengan value asli ke version control. Riwayat git bersifat permanen. Gunakan Sealed Secrets, External Secrets Operator, atau Vault untuk mengelola secret sebagai kode dengan aman. Lebih lanjut tentang ini di bagian best practice.
Secret bisa dikonsumsi dengan dua cara: environment variable atau volume mount. Masing-masing memiliki trade-off yang berbeda.
Pola paling sederhana — injeksi value secret langsung sebagai environment variable container.
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERapiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- secretRef:
name: db-credentialsIni me-mount setiap key di db-credentials sebagai environment variable. Nama key menjadi nama variabel.
Warning
Environment variable terlihat di process listing (/proc/<pid>/environ) dan mungkin ter-log oleh library third-party atau laporan crash. Untuk keamanan maksimal, lebih baik gunakan volume mount untuk credential sensitif.
Secret yang di-mount sebagai volume disimpan di tmpfs (filesystem in-memory), yang lebih aman daripada disk. Setiap key menjadi sebuah file di dalam direktori mount.
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: credentials
mountPath: /etc/secrets
readOnly: true
volumes:
- name: credentials
secret:
secretName: db-credentials
defaultMode: 0400 # hanya owner yang bisa bacaDi dalam container, file-nya akan menjadi:
/etc/secrets/DB_HOST
/etc/secrets/DB_PORT
/etc/secrets/DB_NAME
/etc/secrets/DB_USER
/etc/secrets/DB_PASSWORDvolumes:
- name: credentials
secret:
secretName: db-credentials
items:
- key: DB_PASSWORD
path: password.txt
mode: 0400
- key: DB_USER
path: username.txt
mode: 0444Ini hanya me-mount key yang ditentukan, dengan nama file dan permission yang dikustomisasi.
| Aspek | Environment Variable | Volume Mount |
|---|---|---|
| Visibilitas di proses | cat /proc/self/environ | Tidak terekspos |
| Penyimpanan | Memory (env) | tmpfs (in-memory) |
| Update dinamis | Perlu restart Pod | Update tanpa restart |
| Permission file | N/A | Bisa dikonfigurasi (mode) |
| Audit trail | Lebih sulit diaudit | Lebih mudah (logging akses file) |
| Terbaik untuk | Aplikasi sederhana, kompatibilitas lama | Production, keamanan tinggi |
Pola yang umum: injeksi credential database secara aman ke dalam web application.
apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
namespace: production
type: Opaque
stringData:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: Sup3rS3cur3P@ss!
POSTGRES_DB: myappdbapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- myapp.example.com
secretName: tls-secret
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 8080apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
imagePullSecrets:
- name: regcredMelampirkan pull secret ke ServiceAccount berarti semua Pod yang menggunakan ServiceAccount ini secara otomatis mendapatkan akses ke private registry — tidak perlu menentukan imagePullSecrets di setiap Pod.
# Metode 1: Edit langsung
sudo kubectl edit secret db-credentials -n production
# Metode 2: Apply manifest yang diperbarui
sudo kubectl apply -f db-credentials.yml
# Metode 3: Patch key tertentu (encode base64 dulu)
NEW_PASS=$(echo -n "newpassword" | base64)
sudo kubectl patch secret db-credentials \
-p "{\"data\":{\"DB_PASSWORD\":\"${NEW_PASS}\"}}"Note
Pod yang mengonsumsi Secret sebagai volume mount akan melihat secret yang diperbarui dalam kurang lebih 1 menit (periode sync kubelet) tanpa restart. Pod yang mengonsumsi Secret sebagai environment variable memerlukan restart untuk mengambil nilai baru.
Secara default, Kubernetes menyimpan Secret di etcd sebagai plain text yang di-encode base64. Siapapun yang memiliki akses etcd bisa membacanya. Untuk mengaktifkan enkripsi at rest yang sebenarnya, konfigurasikan EncryptionConfiguration.
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}Terapkan ini dengan meneruskan flag --encryption-provider-config ke API server. Setelah diaktifkan, enkripsi secret yang sudah ada:
# Tulis ulang semua secret yang ada untuk memicu enkripsi
sudo kubectl get secrets --all-namespaces -o json | \
sudo kubectl replace -f -Important
Layanan Kubernetes terkelola (GKE, EKS, AKS) biasanya menyediakan enkripsi at rest untuk Secret secara default menggunakan KMS cloud mereka. Selalu verifikasi bahwa ini diaktifkan di dokumentasi cloud provider kamu.
Problem: Commit file YAML Secret dengan value asli ke version control. Value tersebut tetap ada di riwayat git bahkan setelah dihapus.
Solusi: Gunakan salah satu pendekatan ini:
# JANGAN commit file yang berisi secret asli
git add db-credentials.yml # ❌
git commit -m "add database secret"Problem: Memberikan akses get secrets yang luas mengekspos semua secret di Namespace.
Solusi: Ikuti least privilege. Berikan akses hanya ke secret tertentu:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: db-secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-credentials"] # hanya secret tertentu
verbs: ["get"]Problem: Environment variable terlihat oleh semua proses di container dan mungkin ter-log.
Solusi: Mount data sensitif sebagai file volume dengan permission yang ketat:
# Kurang aman: env var (terlihat di /proc)
env:
- name: PRIVATE_KEY
valueFrom:
secretKeyRef:
name: app-secret
key: private_key
# Lebih aman: volume mount (tmpfs, file permission)
volumeMounts:
- name: app-secret
mountPath: /run/secrets
readOnly: trueProblem: File Secret yang di-mount di volume bisa dibaca oleh semua user di container.
Solusi: Set defaultMode yang restrictif dan mode per-item:
volumes:
- name: credentials
secret:
secretName: db-credentials
defaultMode: 0400 # hanya owner yang bisa bacaProblem: Credential statis yang berumur panjang adalah beban keamanan. Credential yang bocor tetap valid tanpa batas waktu.
Solusi: Implementasikan rotasi secret. Gunakan token berumur pendek jika memungkinkan (misalnya Vault dynamic secrets, AWS IAM roles for service accounts).
Jangan kelola secret secara native di Kubernetes dalam skala besar. Gunakan backend secrets eksternal:
# External Secrets Operator sinkronisasi dari AWS SSM
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: /production/myapp/db-passwordSelalu aktifkan EncryptionConfiguration yang mengarah ke KMS provider (bukan hanya aescbc dengan static key) di production.
Batasi siapa yang bisa list dan get secret:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader-restricted
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
# Tanpa verb "list" — mencegah enumerasi massalTip
Menghapus verb list mencegah user dari enumerasi semua nama Secret di Namespace — langkah defense-in-depth yang berguna meskipun mereka bisa get secret tertentu berdasarkan nama.
Selalu tambahkan readOnly: true pada volume mount Secret:
volumeMounts:
- name: credentials
mountPath: /etc/secrets
readOnly: trueAktifkan Kubernetes audit logging dengan policy yang menangkap operasi get dan watch pada Secret:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]Tandai Secret sebagai immutable untuk mencegah modifikasi yang tidak disengaja dan mengurangi beban API server (Kubernetes berhenti memantau perubahan):
apiVersion: v1
kind: Secret
metadata:
name: app-credentials
type: Opaque
immutable: true
stringData:
API_KEY: my-api-key-valueKubernetes Secret nyaman tapi tidak selalu alat yang tepat:
Note
Kubernetes Secret sudah cukup untuk deployment sederhana dan development. Untuk production dalam skala besar, selalu lapisi dengan enkripsi yang tepat, manajemen secret eksternal, dan RBAC yang ketat.
Pada episode 21.2 ini, kita telah membahas Kubernetes Secret secara komprehensif — mulai dari apa itu dan tipe-tipenya, cara membuat dan mengonsumsinya, hingga cara mengamankannya dengan baik di production.
Key takeaway:
Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjsonlist yang luasimmutable: true untuk secret yang tidak berubah untuk mengurangi overhead API serverManajemen secret yang tepat adalah salah satu aspek paling kritis dari sisi keamanan dalam menjalankan Kubernetes di production. Melakukannya dengan benar sejak awal akan menghindarkanmu dari insiden keamanan yang menyakitkan di kemudian hari.
Bagaimana, makin jelas kan bagaimana Kubernetes mengelola data sensitif? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!
Catatan
Untuk kalian yang ingin melanjutkan ke episode selanjutnya, bisa click thumbnail episode 22 di bawah ini