Cluster Hardening #

Keamanan aplikasi kontainer yang berjalan di Kubernetes tidak pernah berdiri sendiri. Segala bentuk pengerasan (hardening) di level aplikasi — seperti membatasi kemampuan kontainer melalui SecurityContext, melakukan segmentasi jaringan melalui NetworkPolicy, atau memverifikasi tanda tangan image di pipa CI/CD — akan menjadi sia-sia jika infrastruktur kluster di bawahnya tidak dikonfigurasi dengan aman. Jika sebuah Kubernetes API Server dapat diakses tanpa autentikasi, database etcd terekspos ke jaringan publik, atau kubelet pada node worker menerima perintah secara anonim, penyerang dapat dengan mudah mem-bypass RBAC dan mengambil alih kendali penuh atas seluruh kluster host. Artikel ini membahas secara komprehensif langkah-langkah pengerasan (cluster hardening) pada level infrastruktur kluster untuk membangun benteng pertahanan yang tangguh terhadap ancaman eskalasi hak akses tingkat rendah.


Arsitektur Control Plane yang Diperkeras (Hardened) #

Dalam kluster Kubernetes yang aman, setiap jalur komunikasi antar komponen control plane dan worker node harus dilindungi dengan enkripsi Mutual TLS (mTLS) dan dibatasi oleh firewall. Diagram berikut menggambarkan batas keamanan (security boundary) dan jalur komunikasi yang diperkeras untuk mencegah penyadapan (eavesdropping) atau injeksi perintah (command injection) dari jaringan internal.

flowchart TD
    User["Client (kubectl/Developer)"] -->|"mTLS / OIDC / Bastion"| Fire["Firewall / Load Balancer"]
    Fire -->|"Port 6443"| APIServer["kube-apiserver (Hardened)"]
    
    subgraph ControlPlane["Master Node Control Plane (Hardened)"]
        direction TB
        APIServer -->|"mTLS (Port 2379)"| etcd["etcd Database"]
        etcd -->|"KMS / Encryption at Rest"| EncryptConfig["EncryptionProviderConfig"]
        Scheduler["kube-scheduler"] -->|"mTLS"| APIServer
        ControllerManager["kube-controller-manager"] -->|"mTLS"| APIServer
    end
    
    subgraph WorkerNode["Worker Node (Hardened)"]
        direction TB
        APIServer -->|"Webhook Authentication (Port 10250)"| Kubelet["kubelet daemon"]
        Kubelet -->|"gRPC"| CRI["Container Runtime (containerd)"]
        Kubelet -->|"protectKernelDefaults: true"| Kernel["Linux Kernel (Sysctl Hardened)"]
    end

Mengamankan Kubernetes API Server #

Kubernetes API Server (kube-apiserver) adalah pusat administrasi kluster. Semua aksi modifikasi maupun pembacaan sistem harus melalui komponen ini. Konfigurasi default pada instalasi kluster dasar sering kali memprioritaskan kemudahan setup daripada keamanan, sehingga kita wajib meninjau ulang dan mengeraskan konfigurasi parameternya.

Flag Konfigurasi Keamanan API Server #

Berikut adalah perbandingan antara argumen konfigurasi API Server yang tidak aman (insecure) dengan konfigurasi yang diperkeras untuk produksi:

# ANTI-PATTERN: Konfigurasi API Server yang sangat rentan.
# Mengizinkan akses anonim, menggunakan mode otorisasi permisif,
# tidak membatasi koneksi kubelet, dan membiarkan port insecure (http) tetap aktif.
spec:
  containers:
  - command:
    - kube-apiserver
    - --anonymous-auth=true
    - --insecure-port=8080 # Port HTTP tanpa enkripsi dan autentikasi
    - --insecure-bind-address=0.0.0.0
    - --authorization-mode=AlwaysAllow
    - --enable-admission-plugins=AlwaysAdmit

# ==============================================================================
# BENAR: Konfigurasi API Server Produksi yang Diperkeras.
# Menerapkan mTLS penuh, membatasi akses kubelet, dan mematikan jalur HTTP.
spec:
  containers:
  - command:
    - kube-apiserver
    - --anonymous-auth=false                   # Tolak akses tanpa kredensial
    - --authorization-mode=Node,RBAC           # Batasi hak node worker dan gunakan RBAC
    - --enable-admission-plugins=NodeRestriction,PodSecurity,ResourceQuota,LimitRanger
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-key-file=/etc/kubernetes/pki/apiserver.key
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    - --tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 # Gunakan cipher kuat

