Mengamankan Secrets di Kubernetes dengan Sealed Secrets: Enkripsi Konfigurasi Sensitif di Git
Praktik GitOps menjadikan repository sebagai satu-satunya sumber kebenaran untuk seluruh konfigurasi cluster, termasuk Secret. Masalahnya, menyimpan Secret langsung di Git berarti menyimpan kredensial dalam bentuk yang bisa dibaca siapa saja yang memiliki akses ke repository. Artikel ini menjelaskan bagaimana Sealed Secrets menyelesaikan masalah tersebut dengan mengenkripsi konfigurasi sensitif sebelum di-commit, sehingga Git tetap aman tanpa mengorbankan workflow declarative.
Masalah Menyimpan Secrets Kubernetes secara Plaintext di Git
Secret Kubernetes menyimpan data sensitif seperti password, token, dan kunci SSH. Nilai-nilai tersebut disimpan dalam bentuk base64, yang sering disalahpahami sebagai enkripsi. Faktanya, base64 hanyalah encoding — proses yang bisa dibalik dengan mudah tanpa memerlukan kunci apa pun.
Contoh manifest Secret sederhana di bawah ini menunjukkan bahwa data disimpan dalam string base64:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: cG9zdGdyZXM=
password: c3VwZXJzZWNyZXQ=String cG9zdGdyZXM= terlihat acak, tetapi sebenarnya hanyalah hasil encoding dari postgres. Kita bisa membuktikannya dengan perintah decoding:
# Decode nilai base64 kembali ke plaintext
echo "cG9zdGdyZXM=" | base64 -dOutput:
postgresJika manifest seperti ini di-commit ke Git, siapa pun yang memiliki akses ke repository — termasuk developer baru, CI pipeline, atau akun yang terkompromi — bisa mendecode kredensial tersebut tanpa hambatan. Ini menjadi risiko besar ketika repository bersifat publik atau digunakan lintas tim. Kita membutuhkan solusi yang menjaga konfigurasi tetap versionable namun isinya tidak pernah terekspos dalam bentuk yang bisa dibaca.
Konsep Sealed Secrets — Bagaimana Enkripsi Asimetris Bekerja
Sealed Secrets adalah tool yang memperkenalkan custom resource SealedSecret. Resource ini adalah versi terenkripsi dari Secret biasa. Isi asli tidak pernah berada di repository — yang disimpan hanyalah ciphertext hasil enkripsi yang hanya bisa dibuka oleh controller di dalam cluster.
Prinsip kerjanya memanfaatkan enkripsi asimetris. Controller Sealed Secrets menyimpan private key di dalam cluster. CLI kubeseal menggunakan public key yang sesuai untuk mengenkripsi Secret menjadi SealedSecret. Private key tidak pernah meninggalkan cluster, sehingga hanya cluster yang memiliki kunci tersebut yang bisa mendecrypt data.
Alur GitOps-nya sebagai berikut: developer membuat Secret biasa di lokal, mengenkripsinya dengan kubeseal, lalu me-commit hasil SealedSecret ke Git. Saat SealedSecret di-apply ke cluster, controller mendecrypt-nya dan membuat Secret asli secara otomatis. Repository hanya berisi data terenkripsi, sementara Secret yang berfungsi hanya hidup di runtime cluster.

