Kodeloka
  1. Kodeloka
  2. Kelas Kubernetes
  3. Materi

5 · Keamanan · Modul 8 dari 14

RBAC, ServiceAccount & Pod Security

Menerapkan hak akses terkecil untuk manusia maupun aplikasi, dan mengeraskan pod dengan security context serta Pod Security Admission.

Empat Objek RBAC

ObjekCakupanFungsi
RoleSatu namespaceKumpulan izin
ClusterRoleSeluruh klusterKumpulan izin lintas namespace
RoleBindingSatu namespaceMemberikan Role/ClusterRole ke subjek
ClusterRoleBindingSeluruh klusterMemberikan 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
💡 ClusterRole bisa dipakai dengan RoleBinding

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
🚫 Setiap pod mendapat token API secara bawaan

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: {}
📌 readOnlyRootFilesystem sangat efektif

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
ProfilArti
privilegedTanpa pembatasan
baselineMencegah eskalasi hak yang paling jelas
restrictedPengerasan penuh: wajib non-root, semua capability dibuang, seccomp aktif
⚠️ Terapkan bertahap

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

🚫 Secara bawaan, semua pod bisa menghubungi semua pod

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.