#
GOAD
This is my internal notes for getting GOAD - Game of Active Directory up and running on Proxmox.
#
GOAD install (Proxmox + Ludus)
#
Install essentials
apt update && apt install curl sudo tmux -y
curl -s https://ludus.cloud/install | bash
Server may reboot a few times and you'll lose SSH connectivity temporarily.
#
Check install status
ludus-install-status
At the end of the install you'll get an API key. Write it down! You'll need it for the next step.
#
Make an admin user
export LUDUS_API_KEY='ROOT.xxx'
ludus user add --name "Brian Johnson" --userid brian --admin --url https://127.0.0.1:8081
#
Get that user's Proxmox creds
export LUDUS_API_KEY='brian.xxx'
ludus user creds get
#
Download GOAD
sudo apt install python3.11-venv
export LUDUS_API_KEY='brian.123@xxxx'
git clone https://github.com/Orange-Cyberdefense/GOAD.git ~/GOAD
#
Install Ludus
git clone https://gitlab.com/badsectorlabs/ludus ~/ludus
cd ~/ludus/templates
#
Build VM templates - 4 at a time
Note: before you do, edit ~/GOAD/extensions/ws01/providers/ludus/config.yml and put win10-22h2-x64-enterprise-template as the extension instead of the Win10 template name that comes by default. Here's what the file will then look like:
- vm_name: "-GOAD-WS01"
hostname: "-WS01"
template: win10-22h2-x64-enterprise-template
vlan: 10
ip_last_octet: 31
ram_gb: 4
cpus: 2
windows:
sysprep: true
Now you can build the templates:
# Add the templates you need for a full GOAD load:
cd ~/ludus/templates
ludus templates add -d ubuntu-22.04-x64-server
ludus templates add -d win10-22h2-x64-enterprise
ludus templates add -d win2016-server-x64
ludus templates add -d win2019-server-x64
ludus templates build -p 4
# Check the template list
ludus templates list
# Example output of templates list
+------------------------------------+-------+
| TEMPLATE | BUILT |
+------------------------------------+-------+
| debian-11-x64-server-template | TRUE |
| debian-12-x64-server-template | TRUE |
| kali-x64-desktop-template | TRUE |
| win11-22h2-x64-enterprise-template | TRUE |
| win2022-server-x64-template | TRUE |
| ubuntu-22.04-x64-server-template | TRUE |
| win10-22h2-x64-enterprise-template | TRUE |
| win2016-server-x64-template | TRUE |
| win2019-server-x64-template | TRUE |
+------------------------------------+-------+
Note: normally you can run ludus templates status to check status of the template install, but this will fail if you're building multiple templates at once with the -p flag.
#
Install GOAD
cd ~/GOAD
./goad.sh -p ludus
# Check the install
check
# Set lab to GOAD/GOAD-Light/NHA/SCCM/etc
set_lab GOAD
# Set the first three octets of the range (optional)
set_ip_range 10.3.10
# Install
install
#
Install the GOAD extensions (optional)
install_extension elk
install_extension exchange
install_extension ws01
install_extension wazuh
#
Check range status
ludus range config get
ludus range status --user GOAD[plus numbers/digits that appear after the VMs in your lab
#
Snapshot VMs (optional)
See this guide, but in general you could snapshot the whole range with:
ludus snapshots create <snapshot-name>
#
Redeploy a range
You can follow these steps to redeploy a range:
#
Redeploy just a specific VM
...one that might be having license issues (where 180 count is not resetting, for example):
ludus range deploy --limit GOAD0ffddd-GOAD-SRV01 --user GOAD0ffddd-GOAD-SRV01
Watch logs:
ludus range logs -f --user GOAD0ffddd
More information about adding a single VM to a range here
#
Redeploy the WHOLE range
# Run GOAD shell and specify Ludus as provider
cd ~/GOAD
./goad.sh -p ludus
# List instances
list
# "Use" the one that needs redeployment
load xxx-goad-ludus (whatever the range name is)
# Kill it with fire (you'll be asked to confirm the destruction)!
destroy
# Keep tabs on its death
status
After "death" I found a problem (discussed here where even after rebuilding the range, the Windows VMs thought they only had a few days left of licensing (despite doing slmgr /rearm and rebooting).
So I trashed all the templates with:
ludus templates rm -n debian-11-x64-server-template
ludus templates rm -n debian-12-x64-server-template
ludus templates rm -n kali-x64-desktop-template
ludus templates rm -n win11-22h2-x64-enterprise-template
ludus templates rm -n win2022-server-x64-template
ludus templates rm -n ubuntu-22.04-x64-server-template
ludus templates rm -n win10-22h2-x64-enterprise-template
ludus templates rm -n win2016-server-x64-template
ludus templates rm -n win2019-server-x64-template
Then rebuild the templates:
# Readd the templates (sanity check)
cd ~/ludus/templates
ludus templates add -d ubuntu-22.04-x64-server
ludus templates add -d win10-22h2-x64-enterprise
ludus templates add -d win2016-server-x64
ludus templates add -d win2019-server-x64
# Build the templates
ludus templates build -p 4
# Check status on builds
ludus templates list
Before redeploying, I've found that the /root/.ssh/known_hosts file needs to have its previous GOAD hosts cleared out so that you don't get SSH key/security issues on next deployment. In those cases, I do this (assuming 10.3.10.x is my old subnet):
# Backup first just in case
cp /root/.ssh/known_hosts /root/.ssh/known_hosts.bak
# Remove all offending entries
for ip in $(seq 1 254); do
ssh-keygen -f "/root/.ssh/known_hosts" -R "10.3.10.$ip"
done
#
Troubleshooting
#
Elasticsearch reset
I had an issue where Elasticsearch dashboard was all jacked up with messages like:
“Failed to retrieve privileges”
“License is not available”
“Kibana server is not ready yet”
#
Reset Elasticsearch passwords
/usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive
#
Test the new password
curl -u elastic:NewPass2026!!! http://10.3.10.50:9200
#
Update Kibana config
In /etc/kibana/kibana.yml add:
elasticsearch.hosts: ["http://10.3.10.50:9200"]
elasticsearch.username: "elastic"
elasticsearch.password: "NewPass2026!!!"
#
Restart services
sudo systemctl restart elasticsearch
sudo systemctl restart kibana
#
Monitor logs
sudo journalctl -u kibana -f
#
Check that dashboard are actually working
Elasticsearch accessible at http://10.3.10.50:9200
Kibana working at http://10.3.10.50:5601
#
Installing/reinstalling just certain parts of a GOAD install
From the author:
Yes you can retry certain parts. Use `>provision_extension wazuh` to relaunch only wazuh provision once instance is selected.
#
Wazuh manual agent install
Sometimes when installing/reinstalling goad, one or more servers complain that the Wazuh installation fails (typically SRV01). So I'll jump into that machine's VNC console in Proxmox and do this:
invoke-webrequest https://packages.wazuh.com/4.x/windows/wazuh-agent-4.8.2-1.msi -outfile c:\tmp\wazuh-agent
Then I just run the GOAD install command again:
install
There was also one time I had to do the next step, which is complete the manual install:
msiexec.exe /i c:\tmp\wazuh-agent /q WAZUH_MANAGER=ip.of.wazuh.box WAZUH_REGISTRATION_SERVER=ip.of.wazuh.box
#
Wazuh server default password
Per this issue, if you need to get the various Wazuh passwords, login at the console (username: localuser, password: password) and then check the end of this file for the default Wazuh install password:
/opt/wazuh/wazuh-install-output.txt
#
Winlogbeat install
Sometimes Windows systems don't get the winlogbeat.zip properly so I manually visit them from VNC console and install via PowerShell:
invoke-webrequest https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-7.17.6-windows-x86_64.zip -outfile "c:\program files\elastic\winlogbeat\winlogbeat.zip"
#
Debian router config
#
iptables rules
I use a Hetzner server to setup two networks in my GOAD environment. One is configured with an extra external IP where the firewall terminates, and that LAN is 192.168.1.x. So in order to then have an Apache Guacamole server traverse from 192.168.1.x I like to back up the current iptables config and then modify a rule to allow traffic:
# Update and install iptables-persistent module
sudo apt update
sudo apt install iptables-persistent
# Backup current config
sudo iptables-save > 2025-xx-xx-iptables.original
# Punch in rules to allow 192.168.1.x to talk to the GOAD network of 10.3.10.x
sudo iptables -I LUDUS_DEFAULTS 2 -s 192.168.1.111/32 -d 10.3.0.0/16 -m comment --comment "192.168.1.111/32 IP to range forward rule" -j ACCEPT
sudo iptables -I LUDUS_DEFAULTS 3 -s 192.0.2.49/32 -d 10.3.0.0/16 -m comment --comment "opnsense to range forward rule" -j ACCEPT
sudo iptables -t nat -A POSTROUTING -s 10.3.10.0/24 ! -d 198.51.100.0/24 -o ens18 -j MASQUERADE
# Make rules persistent
sudo netfilter-persistent save
# Save the rules one more time
sudo iptables-save > 2025-xx-xx-iptables.golden
#
Block LAN to WAN traffic
I had a few instances where Hetzner was complaining about scanning activity from inside my GOAD network. I could never figure out if it was indeed coming from students, but had this rule ready to block all GOAD-to-Internet traffic with this iptables rule if necessary:
# Block 10.3.10.x from hitting the Internet
sudo iptables -I LUDUS_USER_RULES 1 -s 10.3.10.0/24 -j DROP
# Remove rule when the coast is clear
sudo iptables -D LUDUS_USER_RULES -s 10.3.10.0/24 -j DROP
#
Extend Windows licenses
You can extend them (I think) up to 3 times for a total of 540 days. When the countdown is running low on a Windows system, do this from cmd prompt:
slmgr /rearm
Check license expiration:
slmgr /xpr
More information about the technical bits behind license re-arm procedures, read this.
#
Install (AWS)
#
Create AWS IAM user and assign permissions
- In your AWS console, go under IAM > User and create a user (I called mine GOAD).
- In the Permissions policies area choose Add permissions > Add permissions then click Attach Policies Directly and choose AdministratorAccess from the list.
Danger
I get it - this is giving the GOAD user too much permission. But the AWS account I'm using is used for nothing but GOAD testing, so I don't care too much about this instance getting pwned.
#
Install aws cli
More info on the AWS docs
sudo apt update
sudo apt install zip -y
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
#
Configure aws cli
aws configure
When asked about region name, put eu-west3.
Once you go through the prompts, you'll end up with a ~/.aws/credentials file that looks something like this:
[default]
aws_access_key_id = xxx
aws_secret_access_key = xxx
Copy and paste this section again, but change the header name from [default] to [goad] so the file ends up looking like this:
[default]
aws_access_key_id = xxx
aws_secret_access_key = xxx
[goad]
aws_access_key_id = xxx
aws_secret_access_key = xxx
#
Install terraform
Up-to-date instructions here.
#
Install GOAD
sudo apt install python3.12-venv -y
git clone https://github.com/Orange-Cyberdefense/GOAD.git ~/goad
./goad.sh -p aws
A bunch of stuff will install and then you'll end with a screen like this:
_____ _____ _____
/ ____| / ||| \ /\ | __ \
| | __|| ||| | / \ | | | |
| | |_ || ||| |/ /\ \ | | | |
| |__| || ||| / /__\ \| |__| |
\_____| \_|||_/________\_____/
Game Of Active Directory
Pwning is coming
Goad management console type help or ? to list commands
[*] goad config file not found, create file /home/packie/.goad/goad.ini
[-] provisioner method local is not allowed for provider aws
[*] automatic changing provisioner method local to default for this provider : remote
[*] Start Loading default instance
[*] lab instances :
[-] No instance found, change your config and use install to create a lab instance
GOAD/aws/remote/192.168.56.X >
#
Deploy a range
From the GOAD/aws/remote/192.168.56.X > prompt, set the range to what you want, i.e.:
set_lab GOAD-LIGHT
install
# You'll be asked "Create lab with these settings?" Say yes/no.
Right before kicking off deployment, you'll get a screen like this. Take note of username/password information for future reference, and/or review the creds in ~/goad/ad/NAME-OF-GOAD-INSTALL/providers/NAME-OF-PROVIDER/windows.tf
Changes to Outputs:
+ ubuntu-jumpbox-ip = (known after apply)
+ ubuntu-jumpbox-username = "goad"
+ vm-config = {
+ dc01 = {
+ ami = "ami-03440f0d88fea1060"
+ domain = "sevenkingdoms.local"
+ instance_type = "t2.medium"
+ name = "dc01"
+ password = "xxx"
+ private_ip_address = "192.168.56.10"
+ windows_sku = "2019-Datacenter"
}
+ dc02 = {
+ ami = "ami-03440f0d88fea1060"
+ domain = "north.sevenkingdoms.local"
+ instance_type = "t2.medium"
+ name = "dc02"
+ password = "xxx"
+ private_ip_address = "192.168.56.11"
+ windows_sku = "2019-Datacenter"
}
+ srv02 = {
+ ami = "ami-03440f0d88fea1060"
+ domain = "north.sevenkingdoms.local"
+ instance_type = "t2.medium"
+ name = "srv02"
+ password = "xxx"
+ private_ip_address = "192.168.56.22"
+ windows_sku = "2019-Datacenter"
}
}
+ windows-vm-username = "goadmin"
#
Share the range with a third party (like Coursestack)
If you want to share these AMIs with a third party (like Coursestack), first shutdown the range:
At the GOAD-Light/aws/remote/192.168.56.X (be5faa-goad-light-aws) type stop and hit Enter. You can then type status a few times and wait until everything has a status of stopped.
Then in AWS console, right-click your instance and choose Image and templates > Create image. Give it a name and description. Leave the other options as a default (like Image description, the ticked Reboot instance box and Tag image and snapshots together box).
Once you do this, head to the AMI area of AWS console and you should see your new AMIs. Wait for them to move from status of Pending to Available. Then right click each AMI, choose Edit AMI permissions. At the next screen, click Add account ID and add your third party AWS account. Then click Save changes at the bottom of the screen.
Follow the same process under the Snapshots section (edit permissions and add the third party AWS account).
#
Troubleshooting
As of me going through this process on Jan 5, 2026, there's an open issue where deployment will fail with something like:
Error: creating EC2 Instance: InvalidParameterCombination: The specified instance type is not eligible for Free Tier. For a list of Free Tier instance types, run 'describe-instance-types' with the filter 'free-tier-eligible=true'.
│ status code: 400, request id: 7662731b-13dd-48c0-a8a1-19b787fb03c5
│
│ with aws_instance.goad-vm-jumpbox,
│ on jumpbox.tf line 15, in resource "aws_instance" "goad-vm-jumpbox":
│ 15: resource "aws_instance" "goad-vm-jumpbox" {
│
╵
╷
│ Error: creating EC2 Instance: AuthFailure: Not authorized for images: [ami-03440f0d88fea1060]
│ status code: 400, request id: 8d91b727-65ed-45d4-8db6-db67769f9b13
│
│ with aws_instance.goad-vm["dc02"],
│ on windows.tf line 59, in resource "aws_instance" "goad-vm":
│ 59: resource "aws_instance" "goad-vm" {
│
╵
╷
│ Error: creating EC2 Instance: AuthFailure: Not authorized for images: [ami-03440f0d88fea1060]
│ status code: 400, request id: dee92afa-b139-410b-a323-2fcfd219a317
│
│ with aws_instance.goad-vm["srv02"],
│ on windows.tf line 59, in resource "aws_instance" "goad-vm":
│ 59: resource "aws_instance" "goad-vm" {
│
╵
╷
│ Error: creating EC2 Instance: AuthFailure: Not authorized for images: [ami-03440f0d88fea1060]
│ status code: 400, request id: f9fd06be-53a4-4e20-9122-b7ba31558380
│
│ with aws_instance.goad-vm["dc01"],
│ on windows.tf line 59, in resource "aws_instance" "goad-vm":
│ 59: resource "aws_instance" "goad-vm" {
│
╵
[-] Providing error stop
The issue is that the AMI numbers are outdated:
aws ec2 describe-images \
--region eu-west-3 \
--owners "amazon" \
--filters "Name=name,Values=Windows_Server-2019-English-Full-Base*" \
--query 'Images[*].[ImageId,Name,CreationDate]' \
--output table
-----------------------------------------------------------------------------------------------------------
| DescribeImages |
+-----------------------+----------------------------------------------------+----------------------------+
| ami-02a45af02cb289e37| Windows_Server-2019-English-Full-Base-2025.10.15 | 2025-10-17T06:33:35.000Z |
| ami-0a4c1700182f3bc09| Windows_Server-2019-English-Full-Base-2025.11.12 | 2025-11-12T23:01:57.000Z |
| ami-0a92dbc1c111021cd| Windows_Server-2019-English-Full-Base-2025.09.10 | 2025-09-12T04:00:23.000Z |
+-----------------------+----------------------------------------------------+----------------------------+
In my case I'm using AWS as the provider and GOAD-LIGHT as the lab, so I did:
nano ~/goad/ad/GOAD-Light/providers/aws/windows.tf
Then I replaced every instance of ami-03440f0d88fea1060 with ami-0fc132be18e175019.
Then, back at the GOAD/aws/remote/192.168.56.X > prompt:
destroy
# Follow prompts/confirmations to nuke the existing half-baked install
# update the instance files to pick up on the changes to the windows.tf file
update_instance_files
# reinstall
install
After that if you still have a problem, and you're using a freshly-created AWS account, you may need to click into Billing and Cost Management header, and where it says Your free plan account does not get charged, click Upgrade plan and follow the prompts to enable a proper AWS paid account. Once I did that and destroyed the existing instance, ran update_instance_files and then the install command, everything ran like a champ!