Blog Post
Workload Identity di Kubernetes dengan SPIFFE dan SPIRE untuk mTLS Internal
Panduan teknis untuk menerapkan workload identity berbasis SPIFFE dan SPIRE di Kubernetes agar service-to-service trust tidak lagi bergantung pada shared secret.
TL;DR
- Workload identity memindahkan trust dari static secret ke identity yang diterbitkan dinamis untuk tiap workload.
- SPIFFE mendefinisikan identity format, sedangkan SPIRE menjadi control plane yang menerbitkan SVID untuk workload.
- Implementasi yang sehat butuh selector yang disiplin, distribusi SVID yang aman, dan verifikasi identity pada jalur komunikasi service-to-service.
Pendahuluan
Banyak cluster Kubernetes masih mengandalkan shared secret, token statis, atau mTLS sertifikat yang dikelola manual untuk komunikasi internal. Pola ini cepat terasa tidak sehat ketika jumlah service bertambah. Secret tersebar di banyak namespace, rotasi menjadi menyakitkan, dan sulit membuktikan service mana sebenarnya berbicara dengan service lain. Inilah konteks di mana workload identity menjadi jauh lebih menarik daripada sekadar “sertifikat internal”.
SPIFFE dan SPIRE memberi jalur yang lebih rapi. SPIFFE mendefinisikan identitas workload dalam bentuk URI yang konsisten. SPIRE bertugas menerbitkan dan mengelola identitas itu berdasarkan selector yang berasal dari platform. Di Kubernetes, selector bisa didasarkan pada namespace, service account, atau label workload. Hasil akhirnya adalah service memiliki identity yang lahir dari runtime environment, bukan dari file secret yang disalin ke mana-mana.
Tutorial ini fokus pada baseline mTLS internal menggunakan SPIRE di Kubernetes. Targetnya bukan service mesh penuh, melainkan fondasi identity yang bisa dipakai untuk service-to-service trust dan integrasi berikutnya.
Problem di Production
Rahasia terbesar di banyak environment bukan serangan canggih, tetapi akumulasi credential yang tidak pernah benar-benar hilang. Service account token digunakan di luar konteksnya, sertifikat dipakai lebih lama dari yang seharusnya, dan secret yang sama tersebar ke banyak pipeline. Begitu satu namespace kompromi, blast radius membesar karena trust model didasarkan pada “siapa yang punya file ini”, bukan “workload apa yang sedang hidup di sini”.
Masalah kedua adalah auditability. Saat incident terjadi, tim ingin tahu siapa yang memanggil apa. Jika semua service memakai secret bersama atau internal CA generik, jawaban itu sering terlalu kabur.
Prerequisites
- Cluster Kubernetes 1.29+.
kubectl,helm, dan akses admin.- Namespace khusus identitas, misalnya
spire-system. - Node Linux yang bisa menjalankan daemon SPIRE agent.
- Dua sample namespace untuk uji, misalnya
identity-demodanidentity-client. - Basic familiarity dengan TLS dan sertifikat X.509.
Architecture / Context
Arsitektur yang dipakai terdiri dari SPIRE server, SPIRE agent di tiap node, dan workload yang menerima SVID lewat CSI volume. SPIRE server menyimpan trust domain dan registration entry. SPIRE agent berjalan dekat node dan melakukan attestation terhadap workload berdasarkan selector Kubernetes. Ketika pod yang cocok muncul, agent menyajikan X.509 SVID yang bisa dipakai service untuk mTLS.
Trust domain dalam tutorial ini menggunakan example.org. Dalam praktik production, pilih trust domain yang konsisten dan tidak ambigu, karena identity ini akan hidup lama dalam integrasi platform.
Step-by-Step Implementation
Step 1: Buat namespace dan install SPIRE
kubectl create namespace spire-system
helm repo add spiffe https://spiffe.github.io/helm-charts-hardened/
helm repo update
Buat values sederhana:
global:
spire:
trustDomain: example.org
spire-server:
enabled: true
spire-agent:
enabled: true
spire-csi-driver:
enabled: true
Install:
helm upgrade --install spire spiffe/spire \
-n spire-system \
-f spire-values.yaml
Step 2: Pastikan server dan agent sehat
kubectl -n spire-system get pods
Minimal harus ada SPIRE server, SPIRE agent DaemonSet, dan CSI driver.
Step 3: Buat namespace demo dan service account
kubectl create namespace identity-demo
kubectl create namespace identity-client
kubectl -n identity-demo create serviceaccount mtls-server
kubectl -n identity-client create serviceaccount mtls-client
Step 4: Buat registration entry untuk server dan client
Cari pod SPIRE server:
SPIRE_SERVER_POD=$(kubectl -n spire-system get pod -l app.kubernetes.io/name=spire-server -o jsonpath='{.items[0].metadata.name}')
Tambahkan entry:
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/identity-demo/sa/mtls-server \
-selector k8s:ns:identity-demo \
-selector k8s:sa:mtls-server
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/identity-client/sa/mtls-client \
-selector k8s:ns:identity-client \
-selector k8s:sa:mtls-client
Selector harus cukup ketat. Jangan gunakan selector yang terlalu umum hanya demi mempercepat uji coba.
Step 5: Deploy server yang menerima SVID
apiVersion: apps/v1
kind: Deployment
metadata:
name: mtls-server
namespace: identity-demo
spec:
replicas: 1
selector:
matchLabels:
app: mtls-server
template:
metadata:
labels:
app: mtls-server
spec:
serviceAccountName: mtls-server
containers:
- name: server
image: cgr.dev/chainguard/nginx:latest
command: ["/bin/sh","-c"]
args:
- |
ls -lah /spiffe
sleep 3600
volumeMounts:
- name: spiffe-workload-api
mountPath: /spiffe
readOnly: true
volumes:
- name: spiffe-workload-api
csi:
driver: csi.spiffe.io
readOnly: true
Apply:
kubectl apply -f server.yaml
Untuk aplikasi nyata, SVID dipakai oleh Envoy, gRPC, atau library TLS yang bisa memuat cert dan key dari path volume atau Workload API.
Step 6: Deploy client workload
apiVersion: apps/v1
kind: Deployment
metadata:
name: mtls-client
namespace: identity-client
spec:
replicas: 1
selector:
matchLabels:
app: mtls-client
template:
metadata:
labels:
app: mtls-client
spec:
serviceAccountName: mtls-client
containers:
- name: client
image: cgr.dev/chainguard/wolfi-base
command: ["/bin/sh","-c"]
args:
- "sleep 3600"
volumeMounts:
- name: spiffe-workload-api
mountPath: /spiffe
readOnly: true
volumes:
- name: spiffe-workload-api
csi:
driver: csi.spiffe.io
readOnly: true
Step 7: Verifikasi SVID muncul di pod
kubectl -n identity-demo exec deploy/mtls-server -- ls -lah /spiffe
kubectl -n identity-client exec deploy/mtls-client -- ls -lah /spiffe
Jika aplikasi Anda sudah siap memakai X.509 SVID, mount path ini bisa langsung diintegrasikan ke TLS config.
Step 8: Tampilkan registration entry dan trust bundle
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server entry show
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server bundle show
Step 9: Integrasikan ke proxy atau app runtime
Contoh jika Envoy dipakai untuk mTLS internal:
common_tls_context:
tls_certificate_sds_secret_configs:
- name: "spiffe://example.org/ns/identity-demo/sa/mtls-server"
validation_context_sds_secret_config:
name: "ROOTCA"
Intinya bukan pada Envoy semata, tetapi pada verifikasi identity SPIFFE URI di sisi penerima.
Verification
Cek semua komponen SPIRE:
kubectl -n spire-system get pods
kubectl -n spire-system get ds
Lihat apakah entry sudah benar:
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server entry show
Validasi pod menerima volume SPIFFE:
kubectl -n identity-demo exec deploy/mtls-server -- ls -lah /spiffe
kubectl -n identity-client exec deploy/mtls-client -- ls -lah /spiffe
Hasil yang benar:
- SPIRE server dan agent sehat
- entry registration cocok dengan namespace dan service account
- pod target menerima material identity yang sesuai
Troubleshooting
Jika volume CSI tidak muncul, cek CSI driver:
kubectl -n spire-system get pods
kubectl -n spire-system logs ds/spire-spire-agent
Jika SVID tidak diterbitkan, kemungkinan selector salah:
kubectl -n spire-system exec "$SPIRE_SERVER_POD" -- /opt/spire/bin/spire-server entry show
kubectl -n identity-demo get sa
Jika pod gagal start karena mount, cek apakah CSI driver terpasang di semua node yang menampung workload. Masalah lain yang sering muncul adalah trust domain salah sejak awal atau aplikasi tidak benar-benar membaca certificate path yang diberikan.
Production Notes
Workload identity sebaiknya diperkenalkan bertahap. Mulai dari service internal yang paling sensitif atau paling sering menjadi dependency lintas namespace. Tambahkan audit trail untuk registration entry dan perubahan trust domain. Rotasi trust bundle harus diuji seperti rotasi credential lain. Jika identity dipakai untuk authorization juga, pisahkan policy authz dari issuance identity agar debugging lebih masuk akal.
Untuk skala besar, pikirkan ownership entry lifecycle. Tim platform tidak seharusnya menjadi bottleneck untuk tiap service baru. Buat pola onboarding yang terdokumentasi dan selector policy yang konsisten.
Trade-offs
SPIFFE/SPIRE memberi identity yang jauh lebih sehat daripada shared secret, tetapi operational model-nya lebih kompleks. Tim perlu memahami trust domain, entry selector, dan distribusi certificate. Jika aplikasi tidak siap memakai mTLS atau Workload API, adopsi awal bisa terasa berat. Namun trade-off ini biasanya sepadan pada lingkungan dengan banyak service dan kebutuhan isolasi yang nyata.
Failure Modes
- Selector terlalu longgar sehingga workload yang salah mendapat identity.
- Selector terlalu sempit sehingga pod tidak pernah menerima SVID.
- Trust domain berubah tanpa rencana migrasi sehingga integrasi lama rusak.
- Tim memakai SPIFFE identity tetapi tetap menyimpan shared secret yang sama di mana-mana, sehingga manfaatnya turun drastis.
Best Practices
- Gunakan namespace dan service account sebagai selector minimum.
- Dokumentasikan trust domain sejak awal.
- Mulai dari use case service-to-service yang jelas, bukan mengubah semua service sekaligus.
- Audit registration entry secara berkala.
- Pastikan aplikasi atau proxy benar-benar memverifikasi SPIFFE URI peer, bukan hanya menerima sertifikat apa pun dari bundle internal.
Kesalahan Umum
- memperlakukan SPIRE seperti secret injector biasa
- memakai selector terlalu generik
- tidak menyiapkan pola rotasi dan audit entry
- mengaktifkan identity tetapi tidak mengubah policy trust di aplikasi
Kesimpulan
SPIFFE dan SPIRE memberi fondasi workload identity yang jauh lebih matang untuk Kubernetes dibanding token statis dan shared secret yang tersebar. Begitu identity diterbitkan berdasarkan runtime selector yang jelas, mTLS internal menjadi lebih mudah diaudit, diputar, dan dipahami sebagai sistem kepercayaan yang nyata, bukan sekadar kumpulan file cert.
Kalau artikel ini membantu, kamu bisa support eksperimen berikutnya.
Apresiasi di TrakteerKeep Reading
Related posts
SPIFFE dan SPIRE untuk Workload Identity yang Tahan Realitas Production
Panduan praktis menerapkan SPIFFE dan SPIRE untuk workload identity di production dengan fokus pada trust domain, attestation, dan rollout yang tidak merusak operability.
Ephemeral GitHub Actions Runners di Kubernetes dengan Actions Runner Controller
Panduan operasional untuk menjalankan GitHub Actions runner yang ephemeral di Kubernetes dengan isolasi lebih baik, autoscaling, dan kontrol secret yang lebih rapi.
Hardening vLLM Inference Service di Kubernetes dengan Istio dan OPA
Panduan production-ready untuk menjalankan vLLM di Kubernetes dengan kontrol jaringan, policy admission, mTLS, dan guardrail operasional yang lebih aman.