Kubernetes adalah runtime produksi kalian dan default-nya belum aman; di episode ini kalian memahami model 4C, menerapkan Pod Security Admission restricted, menulis RBAC least privilege, memasang NetworkPolicy default-deny, dan menambah admission control Kyverno sebagai policy gate cluster

Setelah mengamankan IaC di episode 7, kita turun ke tempat workload benar-benar hidup: Kubernetes. Cluster adalah lingkungan yang unik dari sisi security — semua komponennya saling berkomunikasi via API, default-nya permisif (pod bebas bicara ke pod lain), dan satu RBAC yang salah bisa berarti root seluruh armada.
Episode ini menyusun pertahanan berlapis dengan urutan yang akan kalian pakai di setiap cluster production: memahami model 4C → Pod Security Admission → RBAC least privilege → NetworkPolicy → admission control. Cluster kind dari lab episode 0 sudah cukup untuk semua praktiknya.
Kubernetes tidak bisa diamankan secara terpisah — ia bertumpuk pada lapisan-lapisan di bawahnya:
Cloud -> IAM akun cloud, network VPC, enkripsi storage (episode 7 & 9)
Cluster -> API server, RBAC, policies, node hardening (episode ini)
Container -> image minimal, non-root, scan (episode 6)
Code -> SAST/DAST, dependency aman (episode 4)Prinsipnya: kelemahan di lapisan bawah meruntuhkan lapisan atas. Pod yang jalan sebagai root membuat pengerasan cluster kurang berarti; cluster yang terbuka membuat image distroless tetap bisa disusupi lewat API. Amankan dari luar ke dalam.
PSA adalah mekanisme bawaan K8s (sejak 1.25) yang menerapkan Pod Security Standards pada level namespace — tanpa perlu tool tambahan:
| Level | Efek | Untuk Siapa |
|---|---|---|
privileged | Tanpa batasan | Infra khusus (CNI, monitoring agent) |
baseline | Blokir privilege escalation, hostNetwork, dsb. | Workload umum |
restricted | + wajib non-root, drop ALL capabilities, seccomp | Production ideal |
Terapkan restricted pada namespace aplikasi:
kubectl label namespace app-prod \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restrictedKonsekuensinya, manifest pod harus memenuhi syarat ketat:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
serviceAccountName: api-sa # SA khusus, bukan default
automountServiceAccountToken: false # matikan jika tak butuh API K8s
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: api
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: trueBaris-baris yang disorot adalah dua kesalahan paling umum: lupa ganti ServiceAccount default, dan lupa mematikan token API untuk pod yang tidak membutuhkannya.
RBAC mengatur siapa boleh apa di API server. Dua aturan emas: hindari cluster-admin untuk apapun selain break-glass, dan bind ke ServiceAccount per-workload, bukan group generik.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app-prod
name: api-reader
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: app-prod
name: api-reader-binding
subjects:
- kind: ServiceAccount
name: api-sa
roleRef:
kind: Role
name: api-reader
apiGroup: rbac.authorization.k8s.ioPerhatikan Role (namespace-scoped), bukan ClusterRole — prinsip blast radius. Audit cepat untuk mencari binding berbahaya:
kubectl get clusterrolebindings -o json | jq '.items[] |
select(.roleRef.name=="cluster-admin") |
.metadata.name'Warning
Binding system:anonymous atau subject system:unauthenticated pada role berprivilege adalah konfigurasi berbahaya yang masih ditemukan di cluster nyata. Jalankan audit query di atas sekarang terhadap cluster kalian.
Secara default semua pod bisa saling komunikasi — model flat yang berbahaya saat satu pod dicompromise. Pola standar: tolak semua traffic masuk di namespace, lalu izinkan eksplisit.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app-prod
spec:
podSelector: {}
policyTypes:
- IngressPolicy tanpa ingress rules = tolak semuanya. Lalu buka hanya jalur yang dibutuhkan, misal frontend → api port 8080:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-from-frontend
namespace: app-prod
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080Catatan penting: NetworkPolicy butuh CNI yang mendukungnya (Calico, Cilium; kind default tidak — install Cilium di kind untuk praktik). Segmentasi lanjutan termasuk egress control dan mTLS mesh dibahas tuntas di episode 18.
PSA hanya menangkap standar pod. Untuk policy organisasi ("image harus dari registry internal", "wajib label owner"), pasang policy engine di admission webhook. Kyverno paling ramah pemula karena policy-nya YAML murni:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
validationFailureAction: Enforce
rules:
- name: only-internal-registry
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Image harus dari registry organisasi."
pattern:
spec:
containers:
- image: "registry.internal.example/*"Setiap Pod baru dievaluasi sebelum dibuat — melanggar policy = ditolak dengan pesan jelas. Ini adalah policy-as-code versi cluster, saudara dari OPA di episode 7.
Inti yang harus dibawa pulang:
restricted per-namespace memberi baseline kuat tanpa tool eksternal.Di episode 9 selanjutnya kita naik lagi ke lapisan Cloud: Cloud Security in Pipeline — bagaimana deploy yang aman memakai OIDC tanpa static key, guardrails tingkat organisasi seperti SCP/Azure Policy, dan cara mencegah misconfigurasi cloud sejak pipeline. Sampai jumpa!