Standar Keamanan Pod

Profil Pod Security Standards baseline dan restricted beserta penerapannya pada namespace.

Daftar isi

Pod Security Standards adalah kumpulan kebijakan keamanan bawaan yang membatasi isi manifest Pod dengan beberapa tingkat ketegasan, dan penerapannya diatur per namespace melalui label. Standar ini memindahkan kebijakan keamanan dari penulisan security context yang bergantung pada tiap aplikasi menjadi pengaturan kluster yang menutup seluruh namespace sekaligus. Topik ini membahas tiga profil standar, tiga mode penerapan yang masing-masing memiliki reaksi berbeda, serta evaluasi Pod contoh terhadap profil restricted yang dapat dijalankan di sandbox.

Tiga profil standar#

Standar ini mendefinisikan tiga profil dengan tingkat ketegasan yang meningkat. Profil privileged tidak menerapkan batasan apa pun dan hanya tepat untuk komponen sistem yang memang membutuhkan akses host. Profil baseline melarang pengaturan yang paling istimewa, sedangkan profil restricted menuntut kebiasaan pengamanan container modern. Ketiganya dirangkum pada tabel berikut:

ProfilBatasan intiPeruntukan
privilegedtanpa batasankomponen sistem dengan kebutuhan akses host
baselinemelarang privileged, hostNetwork, hostPath, dan kemampuan istimewaaplikasi umum yang masih membutuhkan sedikit kemudahan istimewa
restrictedmenuntut non-root, seccomp, dan drop ALLaplikasi yang mengikuti kebiasaan pengamanan modern

Profil restricted menuntut runAsNonRoot, seccompProfile bertipe RuntimeDefault atau Localhost, allowPrivilegeEscalation bernilai false, dan capabilities dengan drop ALL, dengan pengecualian penambahan NET_BIND_SERVICE. Definisi profil ditetapkan oleh proyek Kubernetes sehingga identik antara kluster, sedangkan kluster hanya memilih profilnya per namespace. Pemilihan ini menjadikan kebijakan dapat ditinjau dengan membaca label namespace saja, tanpa membuka manifest setiap aplikasi, dan alat pemindai keamanan dapat menilai namespace dengan asumsi yang sama seperti admission controller kluster.

Mode penerapan melalui label namespace#

Penerapan standar dilakukan oleh admission controller bawaan bernama Pod Security Admission, yaitu komponen yang menilai setiap Pod pada saat pembuatan. Pengaturannya berupa label Namespace dengan bentuk pod-security.kubernetes.io/<mode>=<level>, dan terdapat tiga mode dengan reaksi berbeda, yaitu enforce yang menolak Pod yang melanggar, audit yang merekam pelanggaran pada audit log tanpa menolak, dan warn yang menampilkan peringatan pada respons kubectl tanpa menolak. Manifest berikut menerapkan profil restricted pada namespace aplikasi-catatan:

yaml pss-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: aplikasi-catatan
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.37
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Label enforce-version berisi versi yang disematkan, pada contoh ini v1.37, yang mengunci definisi profil terhadap definisi versi tersebut. Penguncian ini menjaga agar kebijakan tidak berubah diam-diam apabila definisi profil diperbarui pada rilis kluster berikutnya. Mode audit dan warn pada manifest memungkinkan pelanggaran terlihat lebih dahulu sebelum menjadi alasan penolakan yang menghentikan pengerjaan.

Evaluasi Pod contoh terhadap profil restricted#

Manifest berikut memuat dua Pod dalam satu berkas, yaitu Pod lama yang ditulis sebelum kebijakan ada dan Pod baru yang mengikuti profil restricted:

yaml pod-pss.yaml
apiVersion: v1
kind: Pod
metadata:
  name: catatan-lama
  namespace: aplikasi-catatan
spec:
  hostNetwork: true
  containers:
    - name: catatan
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      securityContext:
        privileged: true
        capabilities:
          add: ["NET_ADMIN"]
