Automated Active Directory Labs with GOAD and Ludus on Proxmox
Deploy repeatable Active Directory attack ranges on Proxmox using Ludus and GOAD, complete with hardware sizing constraints, API key caveats, and Elastic SIEM integration.
5 min read
On this page
Spinning up multi-domain Active Directory environments manually is a great way to waste an entire weekend clicking through Server Manager wizards. Game of Active Directory (GOAD) solves the configuration headaches, while Ludus solves the infrastructure orchestration.
Combining them on a Proxmox host gives you a push-button Active Directory attack range that you can deploy, snapshot, break, and tear down without nuking your homelab.
1. Hardware & System Prerequisites
Active Directory domains are resource hogs. Running five or six Windows VMs concurrently requires realistic hardware sizing.
Hardware Targets (Homelab Mini-PC or Server)
- Hypervisor: Proxmox VE 8.0+ (Tested on Proxmox 8.4.4 and 8.4.14)
- CPU: 8 cores / 16 threads minimum (more cores allow multiple concurrent labs)
- RAM: 32 GB minimum (64 GB strongly recommended for Full GOAD + SIEM)
- Storage: 500 GB to 1 TB NVMe/SSD storage for VM disks and templates
Sizing Strategy
By default, standard enterprise templates will chew through 32 GB of memory before the third domain controller even boots. Size each VM template conservatively: 4 GB RAM and 2 vCPUs. This keeps memory pressure manageable on a single 32 GB host.
2. Infrastructure Setup: Installing Ludus
Ludus acts as the orchestration layer between your configuration files and the Proxmox API.
Install Ludus on Proxmox
SSH into your Proxmox server as root:
curl -s https://ludus.cloud/install | bash
chmod +x install.sh
./install.sh
Retrieve the Root API Key & Create an Admin User
Once the installer finishes, query the installation status to get the default root key:
ludus-install-status
Export the root key and create a dedicated administrative user (GA / Goad Admin):
export LUDUS_API_KEY='ROOT.<key>'
ludus user add --name "Goad Admin" --userid GA --admin --url https://127.0.0.1:8081
API Key Escaping Caveat: If your generated API key contains a
%character, you must escape it as%%when placing it into configuration files. Python's configparser interprets lone%symbols as interpolation tokens and will crash immediately.
Export your new admin key:
export LUDUS_API_KEY='GA.<your_admin_key>'
3. Building Base OS Templates
Ludus needs base VM templates in Proxmox to clone during range provisioning.
git clone https://gitlab.com/badsectorlabs/ludus
cd ludus/templates
ludus templates add -d win2019-server-x64
ludus templates add -d win2016-server-x64
ludus templates add -d debian-12-x64-server
ludus templates list
ludus templates build
Monitor the build progress in another shell:
ludus templates list
Adjusting Template Resources in Proxmox
Once the build completes:
- Open the Proxmox web UI.
- Navigate to each created template.
- Under Hardware, tune Memory to
4096 MB(4 GB) and Processors to2 vCPUs.
4. Configuring the GOAD Management Script
Orange Cyberdefense provides an interactive CLI wrapper (goad.sh) that hooks into Ludus to deploy Active Directory topologies.
Clone & Install GOAD Dependencies
git clone https://github.com/Orange-Cyberdefense/GOAD.git
cd GOAD
pip install poetry
poetry env use python3.11
source $(poetry env info --path)/bin/activate
poetry install
If you encounter dependency resolution errors with requests or urllib, force-update them:
poetry run pip install --upgrade urllib3 chardet charset-normalizer requests
Generate and Edit goad.ini
Run the script once to generate the default configuration directory:
./goad.sh -p ludus
exit
Open ~/.goad/goad.ini for editing:
[ludus]
; Ensure all '%' symbols in the key are doubled (%%)
ludus_api_key = GA.YourKeyHere%%WithEscapedSymbols
use_impersonation = no
Impersonation vs. Direct User Modes
use_impersonation = no: All ranges deploy under your single admin account (GA). Simpler to manage if you only run one lab at a time.use_impersonation = yes: The admin user automatically spins up a separate isolated Ludus user per deployed range. Best if multiple people share the server or if you want strict isolation between lab environments.
5. Deploying the Range
Launch the interactive GOAD console:
./goad.sh -p ludus
Inside the GOAD CLI:
check # Validates connection to Ludus and config parameters
status # Checks currently running lab instances
set_lab GOAD-Light # Lab options: GOAD, GOAD-Light, GOAD-Mini, NHA, SCCM
set_ip_range 10.2.10 # Define the subnet octets for the lab network
install # Initiates provisioning and Ansible configuration
Monitoring the Build
Open another terminal to follow provisioning and Ansible configuration logs in real time:
ludus --user GA range logs -f
6. Lab Extensions: Elastic SIEM Integration
If you want detection engineering telemetry along with your offensive testing, deploy the Elastic SIEM container and forwarders.
From within the GOAD interactive menu:
load # Select and load your deployed range into context
list_extensions # Displays available add-ons
install_extension elk # Provisions the Debian SIEM container & ships agent configs
This installs an Elastic stack container and automatically deploys Elastic Agents to the domain controllers and member servers.
7. Post-Deployment: Snapshots & WireGuard Access
Creating Clean Baselines
Before launching any exploit scripts, take a baseline snapshot of all VMs in the range. You can roll back here whenever Kerberos tickets, ACLs, or services get trashed:
ludus --user GA range snapshot -n "clean-baseline"
Accessing the Range via WireGuard
Ludus isolates lab networks inside private VLANs. External access is routed through a dedicated WireGuard tunnel:
- Retrieve your user's WireGuard configuration file:
ludus --user GA range wireguard - Import the returned configuration into your local attack machine's WireGuard client.
- Bring up the tunnel:
sudo wg-quick up wg-ludus.conf - Verify routing by pinging the domain controller:
ping 10.2.10.10
8. Operational Playbook & Troubleshooting
DNS Resolution Issues during Provisioning
If Windows VMs fail to resolve internal domain resources during provisioning, the static DNS assignment may have drifted to the gateway address.
Log in to the VM console via Proxmox and reset the interface DNS in PowerShell:
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses @("10.2.10.10")
(Replace 10.2.10.10 with the actual IP address of your primary Domain Controller).
Teardown & Rebuilding
If a deployment fails halfway through, let the runner finish reporting errors, then destroy the lab context completely before attempting a re-run:
./goad.sh -p ludus
load # Pick the broken lab
destroy # Tear down VMs and associated networking
exit
If you need to purge the range user from Ludus:
export LUDUS_API_KEY='GA.<key>'
ludus users list all
ludus user rm -i <USER_ID> --url https://127.0.0.1:8081