Files

235 lines
7.7 KiB
Ruby

# -*- mode: ruby -*-
# vi: set ft=ruby :
# Transportable Detonation Chamber - macOS Apple Silicon (QEMU/HVF)
# Windows 11 ARM VM with Detonator + DetonatorAgent + Fibratus + Rustinel + LitterBox
#
# This Vagrantfile targets macOS hosts with Apple Silicon (M1/M2/M3/M4)
# using the vagrant-qemu provider and a Windows 11 ARM64 guest.
#
# Prerequisites:
# Run the automated setup script:
# ./scripts/check-prerequisites.sh --fix
#
# Or manually install:
# 1. Homebrew: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 2. QEMU: brew install qemu
# 3. swtpm: brew install swtpm (for TPM 2.0 emulation)
# 4. Vagrant: brew install --cask vagrant
# 5. Plugin: vagrant plugin install vagrant-qemu
# 6. Box: ./scripts/build-box-macos.sh (builds win11-arm box from ISO)
#
# Usage:
# make up # or: VAGRANT_VAGRANTFILE=Vagrantfile.utm vagrant up --provider=qemu
# make rdp # Connect via RDP
# make status # Check services
# make halt # Stop the VM
# make destroy # Delete the VM
# --- Helper: Locate EFI firmware ---
# Homebrew on Apple Silicon installs to /opt/homebrew, Intel to /usr/local
def find_efi_firmware
candidates = [
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd",
"/usr/local/share/qemu/edk2-aarch64-code.fd",
"#{ENV['HOMEBREW_PREFIX']}/share/qemu/edk2-aarch64-code.fd",
].compact
candidates.each do |path|
return path if File.exist?(path)
end
# Fallback: try to find via brew
brew_prefix = `brew --prefix 2>/dev/null`.strip rescue "/opt/homebrew"
efi = "#{brew_prefix}/share/qemu/edk2-aarch64-code.fd"
return efi if File.exist?(efi)
abort <<~ERR
ERROR: EFI firmware not found. Searched:
#{candidates.join("\n ")}
Install QEMU via Homebrew:
brew install qemu
ERR
end
# --- Helper: Locate or create EFI vars file ---
def ensure_efi_vars(vagrant_dir)
vars_file = File.join(vagrant_dir, "efivars.fd")
unless File.exist?(vars_file)
# Create a 64MB empty file for EFI variable storage
File.open(vars_file, "wb") { |f| f.write("\x00" * (64 * 1024 * 1024)) }
end
vars_file
end
# --- Helper: Set up TPM socket ---
def tpm_socket_path(vagrant_dir)
File.join(vagrant_dir, "tpm")
end
EFI_CODE = find_efi_firmware
Vagrant.configure("2") do |config|
# Windows 11 ARM64 box - built by scripts/build-box-macos.sh
config.vm.box = "win11-arm"
config.vm.hostname = "detonation-chamber"
# Communicator: WinRM over HTTP (port 5985)
config.vm.communicator = "winrm"
config.winrm.username = "vagrant"
config.winrm.password = "vagrant"
config.winrm.timeout = 1800
config.winrm.retry_limit = 30
config.winrm.transport = :plaintext
config.winrm.basic_auth_only = true
# Network: expose service ports
# Note: vagrant-qemu translates these to QEMU hostfwd rules automatically
config.vm.network "forwarded_port", guest: 5985, host: 55985, id: "winrm", auto_correct: true
config.vm.network "forwarded_port", guest: 5000, host: 5000 # Detonator Web UI
config.vm.network "forwarded_port", guest: 8000, host: 8000 # Detonator REST API
config.vm.network "forwarded_port", guest: 8080, host: 8080 # DetonatorAgent API
config.vm.network "forwarded_port", guest: 1337, host: 1337 # LitterBox Web UI
config.vm.network "forwarded_port", guest: 9000, host: 9000 # Unified Web UI
config.vm.network "forwarded_port", guest: 8888, host: 8888 # theZoo-WebUI
config.vm.network "forwarded_port", guest: 3389, host: 53389 # RDP
# QEMU provider settings (vagrant-qemu plugin)
config.vm.provider "qemu" do |qe|
qe.arch = "aarch64"
qe.machine = "virt,highmem=on"
qe.cpu = "host"
qe.smp = "cpus=4,sockets=1,cores=4,threads=1"
qe.memory = "8G"
qe.net_device = "virtio-net-pci"
# Apple Hypervisor.framework - native speed on Apple Silicon
qe.accel = "hvf"
# Disk: virtio-blk (must match how the box was built)
qe.drive_interface = "virtio"
qe.disk_size = "80G"
# Extra QEMU arguments for Windows 11 ARM64 boot
vagrant_dir = File.join(Dir.pwd, ".vagrant", "machines", "default", "qemu")
FileUtils.mkdir_p(vagrant_dir)
efi_vars = ensure_efi_vars(vagrant_dir)
extra_args = [
# EFI firmware (pflash: read-only code + writable vars for NVRAM)
"-drive", "if=pflash,format=raw,readonly=on,file=#{EFI_CODE}",
"-drive", "if=pflash,format=raw,file=#{efi_vars}",
# Display and input devices
"-device", "virtio-gpu-pci",
"-device", "qemu-xhci",
"-device", "usb-kbd",
"-device", "usb-tablet",
]
# TPM 2.0 support (required for Windows 11 unless bypassed in the box)
# Uses swtpm if available - if not, the box must have TPM requirement bypassed
tpm_dir = tpm_socket_path(vagrant_dir)
swtpm_bin = `which swtpm 2>/dev/null`.strip
if !swtpm_bin.empty? && File.exist?(swtpm_bin)
FileUtils.mkdir_p(tpm_dir)
# swtpm will be started by the provisioning wrapper; add device to QEMU
extra_args += [
"-chardev", "socket,id=chrtpm,path=#{tpm_dir}/swtpm-sock",
"-tpmdev", "emulator,id=tpm0,chardev=chrtpm",
"-device", "tpm-tis-device,tpmdev=tpm0",
]
end
qe.extra_qemu_args = extra_args
end
# Increase boot timeout (Windows ARM64 can be slow on first boot)
config.vm.boot_timeout = 1200
# Disable default synced folder (not supported by QEMU user-mode networking)
config.vm.synced_folder ".", "/vagrant", disabled: true
# --- File provisioners: copy project files into VM ---
config.vm.provision "file", source: "config", destination: "C:\\vagrant_config"
config.vm.provision "file", source: "webui", destination: "C:\\vagrant\\webui"
config.vm.provision "file", source: "rules", destination: "C:\\vagrant\\rules"
# --- Shell provisioners: install everything ---
# All scripts detect ARM64 vs x86_64 and handle architecture differences.
# Tools without native ARM64 builds (Fibratus, Rustinel) run under
# Windows' x86_64 emulation layer. ETW kernel tracing works but with
# slight overhead under emulation.
config.vm.provision "prerequisites",
type: "shell",
path: "scripts/install-prerequisites.ps1",
privileged: true
config.vm.provision "sysmon",
type: "shell",
path: "scripts/install-sysmon.ps1",
privileged: true
config.vm.provision "fibratus",
type: "shell",
path: "scripts/install-fibratus.ps1",
privileged: true
config.vm.provision "rustinel",
type: "shell",
path: "scripts/install-rustinel.ps1",
privileged: true
config.vm.provision "detection-rules",
type: "shell",
path: "scripts/install-detection-rules.ps1",
privileged: true
config.vm.provision "detonator",
type: "shell",
path: "scripts/install-detonator.ps1",
privileged: true
config.vm.provision "litterbox",
type: "shell",
path: "scripts/install-litterbox.ps1",
privileged: true
config.vm.provision "thezoo",
type: "shell",
path: "scripts/install-thezoo.ps1",
privileged: true
config.vm.provision "hunt-sleeping-beacons",
type: "shell",
path: "scripts/install-hunt-sleeping-beacons.ps1",
privileged: true
config.vm.provision "beaconeye",
type: "shell",
path: "scripts/install-beaconeye.ps1",
privileged: true
config.vm.provision "scanner-tools",
type: "shell",
path: "scripts/install-scanner-tools.ps1",
privileged: true
config.vm.provision "re-tools",
type: "shell",
path: "scripts/install-re-tools.ps1",
privileged: true
config.vm.provision "webui",
type: "shell",
path: "scripts/install-webui.ps1",
privileged: true
config.vm.provision "configure",
type: "shell",
path: "scripts/configure-services.ps1",
privileged: true,
run: "always"
end