LA NINA // BLOG
Kembali ke Indeks Catatan

Rekayasa Zero-Trust IAM & Linux SecOps: Workload Attestation SPIFFE/SPIRE, Kernel LSM via eBPF, dan Federasi OIDC Keycloak Enterprise

Paradigma Zero-Trust Modern: Dekonstruksi Model Keamanan Perimeter

Dalam lanskap komputasi cloud-native terdistribusi, model keamanan perimeter tradisional berbasis batasan jaringan fisik maupun virtual (Castle-and-Moat Architecture) telah kehilangan relevansi operasionalnya. Mengandalkan firewall perimeter, VPN terpusat, dan daftar putih blok CIDR statis menciptakan ilusi keamanan yang rentan runtuh di hadapan beberapa realitas teknis:

  1. Efemeritas dan Daur Hidup Pod yang Pendek: Pada kluster Kubernetes skala besar, IP workload dialokasikan secara dinamis dan memiliki masa pakai hitungan menit. Mempercayai identitas service berdasarkan IP membuka celah eksploitasi IP recycling reuse dan ARP/IP spoofing di dalam overlay SDN.
  2. Eskalasi Lateral Tanpa Hambatan (Unchecked Lateral Movement): Begitu penyerang menembus satu celah (misalnya melalui kerentanan Remote Code Execution pada frontend web publik), mereka memperoleh akses implisit ke jaringan lokal privat tanpa perlu memvalidasi identitas kriptografis pada setiap komunikasi inter-service.
  3. Prinsip Fundamental NIST SP 800-207: Doktrin "Never Trust, Always Verify" menuntut bahwa setiap transaksi komputasiβ€”baik komunikasi service-to-service, akses pengguna ke API, maupun interaksi kernelβ€”harus dibuktikan identitasnya secara kriptografis (Cryptographically Verifiable Workload Identity), terikat konteks terkini, dan dienkripsi timbal balik (Mutual TLS).

Artikel ini membedah implementasi arsitektur Zero-Trust Identity and Access Management (IAM) tingkat enterprise yang memadukan framework SPIFFE/SPIRE untuk identitas workload otomatis, penegakan kontrol eksekusi pada level Linux Security Modules (LSM) melalui eBPF, pertahanan pipeline Linux PAM, serta orkestrasi federasi identitas terdistribusi menggunakan Keycloak High-Availability. Seluruh contoh konfigurasi dan telemetri disajikan menggunakan data sintetis sesuai standar RFC 5737.


1. Anatomi Low-Level SPIFFE/SPIRE: Node Attestation & Workload Attestation

Standar terbuka SPIFFE (Secure Production Identity Framework for Everyone) mendefinisikan skema URI terstandardisasi untuk merepresentasikan identitas software tanpa bergantung pada kredensial statis. SPIRE (SPIFFE Runtime Environment) mengimplementasikan spesifikasi tersebut ke dalam daemon kontrol produksi.

TEXT
========================================================================================
                     TOPOLOGI ZERO-TRUST WORKLOAD ATTESTATION SPIRE
========================================================================================

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                          SPIRE SERVER CLUSTER                          β”‚
  β”‚  - Trust Domain: prod.lanina.internal                                  β”‚
  β”‚  - Datastore: PostgreSQL Multi-AZ Replica (192.0.2.15:5432)            β”‚
  β”‚  - OIDC Discovery Provider & Upstream CA Integration                   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚
                                      β”‚ gRPC over TLS (Bootstrap Token / PSAT)
                                      β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                    KUBERNETES NODE (198.51.100.24)                     β”‚
  β”‚                                                                        β”‚
  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
  β”‚   β”‚                      SPIRE AGENT DAEMON                        β”‚   β”‚
  β”‚   β”‚  - Socket: /run/spire/sockets/agent.sock (Unix Domain Socket)  β”‚   β”‚
  β”‚   β”‚  - Node Attestor: k8s_psat (Projected Service Account Token)   β”‚   β”‚
  β”‚   β”‚  - Workload Attestors: k8s (cgroup v2 inspection) + unix       β”‚   β”‚
  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
  β”‚                                   β”‚                                    β”‚
  β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
  β”‚         β”‚ Workload API via Kernel Peering (SO_PEERCRED)    β”‚           β”‚
  β”‚         β–Ό                                                  β–Ό           β”‚
  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
  β”‚   β”‚     Microservice Pod      β”‚              β”‚  Sidecar Proxy      β”‚   β”‚
  β”‚   β”‚     (Payment Service)     β”‚              β”‚  (Envoy mTLS SDS)   β”‚   β”‚
  β”‚   β”‚                           β”‚              β”‚                     β”‚   β”‚
  β”‚   β”‚  PID: 41829, UID: 10001   β”‚              β”‚  Fetches X509-SVID  β”‚   β”‚
  β”‚   β”‚  SPIFFE ID:               β”‚              β”‚  Rotates dynamicallyβ”‚   β”‚
  β”‚   β”‚  spiffe://prod.lanina.    β”‚              β”‚  Zero reload delay  β”‚   β”‚
  β”‚   β”‚  internal/ns/core/sa/pay  β”‚              β”‚                     β”‚   β”‚
  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Mekanisme Pengikatan Identitas (Attestation Flow)

