Installation Guide
Full setup from a bare Debian/Proxmox host to a running ThruntOps lab.
Table of contents
- 1. Install Ludus
- 2. Configure API Access
- 3. Build Templates
- 4. Install Ansible Roles
- 5. Deploy the Range
- 6. Verify
- Notes
1. Install Ludus
Full Ludus documentation at docs.ludus.cloud. Project home at ludus.cloud.
Prerequisites
- x86_64 CPU with VMX/SVM (hardware virtualization enabled in BIOS)
- Debian 12/13 or Proxmox 8/9
- Minimum 48 GB RAM, 200 GB storage (NVMe recommended)
- Wired ethernet (WiFi not supported)
- Root access + internet connectivity
- Docker must NOT be installed on the host
Install
curl --proto '=https' --tlsv1.2 -sSf https://ludus.cloud/install | bash
The installer will prompt for configuration values (defaults are fine) and reboot the machine. After reboot, monitor progress:
ludus-install-status
2. Configure API Access
Once Ludus is running, get your API key and configure the client:
ludus users apikey
Set the API URL and key in ~/.config/ludus/config.yml or via environment variables if accessing remotely.
3. Build Templates
List available templates and build the ones required by this lab:
ludus templates list
Build required templates (each can take 20–60 minutes):
ludus templates build -n debian-12-x64-server-template
ludus templates build -n win2022-server-x64-template
ludus templates build -n win11-22h2-x64-enterprise-template
ludus templates build -n kali-x64-desktop-template
LAPS-ready Windows Server 2022 template
ThruntOps requires a custom win2022 template with Security and Critical updates pre-installed. This ensures Windows built-in LAPS (KB5025230+) is available without running Windows Update during every lab deploy — saving 30–60 minutes per deployment.
Build it once from the included Packer template:
ludus templates add -d templates/win2022-server-x64-laps
ludus templates build -n win2022-server-x64-laps-template
This build takes 2–3 hours (downloads ISO + installs all updates). It only needs to be done once.
Monitor build progress:
ludus templates logs -f
Wait for all templates to show BUILT before proceeding:
ludus templates list
4. Install Ansible Roles
Install roles based on your chosen profile.
Elastic profile
# Elastic Stack roles
ludus ansible roles add badsectorlabs.ludus_elastic_container
ludus ansible roles add badsectorlabs.ludus_elastic_agent
# ADCS role
ludus ansible roles add badsectorlabs.ludus_adcs
# MSSQL
ludus ansible roles add badsectorlabs.ludus_mssql
# Custom roles (local user management, AD content, IIS, GitLab)
ludus ansible roles add https://github.com/Cyblex-Consulting/ludus-local-users/archive/refs/heads/main.tar.gz
ludus ansible roles add https://github.com/Cyblex-Consulting/ludus-ad-content/archive/refs/heads/main.tar.gz
# IIS+ASP.NET (local role — included in this repo)
ludus ansible roles add -d roles/ludus_iis
# GitLab CE — requires role_name fix before installing
curl -sL https://github.com/Cyblex-Consulting/ludus-gitlab-ce/archive/refs/heads/main.tar.gz -o /tmp/ludus-gitlab-ce.tar.gz
mkdir -p /tmp/ludus_gitlab_ce
tar -xzf /tmp/ludus-gitlab-ce.tar.gz -C /tmp/ludus_gitlab_ce --strip-components=1
sed -i 's/role_name: ludus_ad_content/role_name: ludus_gitlab_ce/' /tmp/ludus_gitlab_ce/meta/main.yml
ludus ansible roles add -d /tmp/ludus_gitlab_ce
# Local roles (included in this repo)
ludus ansible roles add -d roles/ludus_ad_content
ludus ansible roles add -d roles/ludus_gitlab_ldap
ludus ansible roles add -d roles/ludus_laps
ludus ansible roles add -d roles/ludus_ops
ludus ansible roles add -d roles/ludus_sssd
ludus ansible roles add -d roles/ludus_privesc
ludus ansible roles add -d roles/ludus_sysmon_linux
ludus ansible roles add -d roles/ludus_mssql_config
ludus ansible roles add -d roles/ludus_webapp
ludus ansible roles add -d roles/ludus_gitlab_runner
Wazuh profile
# Custom local roles — Wazuh server + agent (included in this repo)
ludus ansible roles add -d roles/ludus_wazuh_server
ludus ansible roles add -d roles/ludus_wazuh_agent
ludus ansible roles add -d roles/ludus_privesc
ludus ansible roles add -d roles/ludus_ad_content
ludus ansible roles add -d roles/ludus_laps
ludus ansible roles add -d roles/ludus_ops
ludus ansible roles add -d roles/ludus_iis
ludus ansible roles add -d roles/ludus_gitlab_ldap
ludus ansible roles add -d roles/ludus_sssd
ludus ansible roles add -d roles/ludus_mssql_config
ludus ansible roles add -d roles/ludus_webapp
ludus ansible roles add -d roles/ludus_gitlab_runner
# Galaxy / GitHub roles
ludus ansible roles add badsectorlabs.ludus_adcs
ludus ansible roles add badsectorlabs.ludus_mssql
ludus ansible roles add badsectorlabs.ludus_gitlab_ce
ludus ansible roles add https://github.com/Cyblex-Consulting/ludus-local-users/archive/refs/heads/main.tar.gz
Splunk profile
# Custom local roles — Splunk UF (included in this repo)
ludus ansible roles add -d roles/ludus_splunk
ludus ansible roles add -d roles/ludus_splunk_uf
ludus ansible roles add -d roles/ludus_privesc
ludus ansible roles add -d roles/ludus_ad_content
ludus ansible roles add -d roles/ludus_laps
ludus ansible roles add -d roles/ludus_ops
ludus ansible roles add -d roles/ludus_iis
ludus ansible roles add -d roles/ludus_gitlab_ldap
ludus ansible roles add -d roles/ludus_sssd
ludus ansible roles add -d roles/ludus_mssql_config
ludus ansible roles add -d roles/ludus_webapp
ludus ansible roles add -d roles/ludus_gitlab_runner
# Galaxy / GitHub roles
ludus ansible roles add badsectorlabs.ludus_adcs
ludus ansible roles add badsectorlabs.ludus_mssql
ludus ansible roles add badsectorlabs.ludus_gitlab_ce
ludus ansible roles add https://github.com/Cyblex-Consulting/ludus-local-users/archive/refs/heads/main.tar.gz
After any change to a local role, re-sync with --force to overwrite the cached version on the Ludus server:
ludus ansible roles add -d roles/<name> --force
Verify all roles are installed:
ludus ansible roles list
Patch badsectorlabs.ludus_mssql template
The win_template module in the Ludus Ansible environment does not evaluate Jinja2 block tags. After installing the role, patch the SQL Server 2019 config template to remove unrendered blocks and hardcode the instance name:
TMPL="/opt/ludus/users/ludus-admin/.ansible/roles/badsectorlabs.ludus_mssql/templates/sqlsrv_2019_config.ini.j2"
sudo sed -i '/^{%/d' "$TMPL"
sudo sed -i 's/INSTANCENAME="{{ ludus_mssql_instance_name }}"/INSTANCENAME="MSSQLSERVER"/' "$TMPL"
sudo sed -i 's/INSTANCEID="{{ ludus_mssql_instance_name }}"/INSTANCEID="MSSQLSERVER"/' "$TMPL"
sudo sed -i 's/UpdateEnabled="True"/UpdateEnabled="False"/' "$TMPL"
sudo sed -i 's/USEMICROSOFTUPDATE="True"/USEMICROSOFTUPDATE="False"/' "$TMPL"
5. Deploy the Range
Set the range configuration for your chosen profile:
# Elastic profile
ludus range config set -f ranges/elastic.yml
# Wazuh profile
ludus range config set -f ranges/wazuh.yml
# Splunk profile
ludus range config set -f ranges/splunk.yml
Verify the config was accepted without errors, then deploy:
ludus range deploy
Monitor deployment (takes 2–3 hours with the LAPS-ready template):
ludus range logs -f
Check final status:
ludus range status
All VMs should show BUILT and the deployment status should be SUCCESS.
6. Verify
Elastic profile
Run the Fleet status check to confirm all Elastic agents are enrolled:
bash tests/fleet_status.sh
All Windows VMs (DC01-2022, DC01-SEC, ADCS, WEB, WIN11-22H2-1, WIN11-22H2-2), the GitLab VM, and the ops VM should appear with status online.
Wazuh profile
Run the Wazuh agent status check:
bash tests/wazuh_status.sh
All 8 agents (DC01-2022, DC01-SEC, ADCS, WEB, WIN11-22H2-1, WIN11-22H2-2, gitlab, ops) should appear with status active.
Splunk profile
Check that all Universal Forwarders are connected via Splunk Web at http://10.2.50.1:8000:
- Settings → Forwarding and receiving → Forwarder management — all 8 forwarders should appear
- Search:
index=windows earliest=-15m— Windows Event Logs from all domain-joined VMs - Search:
index=sysmon earliest=-15m— Sysmon events from Windows VMs - Search:
index=linux earliest=-15m— syslog/auth.log from ops and gitlab
Common
Once deployed, the ops VM exposes:
- Guacamole (remote access):
http://10.2.50.2:8080/guacamole/— default credentialsguacadmin:guacadmin - Infection Monkey (BAS):
https://10.2.50.2:5000
To add a Kali attacker VM (optional):
bash scripts/add-kali.sh
Notes
- The ADCS VM requires
sysprep: trueto generate a unique machine SID — already set inelastic.ymlandwazuh.yml - DCs do not support local SAM accounts — local user provisioning only applies to member machines
- Windows LAPS schema extension runs on DC01-2022 — requires the domain to be fully provisioned first
- The
win2022-server-x64-laps-templateis used for both DCs. It includes all Windows updates applied at build time, so LAPS cmdlets are immediately available without runningwin_updatesduring deploy ludus ansible roles adddoes not overwrite an existing role — use--forceflag to update installed rolesbadsectorlabs.ludus_mssqlrequires a manual template patch after install (see step 4) —win_templatein the Ludus Ansible env does not evaluate Jinja2 block tags, leaving literal Jinja2 syntax in rendered configs which causessetup.exeto fail- The cached MSSQL ISO at
/opt/ludus/resources/iso/is SQL Server 2019;ludus_mssql_versioninelastic.ymlis set to"2019"accordingly