Usage
Bring the lab up, verify it, and find your way around once it’s running.
Table of contents
- Bring up the full lab
- Credentials
- Common commands
- RDP access to Windows machines
- Optional: Kali attacker box
- Fleet and Elastic Defend
- Verifying the lab is healthy
- Provisioning logs
- Repository structure
- Customisation
- Notes
Bring up the full lab
Start the SIEM first — the Windows agents wait for the Fleet enrollment token (or, in Splunk mode, just retry the forwarder connection) before enrolling:
# 1. Start the SIEM
vagrant up siem
# 2. Wait until Kibana is available (check logs/credentials.txt for password)
# → http://localhost:5601
# 3. Start the Windows endpoints — they auto-enroll with Fleet
vagrant up winserver win11
Or all at once (Windows agents will retry until the SIEM is ready, up to 10 min):
vagrant up
First run downloads the boxes (~5 GB Debian, ~8–15 GB each Windows box).
For Splunk or Wazuh instead of ELK, set ENABLE_SPLUNK=true or ENABLE_WAZUH=true on every vagrant up/vagrant provision call — see Splunk / Wazuh. Mutually exclusive with each other and with the ELK default.
Add ENABLE_VELOCIRAPTOR=true for live triage/hunting alongside whichever SIEM stack you picked — not mutually exclusive with anything above, see Velociraptor.
Installer wrapper
install.sh (Linux/macOS) and install.ps1 (Windows) wrap vagrant up with the right env vars, so you don’t have to remember the flag names:
./install.sh # ELK (default), no extras
./install.sh --siem splunk --guacamole # Splunk + Guacamole
./install.sh --siem wazuh # Wazuh
./install.sh --debug # verbose Vagrant/provisioner output when something fails
./install.sh --destroy # vagrant destroy -f (all VMs)
./install.sh --destroy -- win11 # vagrant destroy -f win11 only
.\install.ps1 # ELK (default), no extras
.\install.ps1 -Siem splunk -Guacamole # Splunk + Guacamole
.\install.ps1 -Siem wazuh # Wazuh
.\install.ps1 -Debug # verbose Vagrant/provisioner output when something fails
.\install.ps1 -Destroy # vagrant destroy -f (all VMs)
.\install.ps1 -Destroy win11 # vagrant destroy -f win11 only
--debug/-Debug sets VAGRANT_LOG=debug, Vagrant’s own verbose mode — useful when a provisioner fails and the logs/*.log file it wrote isn’t enough to see why (e.g. it died before writing anything, or the failure is in Vagrant itself, not the script). It also captures the whole session — this wrapper’s own output plus Vagrant’s — to logs/debug.log, overwritten fresh on every --debug run, so it’s ready to paste/share after a failure without having to re-run and copy the terminal by hand.
Both are thin wrappers — equivalent to setting the ENABLE_* env vars yourself and running vagrant up directly, shown throughout this page.
Lifecycle commands (already-created environment)
--start/--stop/--suspend/--resume/--reload (-Start/-Stop/ -Suspend/-Resume/-Reload on Windows) act on every VM currently defined in the Vagrantfile, in a fixed order — not on individual VMs, and independent of --siem/--guacamole:
- Stop order (
--stop/--suspend):win11→winserver→siem— workstation first, domain controller second, SIEM/log backend last. - Start order (
--start/--resume/--reload): the reverse —siemfirst, so it’s ready before the Windows endpoints come up. kali(if the environment was created withENABLE_KALI=true) goes last on start/resume/reload, first on stop/suspend — it has no dependents.
./install.sh --stop # halt win11, then winserver, then siem
./install.sh --start # up siem, then winserver, then win11
./install.sh --suspend
./install.sh --resume
./install.sh --reload # reload siem, then winserver, then win11
./install.sh --status # vagrant status
--reload runs vagrant reload per VM (restart + re-run provisioners) — use it to re-apply a provisioner fix without a full --destroy/re-create.
If one VM in the sequence fails (or isn’t created), the wrapper warns and continues with the rest rather than aborting the whole sequence.
Credentials
After SIEM provisioning, credentials are written to logs/credentials.txt (ELK) or logs/splunk-credentials.txt (Splunk):
Username : elastic
Password : <auto-generated>
Kibana : http://localhost:5601
Fleet : http://localhost:18220
Common commands
vagrant status # Status of all VMs
vagrant ssh siem # SSH into the SIEM
vagrant winrm winserver -c '…' # Run a command on a Windows VM
vagrant halt # Shut down all VMs
vagrant destroy -f # Destroy and start fresh
vagrant provision siem # Re-run SIEM provisioning only
Provisioners are named, so individual steps can be re-run without redoing earlier ones:
vagrant provision winserver --provision-with ad-domain # AD DS forest promotion only
vagrant provision win11 --provision-with domain-join # Domain join only
ad-domain and domain-join reboot the VM (Vagrant’s reboot: true handles the wait). Set VAGRANT_MAX_REBOOT_RETRY_DURATION (seconds) generously — the default (120s) is too short for AD DS promotion:
VAGRANT_MAX_REBOOT_RETRY_DURATION=900 vagrant provision winserver --provision-with ad-domain
After AD DS promotion, winserver’s WinRM login is Administrator (not the local vagrant account, which stops being valid once the box becomes a Domain Controller — see Notes).
RDP access to Windows machines
| VM | Host port (VirtualBox) | Username | Password |
|---|---|---|---|
| Windows Server 2022 | localhost:13389 | vagrant | vagrant |
| Windows 11 | localhost:23389 | vagrant | vagrant |
xfreerdp /v:localhost:13389 /u:vagrant /p:vagrant /dynamic-resolution
Optional: Kali attacker box
A Kali Linux VM (kalilinux/rolling, 192.168.56.100, 4 GB RAM, headless) for driving traffic/attacks against winserver/win11 to generate detections. Off by default — bring your own attack tooling; the only thing provisioned for you is xrdp (scripts/kali-xrdp-setup.sh), for graphical access to the desktop it already ships with.
ENABLE_KALI=true vagrant up kali
./install.sh --kali -- kali # same thing, via the wrapper
kalilinux/rolling is a full desktop image (~10 GB after first boot) — no smaller official Kali box exists, and no strip-down runs on it.
Access:
vagrant ssh kali
ssh -p 32200 vagrant@localhost # equivalent, straight from the host
For graphical tools, RDP into the existing XFCE desktop instead of opening the VirtualBox GUI window — FreeRDP-based clients (xfreerdp /dynamic-resolution, Remmina, …) negotiate dynamic resize properly, which VirtualBox’s own guest-display integration doesn’t do reliably headless:
xfreerdp /v:192.168.56.100 /u:vagrant /p:vagrant /dynamic-resolution
# or localhost:33389 from the host, same as vagrant ssh vs. -p 32200 above
xfce4-session allows only one session per user. If you open the VirtualBox GUI window for kali (logging vagrant into the console), RDP sessions as vagrant will fail to start a desktop until that console session is closed. Use one access method at a time.
Reach winserver/win11 from inside kali over the private network (192.168.56.20 / .30), not the other way around.
Fleet and Elastic Defend
After provisioning (ELK mode), both Windows VMs appear as enrolled agents in Fleet → Agents.
Adding Elastic Defend (Endpoint Protection)
- Go to Fleet → Agent Policies → Windows Endpoints
- Click Add integration
- Search for Elastic Defend
- Choose preset: EDR Complete
- Set protection mode:
- Detect (monitor only) — logs threats, no blocking; use this to see detections without interference
- Prevent (block) — actively blocks threats; use this to verify blocking behavior
Switching protection modes
While testing detections → Set Elastic Defend to Detect mode
After observing detections → Switch to Prevent mode to verify blocking
Changes apply to all enrolled agents within seconds via Fleet policy push.
Verifying the lab is healthy
bash tests/check-lab.sh # SIEM health (ELK or, with ENABLE_SPLUNK=true, delegates to check-splunk.sh) + agent enrollment
bash tests/check-domain.sh # minilab.local AD domain health + win11 domain membership
bash tests/check-telemetry.sh # ELK only - is telemetry actually arriving, not just "is the collector up"
tests/check-lab.ps1 is the PowerShell equivalent of check-lab.sh, for running the same checks from a Windows host.
check-domain.sh verifies the domain using only nmap and native LDAP/Samba client tools (ldapsearch, nmblookup, smbclient) — see Installation to install them.
check-telemetry.sh queries Elasticsearch directly for recent document counts in each data stream from the telemetry work documented in TELEMETRY.md (Sysmon, PowerShell, Defender, DNS, Firewall, Security) — check-lab.sh only confirms the collector/agent side is up, not that any given source is actually producing events.
To generate that activity on demand instead of waiting for it to happen naturally, scripts/generate-telemetry.ps1 (not a provisioner — run it manually) fires 12 benign, self-cleaning actions (logon success/failure, process creation, PowerShell execution, DNS query, network connection, file/registry/service/scheduled-task/account/group changes), each printing the specific event it expects:
vagrant winrm winserver -c 'C:\vagrant\scripts\generate-telemetry.ps1'
vagrant winrm win11 -c 'C:\vagrant\scripts\generate-telemetry.ps1'
Provisioning logs
Each provisioning script writes a timestamped log to logs/ (Vagrant synced folder). After vagrant up check:
logs/
├── credentials.txt # Kibana/ES username and password (ELK mode)
├── splunk-credentials.txt # Splunk admin username and password (ENABLE_SPLUNK=true)
├── elastic-version.txt # Installed ELK version
├── fleet-enrollment-token.txt # Token used by Windows agents to enroll (ELK mode)
├── fleet-policy-id.txt # ID of the 'Windows Endpoints' policy (ELK mode)
├── elk-provision.log # Full ELK provisioning output
├── splunk-provision.log # Full Splunk provisioning output (ENABLE_SPLUNK=true)
├── winserver-baseline.log # Sysmon + Defender/Firewall on WinServer
├── winserver-elastic-agent.log # Elastic Agent enrollment on WinServer (ELK mode)
├── winserver-splunk-forwarder.log # Splunk UF install on WinServer (ENABLE_SPLUNK=true)
├── win11-baseline.log # Sysmon + Defender/Firewall on Win11
├── win11-elastic-agent.log # Elastic Agent enrollment on Win11 (ELK mode)
└── win11-splunk-forwarder.log # Splunk UF install on Win11 (ENABLE_SPLUNK=true)
Each log uses [OK] / [ERR] markers and includes connectivity checks.
Repository structure
MiniLab/
├── Vagrantfile
├── install.sh # Installer wrapper (Linux/macOS)
├── install.ps1 # Installer wrapper (Windows)
├── logo/
│ └── logotipo.png
├── splunk/
│ ├── Splunk.License # License (gitignored, optional - see Splunk docs)
│ └── downloads.list # wget commands for the Splunk packages (gitignored)
├── scripts/
│ ├── elk-provision.sh # ELK 8.x + Fleet Server on siem (default)
│ ├── splunk-provision.sh # Splunk Enterprise on siem (ENABLE_SPLUNK=true)
│ ├── wazuh-provision.sh # Wazuh all-in-one on siem (ENABLE_WAZUH=true)
│ ├── winserver-baseline.ps1 # Sysmon + Defender/Firewall on WinServer
│ ├── winserver-audit-policy.ps1 # Category-level Security auditing on WinServer
│ ├── winserver-defender-telemetry.ps1 # Defender/Operational channel on WinServer
│ ├── winserver-dns-telemetry.ps1 # DNS-Server/Analytical + DNS-Client/Operational on WinServer
│ ├── winserver-powershell-transcription.ps1 # PowerShell Transcription on WinServer
│ ├── winserver-firewall-logging.ps1 # pfirewall.log on WinServer
│ ├── winserver-elastic-agent.ps1 # Elastic Agent enrollment on WinServer (default)
│ ├── winserver-splunk-forwarder.ps1 # Splunk UF on WinServer (ENABLE_SPLUNK=true)
│ ├── winserver-wazuh-agent.ps1 # Wazuh agent on WinServer (ENABLE_WAZUH=true)
│ ├── win11-baseline.ps1 # Sysmon + Defender/Firewall on Win11
│ ├── win11-audit-policy.ps1 # Category-level Security auditing on Win11
│ ├── win11-defender-telemetry.ps1 # Defender/Operational channel on Win11
│ ├── win11-dns-telemetry.ps1 # DNS-Client/Operational on Win11
│ ├── win11-powershell-transcription.ps1 # PowerShell Transcription on Win11
│ ├── win11-firewall-logging.ps1 # pfirewall.log on Win11
│ ├── generate-telemetry.ps1 # Manual, self-cleaning telemetry generator (not a provisioner)
│ ├── win11-elastic-agent.ps1 # Elastic Agent enrollment on Win11 (default)
│ ├── win11-splunk-forwarder.ps1 # Splunk UF on Win11 (ENABLE_SPLUNK=true)
│ ├── win11-wazuh-agent.ps1 # Wazuh agent on Win11 (ENABLE_WAZUH=true)
│ ├── ad-domain-setup.ps1 # Promotes winserver to a minilab.local DC
│ ├── domain-join.ps1 # Joins win11 to minilab.local
│ ├── docker-setup.sh # Docker Engine + Compose on siem (ENABLE_GUACAMOLE=true)
│ ├── guacamole-setup.sh # Deploys guacd + Guacamole webapp on siem (ENABLE_GUACAMOLE=true)
│ ├── velociraptor-provision.sh # Velociraptor server on siem (ENABLE_VELOCIRAPTOR=true)
│ ├── winserver-velociraptor-agent.ps1 # Velociraptor client on WinServer (ENABLE_VELOCIRAPTOR=true)
│ ├── win11-velociraptor-agent.ps1 # Velociraptor client on Win11 (ENABLE_VELOCIRAPTOR=true)
│ ├── kali-xrdp-setup.sh # xrdp for graphical access on Kali (ENABLE_KALI=true)
├── docs/ # This site (GitHub Pages, served from /docs)
└── tests/
├── check-lab.sh # SIEM health check (bash) - ELK, or delegates to check-splunk.sh/check-wazuh.sh
├── check-telemetry.sh # ELK-only: is telemetry actually arriving per source, not just "collector up"
├── check-lab.ps1 # SIEM health check (PowerShell) - ELK only for now
├── check-splunk.sh # Splunk-mode health check (ENABLE_SPLUNK=true)
├── check-wazuh.sh # Wazuh-mode health check (ENABLE_WAZUH=true)
├── check-domain.sh # AD domain health check (nmap + LDAP/Samba tools)
└── check-guacamole.sh # Guacamole health check (ENABLE_GUACAMOLE=true)
Customisation
Sysmon config
Both Windows baseline scripts download the olafhartong/sysmon-modular merged config. To change it, edit the $SysmonCfgUrl variable in scripts/winserver-baseline.ps1 / scripts/win11-baseline.ps1.
Adjust SIEM VM memory
In Vagrantfile, change vb.memory. Also update the Elasticsearch heap in elk-provision.sh:
# Rule: heap = 50% of VM RAM, 31 GB maximum
-Xms3g
-Xmx3g
Notes
- Elasticsearch uses HTTP (no TLS) with authentication (
elasticuser). Do not expose these VMs to external networks. - Fleet Server runs on HTTP port 8220 (bound to all interfaces) with
--fleet-server-insecure-http— appropriate for an isolated lab. - Logstash is installed and available on port 5044 for ingesting non-Agent sources, but Elastic Agent sends directly to Elasticsearch via Fleet.
- The auto-generated
elasticpassword is stored inlogs/credentials.txt(gitignored). - Both baseline scripts enable PowerShell Script Block + Module logging (events 4103/4104,
Microsoft-Windows-PowerShell/Operational) — needed to see offensive PowerShell TTPs (encoded/obfuscated/in-memory execution) beyond the literal command line Sysmon captures. ELK picks this up via the default Fleetwindowsintegration; Splunk/Wazuh get an explicitinputs.conf/ossec.confstanza from their respective agent scripts, since neither ships it by default. - Sysmon reaches all 3 SIEMs the same way: enabled by default for ELK (Fleet
windowsintegration), explicitinputs.conf/ossec.confstanza for Splunk/Wazuh (both were previously silently dropping it — seeTELEMETRY.md). - Windows Defender Operational telemetry also reaches all 3 SIEMs (
winserver/win11-defender-telemetry.ps1+ a matchinginputs.conf/ossec.confstanza) — this one needed ELK-side work too, since the Fleetwindowsintegration ships that data stream disabled by default. - DNS telemetry (
DNS-Server/Analyticalonwinserveronly,DNS-Client/Operationalon both) reaches all 3 SIEMs too. ELK needed the most novel fix here: no bundled integration ships a DNS data stream at all, soelk-provision.shadds two package policies from Elastic’swinloginput package (“Custom Windows Event Logs”) instead of the usual “windows” integration shortcut. winserver/win11-audit-policy.ps1enable category-level Security auditing (Account Logon, Account Management, Detailed Tracking, DS Access, Logon/Logoff, Object Access, Policy Change, Privilege Use) plus command-line arguments on 4688 — none of this needed SIEM-side changes, it’s all the Security channel.- PowerShell Transcription (
winserver/win11-powershell-transcription.ps1,C:\PSTranscripts) is collected by all 3 SIEMs too, but as files, not an event channel — ELK via Elastic’slog(Custom Logs, Deprecated but the only file-input package compatible with this lab’s pinned Kibana 8.x), Splunk viamonitor://, Wazuh via<localfile log_format="syslog">. - Windows Firewall logging (
winserver/win11-firewall-logging.ps1, defaultpfirewall.logpath, 32767 KB cap) reaches all 3 SIEMs the same file-based way as PowerShell Transcription — this was the most expensive of the telemetry fixes, since none of the 3 SIEMs have a built-in shortcut for it. winserveris theminilab.localActive Directory Domain Controller. Promoting a Windows box to a DC replaces its local SAM with the domain database, so the localvagrantaccount stops being a valid login afterward —winserver’s Vagrant WinRM identity isAdministratorinstead (samevagrantpassword; it becomes the domain Administrator/Domain Admin automatically).win11keeps its own localvagrantaccount unaffected, since domain-joining a member computer doesn’t touch its local SAM.- The DSRM (Directory Services Restore Mode) recovery password is
V4grant!2026— separate from day-to-day credentials, only needed to boot a DC into recovery mode, just in case you broke something while playing. ;)
→ Splunk · Guacamole · Velociraptor