Proses pemberian identitas SPIFFE ID (spiffe://<trust-domain>/<workload-path>) dilakukan tanpa membocorkan secret statis melalui dua tahapan berjenjang:

A. Node Attestation (Pembuktian Integritas Host)

Sebelum agent diizinkan meminta sertifikat atas nama beban kerja, agent harus membuktikan bahwa ia berjalan di atas node komputasi yang sah:

  1. Kubelet memproyeksikan token PSAT (Projected Service Account Token) dengan audiens khusus audience: ["spire-server"] ke volume pod SPIRE Agent.
  2. SPIRE Agent mengirimkan token tersebut ke SPIRE Server via gRPC.
  3. SPIRE Server mengeksekusi panggilan balik ke Kubernetes API Server (TokenReview API) untuk memvalidasi UID node, status pod, dan namespace agent.
  4. Setelah diverifikasi, Server menerbitkan SVID node khusus (spiffe://prod.lanina.internal/spire/agent/k8s_psat/...) untuk mengamankan kanal gRPC permanen.

B. Workload Attestation (Interogasi Kernel Peering)

Ketika sebuah aplikasi memanggil Unix Domain Socket milik SPIRE Agent:

  1. Pemeriksaan Kredensial Kernel: Agent memanggil syscall getsockopt(fd, SOL_SOCKET, SO_PEERCRED, ...) pada soket domain Unix. Linux kernel menjamin keaslian data PID, UID, dan GID proses penelpon secara deterministik tanpa manipulasi userspace.
  2. Resolusi Cgroups v2 & Containerd CRI: Agent membaca /proc/<PID>/cgroup untuk mengekstrak container ID, lalu menanyakan state runtime ke socket Containerd (/run/containerd/containerd.sock). Informasi pod name, namespace, image hash, dan service account berhasil dihimpun.
  3. Pencocokan Selektor & Penerbitan X509-SVID: Agent mencocokkan atribut proses terhadap Registration Entries. Apabila lolos, Agent langsung menyerahkan pasangan kunci privat dan sertifikat X.509 SVID ke memori proses dengan TTL sangat singkat (misalnya 10 hingga 60 menit) tanpa pernah menulis berkas kunci ke disk fisik.

2. Dynamic Envoy mTLS Sidecar Injection via SPIFFE SDS

Dalam arsitektur mesh modern, aplikasi tidak perlu mengelola kriptografi TLS secara langsung di kode backend. Envoy Proxy bertindak sebagai sidecar L7 yang berkomunikasi langsung dengan SPIRE Agent melalui Secret Discovery Service (SDS).

YAML
# /etc/envoy/envoy-spiffe-sds.yaml
# Konfigurasi Envoy Downstream mTLS menggunakan SPIFFE Workload API
static_resources:
  listeners:
  - name: ingress_mtls_listener
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 8443
    filter_chains:
    - transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificate_sds_secret_configs:
            - name: "spiffe://prod.lanina.internal/ns/finance/sa/payment-api"
              sds_config:
                api_config_source:
                  api_type: GRPC
                  transport_api_version: V3
                  grpc_services:
                  - google_grpc:
                      target_uri: "unix:/run/spire/sockets/agent.sock"
                      stat_prefix: "spire_sds_identity"
            validation_context_sds_secret_config:
              name: "spiffe://prod.lanina.internal"
              sds_config:
                api_config_source:
                  api_type: GRPC
                  transport_api_version: V3
                  grpc_services:
                  - google_grpc:
                      target_uri: "unix:/run/spire/sockets/agent.sock"
                      stat_prefix: "spire_sds_ca_bundle"
            require_client_certificate: true
      filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_backend
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: local_service_cluster
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: local_service_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: local_service_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8080

Dengan konfigurasi ini, saat SPIRE memperbarui sertifikat SVID di latar belakang, Envoy menerima stream gRPC pembaruan keypair tanpa memutus koneksi aktif (zero-downtime hot-rotation).


3. Pertahanan Kernel Linux: eBPF LSM Syscall Blocker & PAM Canary

Selain identitas beban kerja di layer aplikasi, perlindungan host fisik dan node worker Linux memerlukan mitigasi eskalasi hak akses (privilege escalation) sebelum instruksi CPU dieksekusi.

3.1 Pencegahan Eksekusi Biner Volatile via eBPF LSM

Melalui framework LSM BPF (CONFIG_BPF_LSM=y), program eBPF dapat di-attach ke security hooks internal kernel (security_bprm_check). Kode berikut memblokir setiap eksekusi biner yang bersumber dari direktori /tmp, /dev/shm, atau /var/tmp secara in-kernel:

C
// ebpf_lsm_exec_shield.c
// Kompilasi: clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -c ebpf_lsm_exec_shield.c -o ebpf_lsm_exec_shield.o
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>

char LICENSE[] SEC("license") = "GPL";
#define EPERM 1

SEC("lsm/bprm_check_security")
int BPF_PROG(restrict_volatile_exec, struct linux_binprm *bprm) {
    struct file *file = BPF_CORE_READ(bprm, file);
    if (!file) {
        return 0;
    }

    struct dentry *parent = BPF_CORE_READ(file, f_path.dentry, d_parent);
    if (!parent) {
        return 0;
    }

    struct qstr parent_qstr = BPF_CORE_READ(parent, d_name);
    char dir_name[16];
    bpf_probe_read_kernel_str(dir_name, sizeof(dir_name), parent_qstr.name);

    // Evaluasi nama folder parent
    if ((dir_name[0] == 't' && dir_name[1] == 'm' && dir_name[2] == 'p') ||
        (dir_name[0] == 's' && dir_name[1] == 'h' && dir_name[2] == 'm')) {
        
        u64 uid_gid = bpf_get_current_uid_gid();
        u32 uid = (u32)(uid_gid & 0xFFFFFFFF);

        // Cetak telemetri audit ke ring buffer trace
        bpf_printk("[ZERO-TRUST LSM BLOCK] Denied execution in /%s for UID %d\n", dir_name, uid);
        
        // Gagalkan syscall execve langsung di VFS level
        return -EPERM;
    }

    return 0;
}

Berbeda dengan IDS/SIEM tradisional seperti Falco yang hanya membunyikan alarm pasca-eksekusi (detective control), hook eBPF LSM mengembalikan -EPERM, sehingga proses penyerang seketika dibatalkan sebelum shell child di-spawn (preventative control).

3.2 Pengerasan Stack PAM & Trap Alert Real-Time

Pada lapisan autentikasi OS (/etc/pam.d/common-auth), kebijakan Zero-Trust mewajibkan penolakan login berbasis password brute-force serta menyuntikkan canary trap via pam_exec.so:

PAM
# /etc/pam.d/common-auth - Hardened Linux PAM Pipeline
auth    required    pam_env.so
auth    requisite   pam_faillock.so preauth audit deny=3 unlock_time=900 even_deny_root fail_interval=300

# Autentikasi Hardware Token U2F / WebAuthn
auth    sufficient  pam_u2f.so authfile=/etc/u2f_mappings cue origin=pam://lanina.internal

# Fallback autentikasi lokal
auth    [success=1 default=ignore]  pam_unix.so nullok_secure try_first_pass
auth    requisite   pam_faillock.so authfail

# Canary Security Execution Trap: Alert SecOps SIEM secara asinkron
auth    optional    pam_exec.so quiet /usr/local/bin/pam-canary-alert.sh

auth    required    pam_deny.so
auth    required    pam_faillock.so authsucc

Script trap /usr/local/bin/pam-canary-alert.sh menangkap variabel lingkungan PAM ($PAM_USER, $PAM_RHOST) dan mengirimkan high-severity webhook ke SIEM internal (198.51.100.90) setiap kali terdeteksi upaya autentikasi akun berhak tinggi dari IP yang tidak terdaftar.


4. Federasi Identitas Terpusat: Keycloak High-Availability & RFC 8693

Pada lapisan identitas pengguna dan federasi API, Keycloak (Quarkus engine) berperan sebagai otoritas sentral OAuth 2.0 / OpenID Connect.

TEXT
========================================================================================
                 KEYCLOAK HIGH-AVAILABILITY CLUSTER ARCHITECTURE
========================================================================================

           Cloudflare WAF / Enterprise Ingress (TLS 1.3 Termination)
                                      β”‚
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β–Ό                                     β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚   Keycloak Node 1   β”‚               β”‚   Keycloak Node 2   β”‚
        β”‚  198.51.100.31:8443 β”‚               β”‚  198.51.100.32:8443 β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚                                     β”‚
                   β”‚    JGroups Unicast TCP (DNS_PING)   β”‚
                   │◄───────────────────────────────────►│
                   β–Ό                                     β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚           Infinispan Distributed In-Memory Cache          β”‚
        β”‚      - User Sessions & Offline Tokens (owners=2)          β”‚
        β”‚      - Brute Force & Rate Limit Sliding Counters          β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                      β”‚
                                      β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚        PostgreSQL HA Patroni Cluster (192.0.2.10)         β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Konfigurasi Produksi Quarkus (/opt/keycloak/conf/keycloak.conf)

PROPERTIES
# Database Connectivity
db=postgres
db-url=jdbc:postgresql://192.0.2.10:5432/keycloak_production?sslmode=verify-full&sslrootcert=/etc/keycloak/certs/db-ca.crt
db-username=keycloak_secops
db-password=${KEYCLOAK_SECURE_PASSWORD}
db-pool-min-size=25
db-pool-max-size=120

# Distributed Caching via JGroups TCP
cache=ispn
cache-config-file=cache-ispn-cluster.xml

# TLS & Cryptographic Cipher Hardening
http-enabled=false
https-port=8443
https-key-store-file=/etc/keycloak/certs/keystore.p12
https-key-store-password=${KEYCLOAK_KEYSTORE_PASSWORD}
https-protocols=TLSv1.3,TLSv1.2
https-cipher-suites=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384

# Hostname & Proxy Enforcements
proxy-headers=xforwarded
hostname=auth.lanina.internal
hostname-strict=true
hostname-strict-backchannel=true

# Fitur Zero-Trust
features=token-exchange,admin-fine-grained-authz,webauthn
metrics-enabled=true
health-enabled=true

Pola Token Exchange (RFC 8693) untuk Mencegah Privilege Leak

Dalam topologi multi-layanan, meneruskan token JWT pengguna asli dari Service A ke Service B adalah anti-pattern berbahaya. Jika Service B disusupi, token pengguna dapat disalahgunakan untuk memanggil Service C.

Melalui Token Exchange (RFC 8693), Service A menukar token pengguna dengan token delegasi yang di-downscope secara spesifik:

BASH
# Request pertukaran token dari Service A (198.51.100.51) ke Keycloak Token Endpoint
curl -s -X POST "https://auth.lanina.internal/realms/production/protocol/openid-connect/token" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
     -d "client_id=service-a" \
     -d "client_secret=SecretSvcA2026" \
     -d "subject_token=eyJhbGciOiJSUzI1NiIsIn..." \
     -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
     -d "audience=service-b" \
     -d "requested_token_type=urn:ietf:params:oauth:token-type:access_token"

Token yang dihasilkan Keycloak hanya memiliki hak akses menuju service-b, membatasi blast radius apabila downstream service mengalami kompromi.


5. Skrip Otomasi Validasi Operasional & Audit Kepatuhan

Untuk memastikan seluruh parameter Zero-Trust berjalan sesuai ekspektasi tanpa konfigurasi yang drifting, tim SecOps mengoperasikan rangkaian skrip verifikasi otomatis.

5.1 Verifikasi SVID & Validitas SAN (verify-spiffe-svid.sh)

BASH
#!/usr/bin/env bash
# verify-spiffe-svid.sh - Validasi Workload API dan Inspeksi Masa Berlaku SVID
set -euo pipefail

SOCKET_PATH="${1:-/run/spire/sockets/agent.sock}"
OUTPUT_DIR="/tmp/spire-audit-$$"
mkdir -p "$OUTPUT_DIR"
trap 'rm -rf "$OUTPUT_DIR"' EXIT

echo "[*] Menguji konektivitas SPIRE Workload API pada: $SOCKET_PATH"
if [[ ! -S "$SOCKET_PATH" ]]; then
    echo "[-] FATAL: Unix Domain Socket $SOCKET_PATH tidak ditemukan!"
    exit 1
fi

# Fetch X.509 SVID dari SPIRE Agent
spire-agent api fetch x509 -socketPath "$SOCKET_PATH" -write "$OUTPUT_DIR" > /dev/null 2>&1 || {
    echo "[-] Gagal mengambil SVID dari socket. Periksa izin akses file socket."
    exit 2
}

SVID_FILE="$OUTPUT_DIR/svid.0.pem"
if [[ -f "$SVID_FILE" ]]; then
    SPIFFE_URI=$(openssl x509 -in "$SVID_FILE" -noout -ext subjectAltName | tr -d ' ' | grep "URI:spiffe://" | sed 's/URI://')
    EXPIRY_DATE=$(openssl x509 -in "$SVID_FILE" -noout -enddate | cut -d= -f2)
    EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
    NOW_EPOCH=$(date +%s)
    REMAINING_SECS=$(( EXPIRY_EPOCH - NOW_EPOCH ))

    echo "[+] Terverifikasi SPIFFE ID : $SPIFFE_URI"
    echo "[+] Waktu Kedaluwarsa       : $EXPIRY_DATE"
    echo "[+] Sisa Masa Berlaku (detik): $REMAINING_SECS"

    if (( REMAINING_SECS <= 0 )); then
        echo "[-] ERROR: SVID telah KEDALUWARSA!"
        exit 3
    elif (( REMAINING_SECS > 3600 )); then
        echo "[!] PERINGATAN: SVID TTL > 1 jam (${REMAINING_SECS}s). Kurangi TTL sesuai standar Zero-Trust."
    else
        echo "[+] STATUS: Kepatuhan Masa Aktif Kriptografis SVID Memenuhi Syarat."
    fi
fi

5.2 Pengujian Kepatuhan Kernel LSM & Proteksi Memori (audit-linux-secops.sh)

BASH
#!/usr/bin/env bash
# audit-linux-secops.sh - Audit Status LSM BPF dan Parameter Sysctl Kernel
set -euo pipefail

echo "[*] 1. Memeriksa Modul Keamanan Kernel Aktif (LSM)..."
if [[ -f /sys/kernel/security/lsm ]]; then
    ACTIVE_LSM=$(cat /sys/kernel/security/lsm)
    echo "    Modul Terpasang: $ACTIVE_LSM"
    if [[ "$ACTIVE_LSM" =~ "bpf" ]]; then
        echo "    [PASS] BPF LSM terdeteksi aktif dalam pipeline kernel."
    else
        echo "    [FAIL] BPF LSM TIDAK aktif! Periksa parameter GRUB 'lsm=...,bpf'."
        exit 1
    fi
fi

echo "[*] 2. Memverifikasi Proteksi Memori Kernel (Sysctl)..."
declare -A CHECKS=(
    ["kernel.randomize_va_space"]="2"
    ["kernel.kptr_restrict"]="2"
    ["fs.protected_hardlinks"]="1"
    ["fs.protected_symlinks"]="1"
    ["net.core.bpf_jit_harden"]="2"
)

for key in "${!CHECKS[@]}"; do
    expected="${CHECKS[$key]}"
    actual=$(sysctl -n "$key" 2>/dev/null || echo "MISSING")
    if [[ "$actual" == "$expected" ]]; then
        echo "    [PASS] $key = $actual"
    else
        echo "    [FAIL] $key bernilai '$actual' (ekspektasi: $expected)"
    fi
done

6. Skenario Troubleshooting Kasus Nyata (RCA & Solusi)

Kasus 1: Workload Attestation Gagal Akibat Transisi Cgroups v2

  • Gejala: Setelah node worker dimigrasikan ke Linux kernel 6.x dengan systemd cgroups v2, pod baru gagal mendapatkan X509-SVID. Log SPIRE Agent menampilkan: unable to extract container ID from cgroup path: /system.slice/containerd.service/....
  • Akar Masalah (RCA): Driver attestation SPIRE default membaca path dengan format Cgroups v1 hierarki ganda. Pada cgroup v2 unified hierarchy, representasi path di /proc/<PID>/cgroup hanya memiliki single line prefix (0::/).
  • Solusi: Aktifkan integrasi langsung Containerd CRI pada file /etc/spire/agent.conf:
    HCL
    WorkloadAttestor "k8s" {
      plugin_data {
        use_cgroup_v2 = true
        container_runtime = "containerd"
        container_runtime_endpoint = "unix:///run/containerd/containerd.sock"
      }
    }

Kasus 2: SVID Renewal Avalanche Saat Rescheduling Massal

  • Gejala: Saat terjadi cluster auto-scaling atau rolling restart pada ribuan pod, SPIRE Server mengalami lonjakan CPU hingga 100% dan throw error ResourceExhausted: connection pool exhausted. Komunikasi inter-service terganggu karena sertifikat gagal diperbarui.
  • Akar Masalah (RCA): Seluruh pod yang lahir bersamaan memiliki TTL sertifikat seragam (30 menit). Ketika mencapai batas perpanjangan (50% dari TTL), ribuan agent secara serentak membanjiri server dengan request perpanjangan kunci (Thundering Herd Problem).
  • Solusi:
    1. Pasang connection pooler PgBouncer di depan database PostgreSQL SPIRE Server untuk mengisolasi ribuan koneksi pendek.
    2. Tambahkan parameter backoff jitter pada rotasi sertifikat agent:
    HCL
    server {
      rate_limit {
        attestation = 250
        signing = 600
      }
    }

Kasus 3: Split-Brain Infinispan pada Keycloak Multi-AZ

  • Gejala: Pengguna secara acak diminta login ulang saat permintaan dialihkan dari Keycloak Pod 1 ke Pod 2. Log mencatat: ISPN000197: Split-brain detected! Current view does not contain member.
  • Akar Masalah (RCA): Konfigurasi default JGroups menggunakan protokol UDP Multicast (MPING), yang secara default diblokir oleh infrastruktur VPC cloud provider antar-Availability Zone. Kedua pod tidak saling mendeteksi dan membentuk dua kluster terisolasi.
  • Solusi: Ganti discovery transport ke protokol Unicast TCP dengan DNS headless service Kubernetes (dns.DNS_PING):
    XML
    <stack name="kubernetes-stack">
        <TCP bind_addr="0.0.0.0" bind_port="7800" />
        <dns.DNS_PING dns_query="keycloak-discovery.identity.svc.cluster.local" />
        <FD_ALL3 timeout="8000" interval="2000" />
        <pbcast.NAKACK2 use_mcast_xmit="false" />
        <UNICAST3 />
        <pbcast.GMS print_local_addr="true" join_timeout="3000" />
    </stack>

7. Matriks Tata Kelola & Evaluasi Kontrol Keamanan

Domain KeamananMekanisme EnforcingTarget SLA / MetrikMetode Verifikasi
Identitas Beban KerjaSPIFFE/SPIRE Workload APIMasa aktif sertifikat $\le$ 60 Menitverify-spiffe-svid.sh
Keamanan Kernel HosteBPF LSM (bprm_check_security)100% eksekusi /tmp dibatalkanaudit-linux-secops.sh
Akses AdministratifLinux PAM + Hardware FIDO2Maksimum 3x kegagalan sebelum lockoutfaillock --user root
Federasi IdentitasKeycloak OIDC (RFC 8693)P95 latency otorisasi $<$ 120 msbenchmark-iam-oidc.sh
Enkripsi TransitEnvoy SDS gRPC Dynamic StreamRotasi sertifikat tanpa restart binerTelemetri Prometheus Envoy

Melalui konvergensi antara SPIFFE/SPIRE untuk pertukaran identitas dinamis, eBPF LSM untuk perlindungan titik temu kernel, dan Keycloak OIDC untuk federasi terpusat, arsitektur Zero-Trust tidak lagi sekadar jargon konseptual, melainkan sistem pertahanan aktif yang tangguh, terukur, dan siap melindungi infrastruktur skala enterprise dari ancaman kontemporer.

Kembali ke Atas ↑