
Hari 33: SecurityContext Hardening — 20 Temuan ke 0
Deployment.yaml di-hardened: non-root, read-only filesystem, cap drop ALL, probes, resources. 3 scanner: Kubesec 0→11, Checkov 20→0, Trivy 16→0. Pods tetap running.
Hari 33. Kemarin 3 scanner menemukan 20+ misconfiguration di deployment.yaml. Hari ini saatnya fix semua. Target: 0 findings.
Before: 26 Lines, 0 Security
spec:
template:
spec:
containers:
- name: api
image: securebank:v1
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: securebank-secrets
Itu saja. No securityContext, no resources, no probes. Pod jalan pakai default Kubernetes security — yang artinya: bisa root, bisa escalate privilege, filesystem writable, semua capabilities aktif.
After: 65 Lines, 16 Security Properties
Hari ini aku tambah 39 lines security config. Ini perbandingan before vs after:
| Property | Before | After |
|---|---|---|
| automountServiceAccountToken | ❌ | false |
| Pod securityContext | ❌ | runAsNonRoot: true |
| runAsUser | ❌ | 65532 (distroless nonroot) |
| runAsGroup | ❌ | 65532 |
| fsGroup | ❌ | 65532 |
| seccompProfile | ❌ | RuntimeDefault |
| Container securityContext | ❌ | full block |
| allowPrivilegeEscalation | ❌ | false |
| readOnlyRootFilesystem | ❌ | true |
| capabilities.drop | ❌ | ["ALL"] |
| resources.requests | ❌ | cpu 50m, mem 64Mi |
| resources.limits | ❌ | cpu 200m, mem 128Mi |
| livenessProbe | ❌ | httpGet /health |
| readinessProbe | ❌ | httpGet /health |
| imagePullPolicy | ❌ | IfNotPresent |
| emptyDir /tmp | ❌ | mounted (preventive) |
Yang Ditambahkan dan Kenapa
Pod-level securityContext — runAsNonRoot: true memaksa Pod tidak boleh jalan sebagai root. runAsUser: 65532 spesifik UID distroless nonroot image. Kalau salah UID, Pod crash. seccompProfile: RuntimeDefault aktifkan seccomp filter untuk restrict syscall.
Container-level securityContext — allowPrivilegeEscalation: false blok container dari sudo atau setuid. readOnlyRootFilesystem: true buat root filesystem immutable — attacker tidak bisa tulis binary. capabilities.drop: ["ALL"] hapus semua Linux capabilities (NET_RAW, SYS_ADMIN, dll).
emptyDir untuk /tmp — best practice dengan readOnlyRootFilesystem. App mungkin butuh write ke /tmp (misal Go library temporary file). Mount emptyDir = writable /tmp, tapi root filesystem tetap immutable.
Resources — requests (cpu 50m, memory 64Mi) untuk scheduler. limits (cpu 200m, memory 128Mi) untuk mencegah DoS via resource exhaustion.
Probes — livenessProbe auto-restart Pod kalau unhealthy. readinessProbe stop traffic ke Pod kalau belum siap. /health endpoint sudah ada dari Fase 1.
Hasil Scan: Before vs After
| Scanner | Before | After |
|---|---|---|
| Kubesec | Score 0 (14 advise) | Score 11 (3 advise remain: AppArmor, SA, seccomp annotation) |
| Checkov | 69 passed, 20 failed | 85 passed, 0 failed (4 skipped with justification) |
| Trivy | 16 findings (3 HIGH, 3 MEDIUM, 10 LOW) | 0 findings (clean) |
Trivy langsung clean. Checkov 0 failed dengan 4 skip (imagePullPolicy, image digest, secret as env, NetworkPolicy — semua ada remediation day tersendiri). Kubesec 11/14 — 3 yang remain akan di-fix di Day 37 (NetworkPolicy), Day 38 (RBAC), dan production (AppArmor).
UID Trap: 65532 vs 65534
Tutorial pakai runAsUser: 65534 (UID nobody). Tapi distroless gcr.io/distroless/static-debian12:nonroot pakai UID 65532. Kalau pakai 65534, Pod crash karena UID mismatch. Lesson: selalu cek image User:
docker inspect securebank:v1 --format '{{.Config.User}}'
# nonroot:nonroot → UID 65532
Pelajaran Hari Ini
readOnlyRootFilesystem + emptyDir = best practice combo. Immutable root filesystem mencegah attacker tulis binary/config. emptyDir untuk /tmp supaya app tetap bisa operasi. Preventive lebih baik daripada debug crash-loop.
UID harus match dengan image. Tutorial bisa salah — selalu validasi dengan docker inspect. Distroless nonroot = 65532, bukan 65534. Mismatch = Pod crash.
20+ findings → 0 dalam 1 commit. Kuncinya: kemarin scan dengan 3 scanner untuk lihat semua POV, hari ini fix semua sekaligus. ZAP/Checkov/Trivy hasil berbeda — pakai ketiganya untuk coverage maksimal.
Repo
Semua code, before/after comparison, dan 3 scanner reports ada di: https://github.com/stayrelevantid/chalange-devsecops
Kesimpulan
Deployment.yaml sekarang hardened: non-root, read-only filesystem, capabilities dropped, seccomp enabled, probes active, resources limited. 3 scanner: Trivy 0, Checkov 0 (dengan 4 justified skip), Kubesec 11/14. Pods tetap running, /health tetap merespons. Besok Day 34 — RBAC setup, dedicated ServiceAccount dengan least privilege.
Diskusi & Komentar
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.