Penjelasan Plugin Otorisasi Kunci #

  1. NodeRestriction: Plugin admission controller ini membatasi hak akses Kubelet pada node worker. Kubelet hanya diizinkan memodifikasi objek Node milik dirinya sendiri dan objek Pod yang secara aktif dijadwalkan berjalan di node tersebut. Ini mencegah eksploitasi lateral jika salah satu node worker berhasil diretas.
  2. PodSecurity: Menerapkan validasi otomatis di tingkat API terhadap standar keamanan Pod (Pod Security Standards) secara native di Kubernetes.

Mengamankan Database etcd #

Database etcd menyimpan seluruh data keadaan (state) kluster Kubernetes, konfigurasi, dan semua rahasia (Secret) aplikasi. Siapa saja yang mendapatkan akses baca-tulis langsung ke etcd dapat melompati seluruh mekanisme keamanan API Server, menulis objek RBAC baru secara manipulatif, dan menguasai seluruh sistem.

1. Isolasi Jaringan etcd #

Secara default, proses etcd harus dikonfigurasi untuk hanya menerima koneksi mTLS dan tidak boleh listen di alamat IP publik 0.0.0.0.

# Periksa socket port etcd di Master Node
# etcd harus listen hanya pada IP loopback (127.0.0.1) atau IP privat internal master node.
ss -tlnp | grep 2379

# Hasil yang aman (contoh):
# LISTEN  0  128  192.168.1.10:2379  0.0.0.0:*  users:(("etcd",pid=1234,fd=6))

2. Konfigurasi Enkripsi Rahasia di etcd (Encryption at Rest) #

Meskipun Secret di Kubernetes disembunyikan dalam format base64, data tersebut disimpan dalam bentuk teks polos (plain text) di dalam database etcd. Kita wajib mengaktifkan Enkripsi di Tingkat Penyimpanan (Encryption at Rest) menggunakan penyedia kriptografi seperti aeadaesgcm.

# Manifest Kebijakan Enkripsi: /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      # Provider pertama dalam daftar adalah yang digunakan untuk mengenkripsi data baru.
      - aescbc:
          keys:
            - name: key1
              secret: dXNlY29uc2lnbnBhc3N3b3Jkc3VwZXJzZWNyZXQ= # Base64 dari 32-byte key
      # Provider identitas (identity) digunakan sebagai cadangan untuk membaca data lama yang belum terenkripsi.
      - identity: {}

Setelah membuat file di atas, tambahkan flag berikut ke manifest kube-apiserver.yaml dan daftarkan path-nya di dalam volumeMounts serta volumes:

- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

Untuk memverifikasi apakah enkripsi telah berjalan sukses, kita dapat menguji dengan menulis Secret baru dan membacanya langsung dari etcd menggunakan etcdctl:

# 1. Buat secret pengujian
kubectl create secret generic test-secret --from-literal=token=flag{super-secret-etcd} -n default

# 2. Baca value langsung dari biner database etcd menggunakan etcdctl
# Jika enkripsi sukses, output pencarian di etcdctl harus menampilkan data acak/terbaca 'k8s:enc:aescbc:v1'
# dan tidak boleh mengandung string 'flag{super-secret-etcd}' dalam bentuk teks biasa.
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  get /registry/secrets/default/test-secret | hexdump -C

Hardening Konfigurasi Kubelet #

Kubelet adalah agen utama yang berjalan di setiap node worker untuk mengelola siklus hidup kontainer. Secara default, port kubelet (10250) dapat diakses tanpa otentikasi penuh (anonymous access), yang merupakan pintu masuk eksploitasi eksekusi perintah jarak jauh (Remote Command Execution).

Konfigurasi File /var/lib/kubelet/config.yaml #

Kita harus memodifikasi konfigurasi kubelet pada setiap master dan worker node untuk mematikan akses anonim dan mengaktifkan otorisasi berbasis webhook.

# ANTI-PATTERN: Konfigurasi default Kubelet yang rentan.
# Mengizinkan anonymous auth, mode otorisasi AlwaysAllow, dan membuka readonly port (10255).
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: true
authorization:
  mode: AlwaysAllow
readOnlyPort: 10255

# ==============================================================================
# BENAR: Konfigurasi Kubelet yang Diperkeras untuk Produksi.
# File: /var/lib/kubelet/config.yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false                   # Matikan akses tanpa autentikasi
  webhook:
    enabled: true                    # Autentikasi token via Kube-API Server
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook                      # Minta keputusan otorisasi dari Kube-API Server
readOnlyPort: 0                      # Nonaktifkan port read-only (10255/tcp)
protectKernelDefaults: true          # Tolak startup kubelet jika sysctl kernel tidak sesuai parameter keamanan
rotateCertificates: true             # Aktifkan rotasi sertifikat otomatis oleh kubelet

Setelah konfigurasi disimpan, restart service kubelet di setiap node:

