This guide gets you running Attack Range using Docker Compose (recommended), then explains the web app, API, and CLI.
- Docker and Docker Compose installed.
- A cloud account and CLI configured for one of: AWS, Azure, or GCP.
- Credentials on the host so the containers can use them:
- AWS:
~/.aws(e.g.aws configure) - Azure:
~/.azure(e.g.az login) - GCP:
~/.config/gcloud(e.g.gcloud auth login)
- AWS:
Docker Compose runs the API, web app, and (optionally) the CLI without installing Python, Ansible, or Terraform on your machine.
-
Start the stack:
git clone https://github.com/splunk/attack_range.git cd attack_range docker compose -f docker/docker-compose.yml build --no-cache docker compose -f docker/docker-compose.yml upThis starts:
- The API on port 4000
- The Web app on port 4321
-
Open the web app: http://localhost:4321
-
Build your first range:
- Choose a template (e.g. aws/splunk_minimal_aws).
- Click build. The backend runs Phase 1 (VPN infrastructure).
- When status changes to Waiting for VPN, download/copy the WireGuard client config.
- Connect to the VPN using WireGuard (Desktop or mobile). If you are using WSL2 make sure to install WireGuard on the host machine not the WSL instance.
- Click Continue build to run Phase 2 (lab: Splunk, Windows, etc.).
-
Optional — CLI in Docker:
docker compose --profile cli -f docker/docker-compose.yml run --rm attack_range build -t aws/splunk_minimal_aws
The CLI will prompt you to connect to the VPN, then continue the lab build. Other commands:
destroy,simulate,share(see Configuration and CLI sections below).
- Build: Select a template and start build; when status is Waiting for VPN, download the WireGuard config, connect, then continue.
- List / status: See all ranges and their status (building, running, error).
- Destroy: Tear down a range by its Attack Range ID.
- Simulate: Run Atomic Red Team techniques against a server in a running range (attack range ID, target server, technique list).
- Share: Generate a new WireGuard config for a named user; config is saved and shown for sharing.
The app talks to the API at http://localhost:4000 (or PUBLIC_API_URL in Docker).
The REST API runs on port 4000 and provides:
- Health:
GET /health - Build (two-phase):
POST /attack-range/buildwithtemplate(phase 1) orattack_range_id(phase 2 after VPN). - Status:
GET /attack-range/status/<attack_range_id> - List:
GET /attack-range/list - Destroy:
POST /attack-range/destroywithattack_range_id - Simulate:
POST /attack-range/simulatewithattack_range_id,target,techniques - Share:
POST /attack-range/sharewithattack_range_id,name - Templates:
GET /templates,GET /templates/<provider>/<name> - Configs:
GET /configs,GET /configs/<config_id> - Providers:
GET /providers/check(CLI/credentials availability)
Interactive API docs:
- Swagger UI: http://localhost:4000/openapi/swagger
- OpenAPI JSON: http://localhost:4000/openapi/openapi.json
All build/destroy operations are asynchronous: you get an attack_range_id and poll the status endpoint until the range is running or error.
You can run the CLI:
- Inside Docker (recommended):
docker compose --profile cli -f docker/docker-compose.yml run --rm attack_range <action> [options] - Locally: from the project root,
python attack_range.py <action> [options](requires Python, Ansible, Terraform, and cloud CLI on the host).
-
build — Build from a template (two-phase: VPN then lab; CLI prompts for VPN connection).
python attack_range.py build -t aws/splunk_minimal_aws- Template can be
provider/nameorname(searched intemplates/).
-
destroy — Tear down a range.
python attack_range.py destroy- With a single config in
config/, that config is used. Otherwise:python attack_range.py destroy -c <config_path_or_id>.
-
simulate — Run Atomic Red Team techniques on a target server.
python attack_range.py simulate -t <target_server_name> -te T1003.001,T1059.003 [-c config]
-
share — Generate a new WireGuard client config for sharing.
python attack_range.py share -n alice [-c config]
Config can be a path or an attack range ID (e.g. uuid.yml in config/). If omitted and there is exactly one *.yml in config/, it is used.
Attack Range uses a two-phase build so the lab is only reachable over VPN:
- Phase 1 (VPN): Terraform creates the network and a router; Ansible configures WireGuard on the router and generates a client config. Status becomes
wait_for_vpn. - You: Download or copy the WireGuard config, connect with the WireGuard client.
- Phase 2 (Lab): You trigger the continuation (in the app or with a second API call or by answering the CLI prompt). Ansible provisions Splunk, Windows, Kali, etc. over the VPN. Status becomes
running.
This keeps lab IPs (e.g. 10.0.2.x) off the public internet.
- Configuration — Config file layout, providers, and options.
- Networking — WireGuard, VPN, and connectivity.
- Sharing — Sharing access with others.
- Templates — Available templates and structure.
- Ansible Roles — Roles used for install and simulation.