Gambar: Skema enkripsi asimetris — Sumber: Wikimedia Commons (Public Domain)
Bayangkan private key seperti satu-satunya kunci gembok yang tersimpan di dalam lemari besi — public key hanya bisa mengunci, bukan membuka. Pendekatan ini memastikan bahwa meskipun repository bocor, data sensitif tetap aman karena kunci pembukanya tidak pernah ada di repository.
Setup Sealed Secrets Controller dan kubeseal
Langkah pertama adalah menginstal controller di dalam cluster. Kita bisa menggunakan manifest resmi yang diterbitkan di halaman releases Sealed Secrets:
# Install Sealed Secrets controller di namespace kube-system
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.1/controller.yaml
# Verifikasi controller berjalan
kubectl get pods -n kube-system | grep sealed-secretsManifest controller.yaml membuat Deployment, Service, dan RBAC yang diperlukan controller. Setelah controller berjalan, kita perlu menginstal CLI kubeseal di mesin lokal. Pada macOS, instalasi bisa dilakukan dengan Homebrew:
# Install kubeseal CLI
brew install kubeseal
# Ambil public certificate dari controller untuk digunakan lokal
kubeseal --fetch-cert \
--controller-name sealed-secrets-controller \
--controller-namespace kube-system \
> public-cert.pemPerintah --fetch-cert mengambil public key dari controller dan menyimpannya sebagai file public-cert.pem. File ini digunakan oleh kubeseal untuk mengenkripsi Secret secara offline tanpa harus selalu terhubung ke cluster. Public key aman untuk dibagikan karena tidak bisa digunakan untuk mendecrypt data.
Membuat SealedSecret dari Secret Biasa
Setelah lingkungan siap, kita bisa mulai mengenkripsi Secret. Prosesnya dimulai dengan membuat manifest Secret biasa di file secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
username: cG9zdGdyZXM=
password: c3VwZXJzZWNyZXQ=Kemudian kita seal file tersebut dengan kubeseal:
# Seal Secret menjadi SealedSecret
kubeseal --format yaml \
--cert public-cert.pem \
< secret.yaml > sealed-secret.yamlHasilnya adalah file sealed-secret.yaml yang berisi field encryptedData alih-alih data:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...File ini aman untuk di-commit ke Git karena isinya berupa ciphertext yang tidak bisa didecode tanpa private key. Aspek penting lainnya adalah scope enkripsi. Secara default, kubeseal menggunakan scope strict, yang berarti SealedSecret hanya bisa di-decrypt di namespace dan dengan nama yang sama persis. Ini mencegah secret digunakan ulang di namespace lain secara tidak sengaja.
Workflow GitOps — Commit, Apply, dan Verifikasi Decrypt
Dengan SealedSecret yang aman di Git, kita menerapkannya ke cluster menggunakan perintah standar:
# Apply SealedSecret ke cluster
kubectl apply -f sealed-secret.yaml
# Verifikasi Secret asli telah dibuat oleh controller
kubectl get secret db-credentials -n production
# Tampilkan nilai yang sudah didecrypt
kubectl get secret db-credentials -n production -o jsonpath='{.data.username}' | base64 -dSaat SealedSecret di-apply, controller mendeteksi resource baru, mendecrypt encryptedData menggunakan private key, lalu membuat Secret db-credentials di namespace production. Perintah kubectl get secret mengonfirmasi bahwa Secret asli tercipta dengan benar, dan perintah terakhir membuktikan nilai yang didecrypt sesuai dengan yang kita harapkan.
Pendekatan ini terintegrasi mulus dengan GitOps tool seperti Argo CD atau Flux. Repository hanya menyimpan SealedSecret, sehingga seluruh konfigurasi tetap declarative dan bisa di-review melalui pull request tanpa pernah mengekspos data sensitif. Ketika terjadi perubahan, kita hanya memperbarui SealedSecret di Git dan biarkan pipeline yang menerapkannya ke cluster.
Best Practice Pengamanan Secrets di Kubernetes
Sealed Secrets menyelesaikan masalah penyimpanan di Git, tetapi keamanan tidak berhenti di situ. Kita perlu mengombinasikannya dengan lapisan perlindungan lain. Batasi akses ke Secret asli menggunakan RBAC — hanya service account yang benar-benar membutuhkan data tersebut yang boleh membaca Secret di cluster.
Private key controller adalah aset paling berharga dalam sistem ini. Kita harus melakukan backup private key secara berkala agar recovery cluster tetap memungkinkan. Rotasi private key juga perlu dijadwalkan secara periodik untuk membatasi dampak jika kunci pernah bocor. Seluruh proses rotasi harus didokumentasikan agar bisa dieksekusi saat dibutuhkan.
Hindari menyimpan Secret asli di mana pun selain runtime cluster. Jangan menyimpannya di file lokal yang ikut ter-commit, di dokumentasi, atau di environment variable CI. Untuk kasus yang lebih kompleks, pertimbangkan alternatif seperti External Secrets Operator yang mengintegrasikan Kubernetes dengan Vault atau cloud secret manager. Pilihan ini berguna ketika tim sudah memiliki central secret management dan membutuhkan sinkronisasi otomatis ke banyak cluster.
Mengamankan Secret di Git adalah langkah fundamental untuk membangun workflow GitOps yang aman. Dengan Sealed Secrets, kita mendapatkan keuntungan declarative dari Git tanpa mengorbankan kerahasiaan data. Pelajari praktik keamanan Kubernetes production-grade lebih dalam di kelas DevOps Rumah Coding.