
Hari 31: Deploy SecureBank API ke Kubernetes dengan k3d
Fase 3 dimulai! Bikin k3d cluster lokal, deploy distroless image 7.97MB ke K8s dengan secret. 2 replicas running, endpoint tested.
Fase 2 selesai yesterday. Hari ini masuk Fase 3 — Kubernetes & Runtime Security. Dimulai dengan bikin cluster lokal dan deploy SecureBank API.
Dari Docker Compose ke Kubernetes. Dari 8-layer hardening di compose file ke SecurityContext di Pod manifest. tapi tenang, hardening K8s itu Day 33. Hari ini fokusnya deploy dulu supaya jalan.
Bikin Cluster k3d
k3d itu Kubernetes cluster di Docker. Ringan, cepat, cocok untuk development lokal. Aku udah punya 2 cluster k3d lain (family-cashflow dan rancang-cluster), jadi tinggal buat yang baru untuk SecureBank.
k3d cluster create securebank --port "9080:80@loadbalancer" --agents 2
Port 8080 di host sudah dipakai Docker Desktop, jadi k3d loadbalancer pakai port 9080. 1 server + 2 agents = 3 node cluster lokal.
4 Manifest Files
Bikin folder k8s/ di dalam securebank-api/ dengan 4 file:
1. namespace.yaml — Namespace terpisah untuk SecureBank, best practice supaya resource terisolasi.
2. secret.yaml — K8s Secret untuk JWT_SECRET. Bukan plain env var di deployment.yaml, tapi menggunakan Secret:
apiVersion: v1
kind: Secret
metadata:
name: securebank-secrets
namespace: securebank
type: Opaque
data:
JWT_SECRET: Y2ktdGVzdC1zZWNyZXQ= # base64("ci-test-secret")
Kenapa Secret? Karena kubectl describe deployment akan expose env var kalau pakai plain. Dengan Secret, value hanya bisa di-decode dengan kubectl get secret -o yaml. Lebih aman meskipun tidak sempurna — Day 43 nanti akan pakai External Secrets Operator untuk proper secret management.
3. deployment.yaml — 2 replicas dengan distroless image securebank:v1 (7.97MB). Deployment ini intentionally insecure — tidak ada securityContext, tidak ada resource limits. Kenapa? Karena besok (Day 32) mau di-scan untuk cari misconfiguration. Fix-nya di Day 33.
4. service.yaml — ClusterIP service, port 80 ke targetPort 8080.
Deploy dan Test
k3d image import securebank:v1 -c securebank
kubectl apply -f k8s/
Ada incident kecil: kubectl apply -f k8s/ apply semua file sekaligus, tapi namespace belum ready waktu deployment.yaml di-apply. Fix: re-apply setelah 2 detik. Normal.
kubectl get pods -n securebank
# NAME READY STATUS RESTARTS AGE
# securebank-api-85556574c4-cqvv9 1/1 Running 0 8s
# securebank-api-85556574c4-n6q6n 1/1 Running 0 8s
2 replicas Running. Test via port-forward:
kubectl port-forward svc/securebank-api 9080:80 -n securebank &
curl http://localhost:9080/health
# {"status":"healthy"}
# Test dengan JWT token
TOKEN="eyJhbGciOiJIUzI1NiIs..."
curl -H "Authorization: Bearer $TOKEN" http://localhost:9080/balance?id=ACC001
# {"id":"ACC001","name":"Alice","balance":10000}
# Test tanpa JWT
curl http://localhost:9080/balance?id=ACC001
# missing authorization header (401)
Semua endpoint berfungsi. Security headers dari Fase 2 juga jalan — ZAP remediation (Day 26) tetap aktif di K8s deployment.
Pelajaran Hari Ini
K8s Secret best practice mulai dari awal. Daripada plain env var di deployment.yaml (yang exposable via kubectl describe), pakai K8s Secret. Base64 bukan encryption, tapi setidaknya terpisah dari manifest dan bisa di-manage secara terpisah. Day 43 akan pakai External Secrets Operator untuk proper secret management.
k3d image import lebih cepat dari registry. Tidak perlu push ke Docker Hub atau GHCR untuk testing local. k3d image import inject image ke semua node di cluster. Cepat dan simple untuk dev. Untuk production nanti pakai registry dengan Cosign verification.
Namespace apply order. kubectl apply -f k8s/ apply semua file sekaligus, tapi namespace butuh waktu untuk terbentuk. Solusi: re-apply setelah 2 detik, atau apply namespace dulu, lalu sisanya. Normal behavior K8s.
Repo
Semua code ada di: https://github.com/stayrelevantid/chalange-devsecops
Kesimpulan
Fase 3 dimulai. SecureBank API sekarang berjalan di Kubernetes — 2 replicas, distroless image, K8s Secret. Deployment ini masih "mentah" — no securityContext, no resources, no probes. Besok Day 32 akan di-scan untuk cari misconfiguration, lalu Day 33 di-fix dengan SecurityContext hardening.
Sampai jumpa besok.
Diskusi & Komentar
Hari 30: Fase 2 Selesai — Retrospektif 15 Hari Container Security
Next ArticleHari 32: Scan K8s Misconfig — 3 Scanner, 20+ Temuan
Artikel Terkait
Hari 5: Trivy SCA Scan Nemukan 4 CVE di Golang API
Hari kelima 60 hari DevSecOps! Scan dependensi Go pakai Trivy dan nemukan 4 CVE termasuk 1 CRITICAL — termasuk library deprecated jwt-go.
Hari 8: Semgrep SAST Scan Temukan Kode Tidak Aman
Hari kedelapan 60 hari DevSecOps! Install Semgrep, buat kode insecure (MD5), dan scan ketemu 2 finding — MD5 weak hash dan HTTP server tanpa TLS.
Hari 10: MD5 ke Bcrypt, Pipeline Hijau Lagi
Hari kesepuluh 60 hari DevSecOps! Fix SAST findings — ganti MD5 ke bcrypt, hapus custom rule HTTP TLS, dan pipeline CI kembali hijau setelah 4 job semua pass.