systemctl restart kubelet

Hardening Node Sistem Operasi (Host Node Security) #

Keamanan kluster Kubernetes sangat bergantung pada keamanan sistem operasi host (Linux kernel). Kita harus mengamankan parameter kernel host menggunakan modul sysctl.

Parameter Pengerasan Kernel (sysctl.conf) #

Terapkan parameter pengerasan jaringan berikut di file /etc/sysctl.d/99-kubernetes-security.conf pada setiap node kluster untuk mencegah serangan manipulasi routing IP (IP spoofing) dan man-in-the-middle:

# Mencegah IP Spoofing dengan mengaktifkan Reverse Path Filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Nonaktifkan penerimaan paket ICMP redirect untuk mencegah modifikasi routing table secara ilegal
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Nonaktifkan pengiriman paket ICMP redirect (kita tidak bertindak sebagai router)
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Aktifkan perlindungan terhadap serangan SYN Flood (SYN cookies)
net.ipv4.tcp_syncookies = 1

# Batasi penggunaan core dump file oleh user biasa demi keamanan memori
fs.suid_dumpable = 0

Terapkan konfigurasi di atas dengan menjalankan perintah:

sysctl --system

Konfigurasi Firewall Node (Firewalld / UFW) #

Kita harus menutup semua port jaringan node dan hanya membuka port minimum yang diperlukan untuk komunikasi antar komponen Kubernetes.

# Konfigurasi UFW di Master Node (Control Plane)
ufw default deny incoming
ufw default allow outgoing

# Port API Server (Kritis)
ufw allow 6443/tcp
# Port etcd Client (Hanya izinkan dari master node lainnya)
ufw allow proto tcp from 192.168.1.0/24 to any port 2379,2380
# Port Kubelet API
ufw allow 10250/tcp
# Port Kube-Scheduler & Controller-Manager
ufw allow 10259/tcp
ufw allow 10257/tcp

ufw enable

Audit Keamanan Menggunakan Kube-bench #

CIS (Center for Internet Security) menerbitkan dokumen standar benchmark keamanan untuk Kubernetes. Dokumen ini berisi ratusan aturan spesifik untuk memeriksa izin file, konfigurasi modul, dan konfigurasi API kluster.

Kita dapat mengotomatiskan audit CIS Kubernetes Benchmark menggunakan alat open-source kube-bench dari Aqua Security.

Menjalankan Kube-bench sebagai Kubernetes Job #

Untuk mempermudah verifikasi kepatuhan (compliance verification) di seluruh kluster, kita dapat mendeploy kube-bench sebagai Job Kubernetes yang membaca file spesifikasi node control plane:

# File: kube-bench-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench-control-plane
  namespace: kube-system
spec:
  template:
    spec:
      hostPID: true # Kube-bench membutuhkan akses host PID untuk memeriksa parameter proses running
      containers:
      - name: kube-bench
        image: aquasec/kube-bench:latest
        command: ["kube-bench", "run", "--targets", "master"]
        volumeMounts:
        - mountPath: /var/lib
          name: var-lib
          readOnly: true
        - mountPath: /etc
          name: etc
          readOnly: true
        - mountPath: /usr/bin
          name: usr-bin
          readOnly: true
      restartPolicy: Never
      volumes:
      - name: var-lib
        hostPath:
          path: /var/lib
      - name: etc
        hostPath:
          path: /etc
      - name: usr-bin
        hostPath:
          path: /usr/bin

Deploy job tersebut dan periksa log keluaran audit:

kubectl apply -f kube-bench-job.yaml

# Periksa log hasil audit
kubectl logs -f job.batch/kube-bench-control-plane -n kube-system

Output log akan menampilkan rincian hasil penilaian (PASS, FAIL, atau WARN) serta langkah perbaikan yang direkomendasikan secara tepat:

[FAIL] 1.1.1 Ensure that the API server pod specification file permissions are set to 600 or more restrictive
[FAIL] 1.1.2 Ensure that the API server pod specification file ownership is set to root:root

# Rekomendasi Solusi perbaikan yang diberikan:
# Run: chmod 600 /etc/kubernetes/manifests/kube-apiserver.yaml
# Run: chown root:root /etc/kubernetes/manifests/kube-apiserver.yaml

Manajemen Patch dan Siklus Hidup Upgrade #

Kluster yang aman hari ini dapat menjadi rentan esok hari jika kita mengabaikan rilis patch keamanan. Siklus hidup Kubernetes sangat dinamis, di mana versi minor baru dirilis setiap 4 bulan sekali dan versi lama (lebih dari 3 versi ke belakang) akan kehilangan dukungan security backport.

Prosedur Upgrade Kluster yang Aman #

