Belajar Kubernetes - Episode 21.2 - Secret

Belajar Kubernetes - Episode 21.2 - Secret

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.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 27, 2026
0 views
10 min read

Pendahuluan

Catatan

Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 21.1 di bawah ini

Episode 21.1Episode 21.1

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.

Apa Itu Secret?

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:

  • Namespace-scoped - Secret tergabung dalam satu Namespace
  • Base64-encoded - Value di-encode dalam base64 (tidak dienkripsi secara default)
  • Banyak tipe - Generic, TLS, Docker registry credential, service account token
  • Banyak pola konsumsi - Environment variable, volume mount, image pull secret
  • Dikontrol RBAC - Akses diatur oleh Kubernetes RBAC
  • Enkripsi at rest - Opsional, harus dikonfigurasi secara eksplisit melalui EncryptionConfiguration

Warning

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.

Secret vs ConfigMap

Baik Secret maupun ConfigMap menyimpan data key-value. Perbedaannya ada pada tujuan dan cara penanganannya:

FiturSecretConfigMap
Dirancang untukData sensitifKonfigurasi non-sensitif
Format penyimpananBase64-encodedPlain text
Enkripsi at restDidukung (opt-in)Biasanya tidak dienkripsi
Di-mount sebagai tmpfsYa (in-memory)Tidak
Granularitas RBACResource secretsResource configmaps
Terlihat di kubectl describe podValue disembunyikanValue ditampilkan

Gunakan ConfigMap untuk konfigurasi aplikasi. Gunakan Secret untuk apapun yang akan berbahaya jika bocor: password, token, sertifikat, private key.

Tipe Secret

Kubernetes mendukung beberapa tipe Secret bawaan, masing-masing dirancang untuk use case tertentu.

Opaque (Generic)

Tipe default untuk data arbitrary yang didefinisikan user.

Kubernetessecret-opaque.yml
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):

Kubernetessecret-opaque-stringdata.yml
apiVersion: v1
kind: Secret
metadata:
    name: app-credentials
type: Opaque
stringData:
    username: admin
    password: secretpass

Tip

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.

kubernetes.io/tls

Digunakan untuk sertifikat TLS dan private key. Dikonsumsi oleh Ingress controller dan komponen lain yang mendukung TLS.

Kubernetessecret-tls.yml
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:

KubernetesBuat TLS Secret dari File
sudo kubectl create secret tls tls-secret \
    --cert=path/to/tls.crt \
    --key=path/to/tls.key

kubernetes.io/dockerconfigjson

Digunakan untuk autentikasi ke private container image registry.

KubernetesBuat Docker Registry Secret
sudo kubectl create secret docker-registry regcred \
    --docker-server=registry.example.com \
    --docker-username=myuser \
    --docker-password=mypassword \
    --docker-email=me@example.com

Referensikan di spec Pod:

Kubernetespod-with-imagepullsecret.yml
apiVersion: v1
kind: Pod
metadata:
    name: private-image-pod
spec:
    imagePullSecrets:
        - name: regcred
    containers:
        - name: app
          image: registry.example.com/myapp:latest

kubernetes.io/service-account-token

Dibuat secara otomatis oleh Kubernetes untuk ServiceAccount. Digunakan oleh Pod untuk autentikasi ke API server. Cluster modern menggunakan projected service account token sebagai gantinya.

Ringkasan Tipe Secret Bawaan

TipeUse Case
OpaqueSecret key-value generik
kubernetes.io/tlsSertifikat dan key TLS
kubernetes.io/dockerconfigjsonAutentikasi registry
kubernetes.io/basic-authUsername/password basic auth
kubernetes.io/ssh-authSSH private key
kubernetes.io/service-account-tokenToken ServiceAccount
bootstrap.kubernetes.io/tokenToken bootstrap node

Membuat Secret

Menggunakan kubectl (Imperatif)

# 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.key

Menggunakan YAML Manifest (Deklaratif)

Kubernetesdb-credentials.yml
apiVersion: 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.

