Secret #
Menyimpan data kredensial sensitif—seperti kata sandi basis data, kunci API (API keys), sertifikat SSL/TLS, token autentikasi, atau kunci SSH—memerlukan penanganan yang jauh lebih ketat dibandingkan dengan konfigurasi aplikasi biasa. Jika data ini bocor ke publik, konsekuensinya bisa sangat fatal bagi kelangsungan bisnis dan integritas data pengguna kita. Kubernetes memfasilitasi penyimpanan data sensitif ini melalui sebuah sumber daya khusus bernama Secret.
Secara struktural, Secret memiliki kemiripan dengan ConfigMap karena sama-sama menyimpan data dalam format kunci-nilai (key-value). Namun, Secret dirancang dengan mekanisme penanganan yang berbeda di tingkat sistem kluster, seperti pembatasan visual di baris perintah, penempatan memori yang aman, dan dukungan untuk enkripsi tingkat lanjut. Memahami cara kerja Secret secara mendalam sangat penting untuk menghindari rasa aman yang semu (false sense of security) yang sering kali menjebak para developer pemula di lingkungan produksi.
Perbedaan Secret dan ConfigMap #
Meskipun secara sepintas terlihat mirip, Secret dan ConfigMap memiliki peran dan karakteristik penanganan yang bertolak belakang.
Mari kita perhatikan tabel perbandingan komparatif di bawah ini untuk melihat bagaimana Kubernetes membedakan kedua objek tersebut secara mendalam:
| Karakteristik | ConfigMap | Secret |
|---|---|---|
| Sensitivitas Data | Data non-sensitif (URL host, setting aplikasi). | Data sangat sensitif (password, API keys, sertifikat). |
| Penyimpanan di etcd | Plain text (teks biasa). | Base64 encoded (atau terenkripsi jika enkripsi aktif). |
Output kubectl describe |
Menampilkan seluruh isi konfigurasi secara transparan. | Menyembunyikan nilai aktual, hanya menampilkan ukuran data. |
| Jejak Penyimpanan Node | Disimpan di dalam disk lokal node worker. | Disimpan di dalam memori RAM (tmpfs) node worker. |
| Validasi Skema | Tidak ada validasi tipe data bawaan. | Memiliki validasi format berdasarkan jenis/tipe Secret. |
PENTING: Base64 Bukanlah Enkripsi!
Banyak developer menganggap bahwa data di dalam objek Secret aman hanya karena data tersebut tidak dapat dibaca langsung (berbentuk karakter acak Base64). Base64 hanyalah metode pengodean (encoding) untuk mengubah teks biasa menjadi representasi karakter ASCII aman, bukan algoritma enkripsi. Siapa pun yang memiliki hak akses untuk membaca manifest YAML Secret dapat menerjemahkannya kembali (decode) ke teks asli secara instan menggunakan perintah base64 --decode.
Tipe-Tipe Secret Bawaan Kubernetes #
Kubernetes menyediakan beberapa tipe Secret bawaan (built-in types) yang masing-masing dilengkapi dengan aturan validasi khusus oleh API Server guna mencegah kesalahan penulisan parameter:
1. Opaque (Default)
#
Ini adalah tipe Secret default jika kita tidak menentukannya secara eksplisit. Tipe ini digunakan untuk menyimpan data kunci-nilai bebas yang tidak memiliki aturan validasi khusus, seperti konfigurasi kata sandi database atau token Slack webhook.
2. kubernetes.io/tls
#
Tipe ini dirancang khusus untuk menyimpan sertifikat SSL/TLS beserta kunci privatnya (private key). Kubernetes mewajibkan data di bawah tipe ini memuat kunci tls.crt (untuk sertifikat) dan tls.key (untuk kunci privat).
3. kubernetes.io/dockerconfigjson
#
Tipe ini digunakan untuk menyimpan kredensial autentikasi ke dalam registri kontainer privat (seperti Docker Hub, AWS ECR, atau GCP Artifact Registry). Data wajib disimpan di bawah kunci .dockerconfigjson dalam format JSON yang valid agar kubelet dapat menggunakannya saat melakukan proses penarikan citra kontainer (image pull).
4. kubernetes.io/service-account-token
#
Tipe ini digunakan oleh Kubernetes control plane untuk menyimpan token autentikasi yang digunakan oleh objek ServiceAccount agar dapat berinteraksi secara aman dengan API Server.
Metode Pembuatan Secret #
Sama seperti ConfigMap, kita dapat membuat Secret baik secara imperatif maupun secara deklaratif. Namun, untuk manifest deklaratif, kita wajib mengubah data teks biasa menjadi format Base64 terlebih dahulu.
Perbandingan Pembuatan Kredensial #
Berikut adalah perbedaan antara membuat kredensial YAML secara tidak aman karena menyimpan password plain text ke dalam repositori Git (anti-pattern) dengan cara pembuatan deklaratif yang benar (solusi):
# ANTI-PATTERN: Menulis password teks biasa di manifest YAML lalu menyimpannya ke Git
# ✗ Ini adalah celah keamanan fatal karena siapapun yang membaca Git bisa langsung mengetahui password kita.
apiVersion: v1
kind: Secret
metadata:
name: db-secret-bad
data:
password: "my-super-secret-password-123" # ✗ SALAH: Ini bukan format Base64 dan terekspos plain text!
---
# BENAR: Lakukan konversi Base64 di terminal tanpa menyertakan baris baru (newline)
# ✓ Gunakan parameter -n pada perintah echo agar karakter '\n' tidak ikut ter-encode.
echo -n "my-super-secret-password-123" | base64
# Output terminal: bXktc3VwZXItc2VjcmV0LXBhc3N3b3JkLTEyMw==
# Kemudian gunakan nilai tersebut di dalam manifest YAML deklaratif
apiVersion: v1
kind: Secret
metadata:
name: db-secret-good
namespace: production
type: Opaque
data:
password: bXktc3VwZXItc2VjcmV0LXBhc3N3b3JkLTEyMw== # ✓ BENAR: Nilai Base64 valid
Jika kita membuat Secret secara imperatif menggunakan perintah kubectl create secret, CLI kubectl akan secara otomatis melakukan proses pengodean Base64 untuk kita di belakang layar:
# Membuat Secret secara imperatif dari literal
kubectl create secret generic db-credentials \
--from-literal=username=app-admin \
--from-literal=password=S3cur3P@ssw0rd \
-n production
Menggunakan Secret sebagai Environment Variable #
Kita dapat menyuntikkan data Secret ke dalam kontainer sebagai variabel lingkungan agar dapat dibaca oleh aplikasi runtime kita. Kita bisa menyuntikkan kunci tertentu menggunakan parameter secretKeyRef atau memuat semua entri secara massal dengan envFrom.
Manifest Injeksi Variabel Lingkungan Sensitif #
Berikut adalah contoh manifes Deployment yang mengimpor kredensial database dari objek Secret:
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-processor
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: payment-processor
template:
metadata:
labels:
app: payment-processor
spec:
containers:
- name: processor
image: payment-app:v2.1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: password
[!WARNING] Menyuntikkan Secret sebagai variabel lingkungan memiliki risiko kebocoran yang tinggi. Nilai variabel lingkungan dapat terekspos melalui berkas log dump (crash dumps), log pelacakan proses internal kontainer, atau oleh pengguna lain yang memiliki izin akses menjalankan perintah
envdi dalam kontainer menggunakan perintahkubectl exec.
Menggunakan Secret sebagai Volume Mount (tmpfs) #
Metode yang jauh lebih aman untuk mengonsumsi data Secret di dalam kontainer adalah dengan me-mount Secret tersebut sebagai berkas fisik di dalam volume.
Jalur Penyimpanan tmpfs (In-Memory RAM) untuk Keamanan Maksimum #
Ketika kita me-mount Secret sebagai volume, Kubernetes tidak menulis berkas tersebut ke media penyimpanan fisik node (SSD/HDD host). Kubelet me-mount volume tersebut menggunakan sistem berkas berbasis memori RAM (tmpfs).
[etcd (Encrypted)] ---> API Server ---> Kubelet ---> Memory RAM (tmpfs Volume) ---> Container Pod
^
(Tidak ada jejak data di disk host)
Dengan metode ini, begitu Pod dihancurkan atau dipindahkan, data Secret di dalam RAM node worker akan langsung terhapus tanpa meninggalkan jejak forensik apa pun di media penyimpanan fisik server node worker kita.
Manifest Implementasi Volume Mount Secret #
Berikut adalah contoh manifes Deployment yang memasang sertifikat TLS menggunakan volume mount dengan hak akses POSIX yang ketat:
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-gateway
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: secure-gateway
template:
metadata:
labels:
app: secure-gateway
spec:
volumes:
- name: tls-volume
secret:
secretName: secure-gateway-tls
# Tentukan hak akses POSIX sangat ketat (0400 = Read-only hanya untuk owner kontainer)
defaultMode: 256 # Setara dengan oktal 0400
containers:
- name: gateway-proxy
image: nginx:1.25.3-alpine
volumeMounts:
- name: tls-volume
mountPath: /etc/nginx/certs
readOnly: true
Mengaktifkan Enkripsi At-Rest pada etcd #
Secara default, seluruh data objek Secret disimpan di dalam database etcd Kubernetes dalam bentuk teks biasa (plain text Base64). Jika seseorang berhasil menyusup ke server control plane dan mengakses database etcd secara langsung, mereka dapat membaca seluruh kredensial rahasia kita. Untuk mengantisipasi hal ini, kita wajib mengaktifkan Enkripsi At-Rest (Encryption at Rest).
Konfigurasi EncryptionConfiguration di API Server #
Kita dapat mengonfigurasi API Server agar secara otomatis mengenkripsi data Secret sebelum ditulis ke database etcd dengan membuat berkas konfigurasi EncryptionConfiguration pada control plane:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc: # Gunakan penyedia enkripsi AES-CBC dengan kunci 32-byte
keys:
- name: key1
secret: c3VwZXItc2VjcmV0LWtleS1tdXN0LWJlLTMyLWJ5dGVzLXV0Zjg= # Kunci Base64 32-byte
- identity: {} # Cadangan untuk tetap bisa membaca Secret lama yang belum terenkripsi
Di lingkungan cloud terkelola (managed Kubernetes), kita tidak memiliki akses langsung ke konfigurasi API Server. Namun, cloud provider menyediakan opsi integrasi enkripsi menggunakan layanan manajemen kunci (Key Management Service / KMS) terintegrasi:
# EKS (AWS): Aktifkan envelope encryption menggunakan AWS KMS Key
aws eks update-cluster-config \
--name production-cluster \
--encryption-config '[{"resources":["secrets"],"provider":{"keyArn":"arn:aws:kms:ap-southeast-1:123456789012:key/xxxx-xxxx"}}]'
# GKE (Google Cloud): Aktifkan enkripsi tingkat aplikasi menggunakan Cloud KMS
gcloud container clusters update production-cluster \
--database-encryption-key projects/my-project/locations/asia-southeast1/keyRings/my-ring/cryptoKeys/my-key
Risiko Keamanan Tingkat Lanjut yang Sering Diabaikan #
Menggunakan objek Secret bawaan Kubernetes tidak serta-merta membuat aplikasi kita terbebas dari ancaman keamanan. Ada beberapa kerentanan senyap (silent vulnerabilities) yang sering kali diabaikan oleh platform engineer:
- Kebocoran Melalui Log Sistem: Jika aplikasi kita dikonfigurasi untuk mencatat seluruh argumen kueri HTTP atau mencetak variabel lingkungan saat terjadi error (stack trace), nilai Secret yang disimpan di variabel lingkungan akan tertulis sebagai teks biasa di dalam berkas log. Log ini kemudian dikirim ke server agregasi log terpusat (seperti Elasticsearch/Loki) di mana tim non-admin dapat membacanya secara bebas.
- RBAC yang Terlalu Longgar: Izin RBAC tingkat namespace default sering kali memberikan hak akses
getdanlistuntuk seluruh objek di dalam namespace kepada developer. Siapa pun yang memiliki hak akses ini dapat menjalankan perintahkubectl get secrets -o yamldan menerjemahkan seluruh kredensial produksi kita secara instan. - Jejak File di Repositori Git: Tim yang menerapkan alur GitOps sering kali secara tidak sengaja menambahkan file YAML Secret ke repositori Git. Sekali berkas tersebut ter-commit, data tersebut akan terekam secara permanen di riwayat Git (git history) meskipun berkas tersebut telah dihapus di commit berikutnya.
Perbandingan Pengamanan Akses RBAC #
Berikut adalah perbedaan antara penulisan RBAC default yang terlalu permisif (anti-pattern) dengan pembatasan RBAC least privilege untuk pembacaan Secret (solusi):
# ANTI-PATTERN: Memberikan izin penuh ke seluruh API group bagi ServiceAccount aplikasi
# ✗ Pod aplikasi mendapatkan akses pembacaan ke seluruh Secret di namespace tersebut.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role-unsafe
namespace: production
rules:
- apiGroups: [""]
resources: ["*"] # ✗ Memberikan izin pembacaan ke semua resource termasuk Secret!
verbs: ["*"]
---
# BENAR: Batasi izin pembacaan hanya untuk objek spesifik yang dibutuhkan
# ✓ Hanya izinkan pembacaan pada resource non-sensitif dan isolasi akses Secret secara ketat.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role-safe
namespace: production
rules:
- apiGroups: [""]
resources: ["services", "configmaps"] # Hanya resource non-sensitif
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["app-specific-secret"] # Batasi hanya boleh membaca Secret ini saja!
verbs: ["get"]
Praktik Terbaik Manajemen Secret di Produksi #
Untuk memastikan keamanan kredensial kluster kita berada di tingkat optimal, kita wajib menerapkan beberapa prinsip panduan berikut:
- Batasi Akses RBAC dengan Prinsip Least Privilege: Hanya berikan izin akses pembacaan Secret kepada tim operasional yang berwenang dan Pod aplikasi yang memang membutuhkannya.
- Gunakan Volume Mount tmpfs: Sebisa mungkin, mount Secret sebagai volume daripada menyuntikkannya sebagai variabel lingkungan guna menghindari risiko kebocoran log dump memori RAM.
- Gunakan Pengelola Rahasia Eksternal (External Secret Manager): Integrasikan Kubernetes dengan layanan terkelola seperti AWS Secrets Manager, Google Cloud Secret Manager, atau HashiCorp Vault. Gunakan operator seperti External Secrets Operator (ESO) untuk menyinkronkan data dari cloud manager ke objek Secret Kubernetes secara dinamis, sehingga tim developer tidak perlu menulis data sensitif di repositori Git.
- Rotasikan Kredensial Secara Berkala: Lakukan rotasi kata sandi dan API keys secara teratur (misalnya setiap 30 atau 90 hari) untuk meminimalkan dampak eksploitasi jika terjadi kebocoran kredensial tanpa disadari.
- Gunakan Scanner Pemindaian Kebocoran Git: Pasang perkakas audit seperti
trufflehogataugit-secretsdi dalam pipeline CI/CD kita untuk mendeteksi secara dini jika ada developer yang tidak sengaja menulis atau meng-commit kredensial Base64 ke dalam repositori kode.
Ringkasan #
- Base64 bukan enkripsi: Selalu ingat bahwa Secret di Kubernetes secara default hanya di-encode Base64, bukan dienkripsi.
- Aktifkan enkripsi at-rest: Pastikan enkripsi tingkat etcd diaktifkan di kluster produksi Anda, baik melalui KMS provider cloud maupun konfigurasi
EncryptionConfigurationlokal.- Utamakan volume mount berbasis tmpfs: Gunakan metode volume mount untuk konsumsi Secret di Pod karena data disimpan di RAM Node worker tanpa meninggalkan jejak fisik di disk.
- Batasi otorisasi via RBAC: Gunakan filter
resourceNamespada aturan Role/ClusterRole RBAC untuk mengunci akses pembacaan Secret secara presisi per aplikasi.- Gunakan External Secrets Operator (ESO): Terapkan integrasi manajemen rahasia eksternal (AWS/GCP KMS/Vault) untuk memisahkan kepemilikan kredensial dari repositori GitOps kluster.
- Jangan gunakan data sensitif di ConfigMap: Selalu gunakan Secret untuk menyimpan password dan token, simpan data non-sensitif biasa di ConfigMap.
Navigasi #
← Sebelumnya: ConfigMap Berikutnya: Environment Variable Pattern →