---
apiVersion: v1
kind: Pod
metadata:
  name: catatan-baru
  namespace: aplikasi-catatan
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 65532
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: catatan
      image: busybox:1.36
      command: ["sh", "-c", "sleep 3600"]
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]

Skrip berikut membaca label enforce namespace dan kedua Pod tersebut, lalu menilai setiap container terhadap syarat profil restricted:

python simulasi-pss.py
"""Evaluasi pod contoh terhadap profil Pod Security Standards restricted."""
import yaml

with open("pss-namespace.yaml", encoding="utf-8") as f:
    namespace = yaml.safe_load(f)
with open("pod-pss.yaml", encoding="utf-8") as f:
    pods = [p for p in yaml.safe_load_all(f) if p]

label = namespace["metadata"]["labels"]
enforce = label["pod-security.kubernetes.io/enforce"]
versi = label["pod-security.kubernetes.io/enforce-version"]
print(f"Namespace {namespace['metadata']['name']} dengan enforce={enforce} (versi {versi})")
print()

# kemampuan yang dianggap terlalu istimewa pada profil restricted
CAPS_PRIVILEGED = {"NET_ADMIN", "SYS_ADMIN", "SYS_PTRACE", "SYS_BOOT", "SYS_TIME"}


def pelanggaran(pod):
    hasil = []
    spec = pod["spec"]
    sc_pod = spec.get("securityContext", {})
    if spec.get("hostNetwork") or spec.get("hostPID") or spec.get("hostIPC"):
        hasil.append("host namespace tidak diizinkan (hostNetwork/hostPID/hostIPC)")
    for c in spec["containers"]:
        sc = c.get("securityContext", {})
        if sc.get("privileged"):
            hasil.append(f"privileged=true pada container {c['name']}")
        if sc.get("allowPrivilegeEscalation", True):
            hasil.append(f"allowPrivilegeEscalation tidak bernilai false pada container {c['name']}")
        if not (sc.get("runAsNonRoot") or sc_pod.get("runAsNonRoot")):
            hasil.append(f"runAsNonRoot tidak bernilai true pada container {c['name']}")
        seccomp = sc.get("seccompProfile", sc_pod.get("seccompProfile", {})).get("type")
        if seccomp not in ("RuntimeDefault", "Localhost"):
            hasil.append(f"seccompProfile bukan RuntimeDefault atau Localhost pada container {c['name']}")
        tambah = sc.get("capabilities", {}).get("add", [])
        kotor = [cap for cap in tambah if cap in CAPS_PRIVILEGED]
        if kotor:
            hasil.append(f"capabilities add berisi {', '.join(kotor)} pada container {c['name']}")
        if "ALL" not in sc.get("capabilities", {}).get("drop", []):
            hasil.append(f"capabilities drop ALL tidak diatur pada container {c['name']}")
    return hasil


for pod in pods:
    nama = pod["metadata"]["name"]
    daftar = pelanggaran(pod)
    if daftar:
        print(f"Pod {nama} : DITOLAK ({len(daftar)} pelanggaran)")
        for item in daftar:
            print(f"  - {item}")
    else:
        print(f"Pod {nama} : DITERIMA")
    print()
text Keluaran
Namespace aplikasi-catatan dengan enforce=restricted (versi v1.37)

Pod catatan-lama : DITOLAK (7 pelanggaran)
  - host namespace tidak diizinkan (hostNetwork/hostPID/hostIPC)
  - privileged=true pada container catatan
  - allowPrivilegeEscalation tidak bernilai false pada container catatan
  - runAsNonRoot tidak bernilai true pada container catatan
  - seccompProfile bukan RuntimeDefault atau Localhost pada container catatan
  - capabilities add berisi NET_ADMIN pada container catatan
  - capabilities drop ALL tidak diatur pada container catatan

Pod catatan-baru : DITERIMA