Mengonsumsi Secret di Pod

Secret bisa dikonsumsi dengan dua cara: environment variable atau volume mount. Masing-masing memiliki trade-off yang berbeda.

Sebagai Environment Variable

Pola paling sederhana — injeksi value secret langsung sebagai environment variable container.

Injeksi Key Tertentu

Kubernetespod-env-from-secret.yml
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_USER

Injeksi Semua Key dari Secret

Kubernetespod-envfrom-secret.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-pod
spec:
    containers:
        - name: app
          image: myapp:latest
          envFrom:
              - secretRef:
                    name: db-credentials

Ini 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.

Sebagai Volume Mount (File)

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.

Kubernetespod-secret-volume.yml
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 baca

Di dalam container, file-nya akan menjadi:

bash
/etc/secrets/DB_HOST
/etc/secrets/DB_PORT
/etc/secrets/DB_NAME
/etc/secrets/DB_USER
/etc/secrets/DB_PASSWORD

Mount Key Tertentu Saja

Kubernetespod-secret-volume-items.yml
volumes:
    - name: credentials
      secret:
          secretName: db-credentials
          items:
              - key: DB_PASSWORD
                path: password.txt
                mode: 0400
              - key: DB_USER
                path: username.txt
                mode: 0444

Ini hanya me-mount key yang ditentukan, dengan nama file dan permission yang dikustomisasi.

Perbandingan Env Var vs Volume Mount

AspekEnvironment VariableVolume Mount
Visibilitas di prosescat /proc/self/environTidak terekspos
PenyimpananMemory (env)tmpfs (in-memory)
Update dinamisPerlu restart PodUpdate tanpa restart
Permission fileN/ABisa dikonfigurasi (mode)
Audit trailLebih sulit diauditLebih mudah (logging akses file)
Terbaik untukAplikasi sederhana, kompatibilitas lamaProduction, keamanan tinggi

Implementasi Praktis

Contoh 1: Aplikasi Database dengan Secret

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: myappdb

Contoh 2: TLS Termination dengan Ingress

Kubernetesingress-tls.yml
apiVersion: 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: 8080

Contoh 3: Private Registry Pull Secret

Kubernetesserviceaccount-with-pull-secret.yml
apiVersion: v1
kind: ServiceAccount
metadata:
    name: app-service-account
    namespace: production
imagePullSecrets:
    - name: regcred

Melampirkan 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.

Contoh 4: Memperbarui Secret

KubernetesUpdate Value Secret
# 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.

Enkripsi Secret at Rest

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.

Kubernetesencryption-config.yml
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:

KubernetesEnkripsi 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.

Kesalahan Umum dan Pitfall

Kesalahan 1: Menyimpan Secret di Git

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:

  • Sealed Secrets (Bitnami) — enkripsi secret di sisi client, aman untuk di-commit
  • External Secrets Operator — sinkronisasi secret dari Vault, AWS SSM, GCP Secret Manager
  • SOPS — enkripsi file YAML/JSON menggunakan age atau KMS
KubernetesJangan Pernah Lakukan Ini
# JANGAN commit file yang berisi secret asli
git add db-credentials.yml   # ❌
git commit -m "add database secret"

Kesalahan 2: RBAC yang Terlalu Permisif

Problem: Memberikan akses get secrets yang luas mengekspos semua secret di Namespace.

Solusi: Ikuti least privilege. Berikan akses hanya ke secret tertentu:

Kubernetesrbac-secret-specific.yml
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"]

Kesalahan 3: Menggunakan Environment Variable untuk Data Sangat Sensitif

Problem: Environment variable terlihat oleh semua proses di container dan mungkin ter-log.

Solusi: Mount data sensitif sebagai file volume dengan permission yang ketat:

Kubernetesyml
# 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: true

Kesalahan 4: Tidak Mengatur File Permission

Problem: File Secret yang di-mount di volume bisa dibaca oleh semua user di container.

Solusi: Set defaultMode yang restrictif dan mode per-item:

