Empat Objek RBAC
| Objek | Cakupan | Fungsi |
|---|---|---|
| Role | Satu namespace | Kumpulan izin |
| ClusterRole | Seluruh kluster | Kumpulan izin lintas namespace |
| RoleBinding | Satu namespace | Memberikan Role/ClusterRole ke subjek |
| ClusterRoleBinding | Seluruh kluster | Memberikan ClusterRole secara global |
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: produksi
name: pembaca-pod
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: produksi
name: dev-boleh-baca
subjects:
- kind: User
name: [email protected]
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pembaca-pod
apiGroup: rbac.authorization.k8s.io
Kombinasi yang sangat berguna: definisikan izin sekali sebagai ClusterRole, lalu berikan per namespace lewat RoleBinding. Anda tidak perlu menyalin Role yang sama ke dua puluh namespace, sementara cakupan izinnya tetap terbatas pada namespace masing-masing.
ServiceAccount untuk Aplikasi
apiVersion: v1 kind: ServiceAccount metadata: name: sa-pesanan namespace: produksi automountServiceAccountToken: false # matikan bila tidak butuh akses API --- spec: serviceAccountName: sa-pesanan
Tanpa konfigurasi apa pun, Kubernetes memasang token ServiceAccount default ke dalam setiap pod di /var/run/secrets/kubernetes.io/serviceaccount/. Bila aplikasi Anda diretas, penyerang langsung punya kredensial untuk berbicara dengan API server. Sebagian besar aplikasi tidak butuh akses API sama sekali — setel automountServiceAccountToken: false dan hilangkan seluruh jalur serangan itu.
Menguji Izin
# Apakah SAYA boleh melakukan ini? kubectl auth can-i delete pods -n produksi kubectl auth can-i '*' '*' --all-namespaces # Apakah ORANG LAIN boleh? (butuh hak admin) kubectl auth can-i get secrets -n produksi [email protected] # Apakah SERVICEACCOUNT boleh? kubectl auth can-i list pods -n produksi \ --as=system:serviceaccount:produksi:sa-pesanan # Daftar lengkap izin kubectl auth can-i --list -n produksi
Security Context
spec:
securityContext: # tingkat pod
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
securityContext: # tingkat kontainer
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
privileged: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp # perlu, karena root fs read-only
volumes:
- name: tmp
emptyDir: {}
Dengan filesystem root yang tidak bisa ditulisi, penyerang tidak dapat menaruh skrip, binary, atau alat apa pun ke dalam kontainer. Ini memutus banyak rantai serangan sekaligus. Yang perlu diperhatikan: sediakan emptyDir untuk direktori yang memang harus bisa ditulisi seperti /tmp dan direktori cache aplikasi.
Pod Security Admission
PodSecurityPolicy sudah dihapus sejak Kubernetes 1.25. Penggantinya adalah Pod Security Admission yang jauh lebih sederhana: cukup label pada namespace.
apiVersion: v1
kind: Namespace
metadata:
name: produksi
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
| Profil | Arti |
|---|---|
privileged | Tanpa pembatasan |
baseline | Mencegah eskalasi hak yang paling jelas |
restricted | Pengerasan penuh: wajib non-root, semua capability dibuang, seccomp aktif |
Langsung menyetel enforce: restricted pada namespace yang sudah berisi beban kerja akan membuat pod baru ditolak dan rilis gagal. Mulailah dengan warn dan audit saja untuk melihat apa yang akan melanggar, perbaiki manifest yang bermasalah, baru aktifkan enforce.
NetworkPolicy
Kubernetes tidak punya isolasi jaringan bawaan. Pod di namespace mana pun bisa membuka koneksi ke pod mana pun, termasuk langsung ke database. NetworkPolicy mengubah itu — tetapi hanya bila plugin jaringan Anda mendukungnya (Calico, Cilium; beberapa CNI sederhana mengabaikannya diam-diam).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: tolak-semua-masuk
namespace: produksi
spec:
podSelector: {} # berlaku untuk SEMUA pod
policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: izinkan-api-ke-basisdata
namespace: produksi
spec:
podSelector:
matchLabels: { app: postgres }
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: { app: layanan-pesanan }
ports:
- protocol: TCP
port: 5432
Pola yang dianjurkan: mulai dengan kebijakan tolak-semua di tiap namespace, lalu buka jalur yang memang dibutuhkan satu per satu. Jangan lupa mengizinkan DNS ke kube-system, karena tanpa itu semua resolusi nama akan gagal.