Pod catatan-lama ditolak karena tujuh pelanggaran yang disebut satu per satu, mulai dari privileged dan hostNetwork sampai seccompProfile yang tidak diatur. Pod catatan-baru lolos karena memenuhi seluruh syarat, yaitu runAsNonRoot pada level Pod, seccompProfile bertipe RuntimeDefault, allowPrivilegeEscalation bernilai false, dan capabilities yang hanya berisi drop ALL. Daftar pelanggaran pada keluaran meniru isi peringatan Pod Security Admission yang sebenarnya, sehingga skrip ini dapat digunakan sebagai pratinjau cepat sebelum manifest dikirim ke kluster. Kasus Pod lama juga memperlihatkan perilaku kebijakan yang menghasilkan daftar temuan, yaitu admission menampung seluruh pelanggaran dalam satu respons dan tidak berhenti pada pelanggaran pertama, sehingga perbaikan dapat dilakukan sekali jalan. Perintah berikut memperlihatkan reaksi kluster ketika kedua Pod diterapkan:

bash
# tidak-dijalankan
# terapkan namespace dengan label keamanan pod
kubectl apply -f pss-namespace.yaml

# terapkan dua pod contoh, satu melanggar dan satu memenuhi profil
kubectl apply -f pod-pss.yaml
text Contoh keluaran
namespace/aplikasi-catatan created
Error from server (Forbidden): error when creating "pod-pss.yaml": pods "catatan-lama" is forbidden: violates PodSecurity "restricted:v1.37": privileged (container "catatan" must not set privileged=true), hostNetwork (pod must not set hostNetwork=true), allowPrivilegeEscalation (container "catatan" must set securityContext.allowPrivilegeEscalation=false), runAsNonRoot (pod or container must set securityContext.runAsNonRoot=true), seccompProfile (pod or container must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost"), capabilities (container "catatan" must not add "NET_ADMIN" in securityContext.capabilities.add)
pod/catatan-baru created

Kesalahan umum#

Tiga kesalahan berikut paling sering muncul saat standar ini mulai diterapkan pada namespace yang sudah terisi workload. Ketiganya berpangkal pada perbedaan antara kebijakan yang berlaku untuk Pod baru dan keadaan namespace yang sudah terisi:

  • Memasang label enforce restricted pada namespace yang dipenuhi workload lama tanpa evaluasi awal. Pod yang sudah berjalan tidak terganggu, tetapi setiap Pod baru pada namespace tersebut ditolak, termasuk Pod sementara yang dibuat oleh perkakas lain. Perbaikannya adalah memasang mode audit dan warn terlebih dahulu, memperbaiki manifest yang tercatat melanggar, lalu mengaktifkan enforce.
  • Menganggap Pod yang lolos di satu namespace berarti lolos di semua namespace. Definisi profil sama di seluruh kluster, tetapi label setiap namespace dapat berbeda. Perbaikannya adalah memeriksa label namespace tujuan dengan kubectl describe namespace <nama> sebelum manifest dipindahkan.
  • Menghapus seccompProfile agar manifest lebih pendek. Profil restricted menuntutnya pada level Pod atau container, sehingga Pod langsung ditolak. Perbaikannya adalah menetapkan seccompProfile bertipe RuntimeDefault pada level Pod.

Ringkasan#

  • Pod Security Standards menyediakan tiga profil, yaitu privileged tanpa batasan, baseline yang melarang pengaturan paling istimewa, dan restricted yang menuntut kebiasaan pengamanan modern.
  • Penerapan dilakukan Pod Security Admission melalui label namespace dengan mode enforce yang menolak, audit yang merekam, dan warn yang memperingatkan.
  • Label hanya dinilai pada saat Pod dibuat, sehingga Pod lama yang sudah berjalan tidak ikut dievaluasi.
  • Profil restricted menuntut runAsNonRoot, seccompProfile, allowPrivilegeEscalation false, dan capabilities drop ALL dengan pengecualian penambahan NET_BIND_SERVICE.

Referensi#