Kubernetesyml
volumes:
    - name: credentials
      secret:
          secretName: db-credentials
          defaultMode: 0400  # hanya owner yang bisa baca

Kesalahan 5: Tidak Melakukan Rotasi Secret

Problem: 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).

Best Practice

Gunakan Manajemen Secret Eksternal di Production

Jangan kelola secret secara native di Kubernetes dalam skala besar. Gunakan backend secrets eksternal:

Kubernetesexternal-secret.yml
# 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-password

Aktifkan Enkripsi at Rest

Selalu aktifkan EncryptionConfiguration yang mengarah ke KMS provider (bukan hanya aescbc dengan static key) di production.

Terapkan RBAC yang Ketat

Batasi siapa yang bisa list dan get secret:

Kubernetesrestrict-secret-access.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
    name: secret-reader-restricted
rules:
    - apiGroups: [""]
      resources: ["secrets"]
      verbs: ["get"]
      # Tanpa verb "list" — mencegah enumerasi massal

Tip

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.

Mount Secret sebagai Read-Only

Selalu tambahkan readOnly: true pada volume mount Secret:

Kubernetesyml
volumeMounts:
    - name: credentials
      mountPath: /etc/secrets
      readOnly: true

Audit Akses Secret

Aktifkan Kubernetes audit logging dengan policy yang menangkap operasi get dan watch pada Secret:

Kubernetesaudit-policy.yml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
    - level: Metadata
      resources:
          - group: ""
            resources: ["secrets"]

Gunakan Immutable Secret

Tandai Secret sebagai immutable untuk mencegah modifikasi yang tidak disengaja dan mengurangi beban API server (Kubernetes berhenti memantau perubahan):

Kubernetesyml
apiVersion: v1
kind: Secret
metadata:
    name: app-credentials
type: Opaque
immutable: true
stringData:
    API_KEY: my-api-key-value

Kapan TIDAK Menggunakan Kubernetes Secret

Kubernetes Secret nyaman tapi tidak selalu alat yang tepat:

  • Payload binary besar — Secret memiliki batas ukuran 1 MiB. Gunakan object storage (S3, GCS) untuk file besar.
  • Secret yang digunakan di banyak cluster — Mengelola secret yang sama per-cluster sangat berat secara operasional. Gunakan vault terpusat (HashiCorp Vault, AWS Secrets Manager) sebagai single source of truth.
  • Secret yang memerlukan generasi dinamis — Jika credential perlu di-generate sesuai permintaan per-pod (misalnya short-lived database credential), Vault dynamic secrets dirancang khusus untuk ini.
  • Lingkungan yang diregulasi kepatuhan — Beberapa framework kepatuhan (PCI-DSS, HIPAA) memerlukan hardware atau layanan manajemen secret khusus. Kubernetes Secret saja mungkin tidak memenuhi persyaratan audit tersebut.

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.

Penutup

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:

  • Secret menyimpan data sensitif sebagai pasangan key-value yang di-encode base64 di etcd
  • Base64 adalah encoding, bukan enkripsi — selalu aktifkan enkripsi at rest
  • Tipe Secret yang umum: Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjson
  • Secret bisa dikonsumsi sebagai environment variable (lebih sederhana) atau volume mount (lebih aman)
  • Secret yang di-mount via volume disimpan di tmpfs (in-memory), lebih aman dari env var
  • Volume mount update secara dinamis (~1 menit); env var memerlukan restart Pod
  • Jangan pernah commit manifest Secret dengan value asli ke version control
  • Gunakan RBAC untuk membatasi siapa yang bisa mengakses secret mana — hindari permission list yang luas
  • Gunakan External Secrets Operator, Sealed Secrets, atau HashiCorp Vault untuk manajemen secret production-grade
  • Set immutable: true untuk secret yang tidak berubah untuk mengurangi overhead API server
  • Aktifkan audit logging Kubernetes untuk melacak akses Secret

Manajemen 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

Episode 22Episode 22

Related Posts