Vektor Serangan Akses CLI pada VM Persistent

Virtual Machine (VM) berbasis NixOS yang digunakan sebagai CLI environment atau runner sering kali menyimpan kredensial sensitif di memori dan disk. Masalah utama pada persistent VM meliputi kebocoran SSH agent forwarding, secret build yang tertulis permanen di /nix/store, build sandbox escape melalui socket privilege escalation, dan serangan brute-force network langsung pada port SSH.

Panduan ini mengimplementasikan baseline hardening deklaratif menggunakan modul native NixOS, enkripsi SOPS, dan filtering paket nftables.

Isolasi Autentikasi SSH: Enforce Hardware Key dan Matikan Agent Forwarding

Penggunaan SSH Agent Forwarding tradisional memungkinkan pihak dengan akses root di remote machine membajak socket autentikasi lokal. Solusinya adalah mematikan socket forwarding, membatasi tipe kunci hanya ke FIDO2 hardware authenticator (ed25519-sk), dan menonaktifkan metode autentikasi legacy.

# /etc/nixos/modules/ssh-hardening.nix
{
  services.openssh = {
    enable = true;
    openFirewall = false; # Kontrol port via nftables
    settings = {
      PermitRootLogin = "no";
      PasswordAuthentication = false;
      KbdInteractiveAuthentication = false;
      X11Forwarding = false;
      AllowAgentForwarding = false;
      AllowTcpForwarding = false;
      MaxAuthTries = 3;
      # Batasi hanya algoritma kunci publik berbasis hardware token
      PubkeyAcceptedKeyTypes = "[email protected],ssh-ed25519";
    };
  };
}

Untuk user admin, daftarkan public key hasil generate FIDO2 token (ssh-keygen -t ed25519-sk -O resident):

users.users.deployer = {
  isNormalUser = true;
  extraGroups = [ "wheel" ];
  openssh.authorizedKeys.keys = [
    "[email protected] AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAI... admin@yubikey"
  ];
};

Manajemen Secret Ephemeral Berbasis RAM dengan sops-nix

NixOS menyimpan seluruh derivasi di /nix/store yang sifatnya world-readable secara default. Menaruh string kredensial di konfigurasi deklaratif akan membocorkannya ke semua user di VM.

Gunakan sops-nix untuk mendekripsi secret langsung ke direktori ramfs (atau tmpfs) saat boot, sehingga plaintext tidak pernah menyentuh persistent storage.

# Potongan flake.nix atau configuration.nix
{
  sops = {
    defaultSopsFile = ./secrets/secrets.yaml;
    age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
    
    # Mount storage secret murni di RAM
    defaultSecretsMountPoint = "/run/secrets.d";

    secrets."api_token" = {
      mode = "0400";
      owner = "deployer";
      # Terdekripsi ke tmpfs /run/secrets/api_token
    };
  };
}
Catatan Keamanan: Direktori /run di NixOS secara default adalah tmpfs. Membaca secret via path /run/secrets/<name> menjamin isi secret hilang dari RAM begitu instance mati atau di-restart, memitigasi serangan cold-boot dan eksfiltrasi image disk offline.

Mitigasi Eskalasi Hak Akses pada Nix Flake Builds

User yang memiliki akses mengeksekusi build melalui Nix daemon dapat melompati batasan sandbox jika opsi konfigurasi tidak dikunci. Parameter trusted-users memungkinkan user mengeksekusi arbitrary code di sandbox execution context.

# /etc/nixos/modules/nix-security.nix
{
  nix.settings = {
    # Hindari memasukkan user biasa ke trusted-users
    trusted-users = [ "root" ];
    allowed-users = [ "@wheel" ];

    # Wajibkan isolasi sandbox build
    sandbox = true;
    sandbox-fallback = false;
    
    # Hapus privilege network namespace saat proses nix build
    extra-sandbox-paths = [];
    
    experimental-features = [ "nix-command" "flakes" ];
  };

  # Batasi sudo rules agar user wheel tidak mengeksekusi substitusi biner tanpa proteksi
  security.sudo.execWheelOnly = true;
}

Menghapus user reguler dari trusted-users memastikan bahwa user tidak dapat mem-bypass opsi seperti sandbox = true atau menyuntikkan binary cache yang belum terverifikasi ke dalam sistem.

Rate Limiting Brute-Force Login Menggunakan nftables Native

Daripada menggunakan fail2ban yang memerlukan parsing log Python dan I/O disk konstan, implementasikan connection tracking langsung di Linux kernel network stack menggunakan modul networking.nftables.

# /etc/nixos/modules/firewall.nix
{
  networking.firewall.enable = false; # Matikan wrapper iptables legacy
  networking.nftables = {
    enable = true;
    ruleset = ''
      table inet filter {
        set ssh_meter {
          type ipv4_addr
          flags dynamic
          size 65535
          timeout 60s
        }

        chain input {
          type filter hook input priority filter; policy drop;

          # Izinkan loopback & stateful established traffic
          iifname "lo" accept
          ct state established,related accept
          ct state invalid drop

          # Rate limit SSH: Max 3 koneksi baru per 60 detik per IP
          tcp dport 22 ct state new update @ssh_meter { ip saddr limit rate over 3/minute burst 2 packets } drop
          tcp dport 22 ct state new accept
        }
      }
    '';
  };
}

Verifikasi dan Debugging

Setelah menerapkan konfigurasi dengan nixos-rebuild switch, lakukan verifikasi kontrol keamanan:

  1. Verifikasi RAM Secret: Jalankan df -T /run/secrets dan pastikan filesystem berupa tmpfs. Verifikasi permission file secret dengan stat /run/secrets/api_token untuk memastikan file beratribut 0400.
  2. Uji Koneksi FIDO2: Coba login menggunakan generic SSH key non-hardware. SSH daemon harus menolak dengan log userauth_pubkey: key type ... not in PubkeyAcceptedKeyTypes.
  3. Validasi nftables: Jalankan loop verifikasi koneksi: for i in {1..5}; do nc -zv <IP-VM> 22; done. Paket ke-4 dan seterusnya harus di-drop di level network layer.