# 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

  1. In your AWS console, go under IAM > User and create a user (I called mine GOAD).
  2. In the Permissions policies area choose Add permissions > Add permissions then click Attach Policies Directly and choose AdministratorAccess from the list.

# 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!