Saat melakukan pembaruan versi Kubernetes (misalnya dari v1.28 ke v1.29), kita harus meminimalkan risiko gangguan beban kerja (workload downtime) dengan mengosongkan node secara teratur.

# 1. Drain node target untuk mengevakuasi semua pod yang berjalan ke node lain dengan aman
kubectl drain worker-node-1 --ignore-daemonsets --delete-emptydir-data

# 2. Hubungkan ke node target melalui SSH dan lakukan upgrade komponen kubeadm & kubelet
ssh worker-node-1
apt-mark unhold kubeadm kubelet
apt-get update && apt-get install -y kubeadm=1.29.0-1.1 kubelet=1.29.0-1.1
kubeadm upgrade node
systemctl daemon-reload
systemctl restart kubelet
apt-mark hold kubeadm kubelet
exit

# 3. Kembalikan node agar dapat menerima penjadwalan pod baru
kubectl uncordon worker-node-1

Prosedur ini memastikan bahwa transisi versi komponen berjalan secara bertahap tanpa memicu pemadaman layanan aplikasi secara mendadak.


Checklist Pengerasan Kluster (Cluster Hardening) #

Sebelum kluster Kubernetes dinyatakan siap untuk menampung beban kerja produksi yang sensitif, pastikan seluruh konfigurasi berikut telah diterapkan:

KONTROL KEAMANAN API SERVER:
  □ Argumen '--anonymous-auth' diatur ke nilai 'false'.
  □ Mode otorisasi diatur ke '--authorization-mode=Node,RBAC'.
  □ Port insecure '--insecure-port=0' dinonaktifkan sepenuhnya.
  □ Plugin admission control 'NodeRestriction' diaktifkan.
  □ Cipher suites TLS dikonfigurasi untuk hanya menerima enkripsi kuat.

KONTROL KEAMANAN DATABASE ETCD:
  □ Port etcd (2379, 2380) dibatasi hanya untuk interface lokal atau internal.
  □ Otentikasi Mutual TLS (mTLS) diaktifkan penuh untuk akses client API Server ke etcd.
  □ Enkripsi Secret at-Rest aktif menggunakan file 'EncryptionConfiguration' ber-provider AES-CBC/GCM.
  □ Kunci enkripsi dirotasi secara berkala secara manual atau terintegrasi dengan KMS.

KONTROL KEAMANAN KUBELET (NODE):
  □ Otentikasi anonim kubelet dinonaktifkan ('authentication.anonymous.enabled: false').
  □ Otorisasi kubelet diatur ke mode 'Webhook'.
  □ Port read-only kubelet ('readOnlyPort: 0') dinonaktifkan.
  □ Konfigurasi 'protectKernelDefaults: true' diaktifkan di tingkat kubelet.

HARDENING SISTEM OPERASI NODE:
  □ Parameter kernel sysctl untuk proteksi IP spoofing dan SYN flood telah diterapkan.
  □ Port firewall host ditutup secara default (default deny) dan dikonfigurasi berdasarkan port resmi Kubernetes.
  □ Akses SSH password login dinonaktifkan di master dan worker node (key-based only).
  □ Menjalankan scan audit kube-bench secara otomatis dan rutin untuk memverifikasi CIS compliance.

Ringkasan #

  • Hardening Control Plane adalah Fondasi Utama — Keamanan container runtime tidak dapat melindungi aplikasi jika control plane (API Server, Kubelet) memiliki konfigurasi yang longgar.
  • NodeRestriction Membatasi Kerusakan Worker — Mengaktifkan admission plugin NodeRestriction mengunci izin kubelet agar tidak bisa memodifikasi objek di luar areanya jika node tersebut disusupi.
  • Enkripsi etcd Melindungi Rahasia at-Rest — Tanpa EncryptionConfiguration, nilai rahasia (Secret) di dalam etcd disimpan dalam teks polos dan rawan dibaca langsung jika database terekspos.
  • Tutup Celah Kubelet Anonim — Konfigurasikan file config.yaml kubelet agar mematikan anonymous access dan port read-only (10255) untuk memblokir eksploitasi eksekusi command interaktif.
  • Gunakan kube-bench untuk Audit CIS — Otomatiskan kepatuhan standar CIS Benchmark di tingkat produksi untuk secara proaktif mendeteksi ketidaksesuaian parameter keamanan file manifest.
  • Upgrade Berkala untuk Patch CVE — Jadwalkan siklus peningkatan versi Kubernetes setidaknya dalam waktu 3-4 bulan untuk memastikan kluster Anda tetap menerima perbaikan celah keamanan kritis secara resmi.

← Sebelumnya: Audit Logging   Berikutnya: Anti-Pattern Security →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact