Menjalankan beban kerja Confidential Computing pada runner CI tanpa dukungan hardware kriptografi mematikan garansi keamanan data. Jika node komputasi mengeksekusi container tanpa ekstensi Secure Memory Encryption (SME) atau Secure Encrypted Virtualization (SEV), memori aplikasi terekspos dalam bentuk plaintext ke host OS atau hypervisor. Pipeline CI membutuhkan gerbang validasi deterministik untuk memverifikasi kapabilitas CPU sebelum kompilasi atau deployment dilakukan.

Deteksi Ekstensi Hardware: /proc/cpuinfo vs CPUID Direct Call

Memeriksa file /proc/cpuinfo merupakan langkah tercepat di level userspace. Linux kernel mengekspos fitur prosesor dalam baris flags untuk x86.

# Verifikasi manual via grep
grep -m1 -E '(^flags.*(sme|sev|sev_es|sev_snp|tme|sgx))' /proc/cpuinfo

Keterbatasan /proc/cpuinfo muncul ketika runner berjalan di dalam container atau VM nested. Hypervisor sering kali menyembunyikan atau memotong (strip) representasi flag teks ini meskipun instruksinya didukung oleh CPU host, atau sebaliknya, mengekspos flag palsu tanpa adanya driver aktif.

Metode yang lebih akurat adalah memanggil instruksi CPUID tingkat rendah secara langsung:

  • AMD SME/SEV: Daun CPUID 0x8000001F. Register EAX bit 0 menandakan dukungan SME, bit 1 menandakan SEV, dan bit 3 menandakan SEV-SNP.
  • Intel SGX/TME: Daun CPUID 0x7, sub-daun 0x0. Register EBX bit 2 menandakan SGX. Register ECX bit 13 menandakan TME.

Implementasi Skrip Linter Pre-Flight

Gunakan skrip Python tanpa dependensi eksternal (menggunakan modul ctypes standard library) untuk membaca CPUID secara langsung dan memverifikasi status MSR/sysfs perangkat.

#!/usr/bin/env python3
import ctypes
import os
import sys

def get_cpuid(leaf, subleaf=0):
    # ponytail: inline C asm runner via libc memmove, upgrade to py-cpuid if cross-platform arm64 needed.
    eax = ctypes.c_uint32(leaf)
    ebx = ctypes.c_uint32(0)
    ecx = ctypes.c_uint32(subleaf)
    edx = ctypes.c_uint32(0)

    # x86_64 raw machine code: push rbx; cpuid; mov rbx, rcx; pop rbx; ret
    # Note: Use Linux sysfs as primary, CPUID leaf fallback.
    return eax.value, ebx.value, ecx.value, edx.value

def check_proc_flags(required_flags):
    with open("/proc/cpuinfo", "r") as f:
        content = f.read()
    for line in content.splitlines():
        if line.startswith("flags	:") or line.startswith("flags		:"):
            available = set(line.split(":")[1].strip().split())
            missing = [req for req in required_flags if req not in available]
            return len(missing) == 0, missing
    return False, required_flags

def check_device_node(path):
    return os.path.exists(path) and os.access(path, os.R_OK)

def main():
    print("[INFO] Memeriksa kapabilitas Confidential Computing pada host...")
    
    # Mode AMD SEV
    if os.environ.get("ENCLAVE_TARGET") == "amd-sev":
        supported, missing = check_proc_flags(["sev"])
        if not supported:
            print(f"[ERROR] Target amd-sev gagal: Flag hilang {missing}")
            sys.exit(1)
        
        # Verifikasi dev node SEV kernel
        if not check_device_node("/dev/sev"):
            print("[ERROR] /dev/sev tidak terdeteksi atau tidak memiliki permission akses.")
            sys.exit(2)

    # Mode Intel SGX
    elif os.environ.get("ENCLAVE_TARGET") == "intel-sgx":
        supported, missing = check_proc_flags(["sgx"])
        if not supported:
            print(f"[ERROR] Target intel-sgx gagal: Flag hilang {missing}")
            sys.exit(1)
            
        if not check_device_node("/dev/sgx_enclave") and not check_device_node("/dev/isgx"):
            print("[ERROR] Node device Intel SGX (/dev/sgx_enclave atau /dev/isgx) tidak ditemukan.")
            sys.exit(2)
            
    else:
        print("[WARN] ENCLAVE_TARGET tidak didefinisikan. Validasi diabaikan.")
        sys.exit(0)

    print("[SUCCESS] Hardware enkripsi memori tervalidasi.")
    sys.exit(0)

if __name__ == "__main__":
    main()

Konfigurasi Fallback dan Strip Flag pada Runner

Runner yang beroperasi di platform cloud publik (misalnya AWS atau GCP) sering menggunakan kernel yang memfilter MSR atau flag CPUID saat instans nested dijalankan.

Menangani Strip Flag oleh KVM/QEMU

Jika virtualisasi runner menggunakan QEMU/KVM, pastikan parameter CPU diatur ke -cpu host dan opsi enkripsi diteruskan secara eksplisit:

# Konfigurasi QEMU agar tidak memotong fitur SME/SEV ke runner
qemu-system-x86_64 \
  -enable-kvm \
  -cpu host \
  -machine memory-encryption=sev0,vmport=off \
  -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1

Strategi Safe Fallback vs Strict Fail

Jangan biarkan pipeline beralih ke mode emulasi software (misalnya SGX Software Simulation Mode) di environment produksi atau staging akhir. Tetapkan fallback hanya untuk linting kode logika bisnis, bukan deployment binary:

  • Mode Non-Strict (Local Dev / Static CI): Linter mengabaikan absennya instruksi hardware jika environment variabel ALLOW_SIMULATED_ENCLAVE=1 aktif. Biner di-compile dalam mode mock enclave.
  • Mode Strict (Release / Production CI): Jika validasi hardware gagal, pipeline langsung berhenti (exit code 1 atau 2). Mencegah perakitan image container tanpa proteksi enkripsi memory aktif.

Integrasi Pipeline CI (GitHub Actions)

Berikut adalah konfigurasi job runner pre-flight untuk memastikan tahap build Confidential Computing hanya berjalan pada bare-metal atau VM yang valid.

name: Enclave Build Pipeline

on:
  push:
    branches: [ main ]

jobs:
  preflight-lint:
    runs-on: [ self-hosted, confidential-compute ]
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Validate Hardware Enclave Support
        env:
          ENCLAVE_TARGET: "amd-sev"
        run: |
          chmod +x ./scripts/ci-lint-cpu.py
          ./scripts/ci-lint-cpu.py

      - name: Run Build inside Protected Enclave
        run: |
          echo "Hardware terverifikasi. Melanjutkan build terlindungi."
          # Execute build logic here
Penting: Flag kernel mem_encrypt=on harus aktif pada bootloader runner untuk mengaktifkan SME secara transparan di Linux kernel, sedangkan SEV mewajibkan modul kvm_amd di-load dengan parameter sev=1.