terraform.tfvars configured, you are ready to deploy. This page covers the four Terraform commands, what to expect at each step, and what happens on each instance during the 5-10 minute post-deploy initialization window.
Terraform commands reference
| Command | What it does |
|---|---|
terraform init | Downloads provider plugins and initializes the working directory. Run once before anything else, or after adding new providers. |
terraform plan | Dry run. Shows exactly what Terraform will create, change, or destroy. No changes are made. |
terraform apply | Provisions the infrastructure defined in your .tf files. Prints the plan and prompts you to type yes before making any changes. |
terraform destroy | Tears down all infrastructure managed by Terraform. Prompts for confirmation. Run this when you are done with the lab. |
Deployment steps
Initialize Terraform
Download the AWS and random provider plugins and initialize the working directory:Expected output:If you see errors about missing providers or version constraints, ensure you are running Terraform 1.0 or higher (
terraform --version) and that you have internet access from the machine running the command.Review the deployment plan
Run a dry-run to see every resource Terraform will create before committing:Review the plan output. You should see:
- 50+ resources to be created
- 6 EC2 instances: Mythic, Sliver, Havoc, Guacamole, Windows operator workstation, Apache redirector
- 2 VPCs: team server VPC (
10.50.0.0/16) and redirector VPC (10.60.0.0/16) - 2 Elastic IPs: one for Guacamole (public access portal), one for the redirector (C2 entry point)
- Mythic, Sliver, and Havoc have no public IPs — they are internal only, reachable only through the redirector via VPC peering
- Security groups, VPC peering connection, route tables, and network interfaces
- 0 errors or warnings
If
terraform plan reports errors, the most common causes are: missing required variables in terraform.tfvars, the rs-rsa-key key pair not existing in AWS, or insufficient IAM permissions. Fix the error before proceeding.Deploy infrastructure
Apply the plan and provision all AWS resources:Terraform prints the full plan and then prompts:Type Credentials, IPs, and SSH commands are printed automatically after apply completes. You can retrieve them again at any time with
yes and press Enter. Terraform begins creating resources in dependency order.Deployment takes approximately 10 minutes from yes to completion. Expected final output:terraform output deployment_info.Wait for user data scripts
Terraform completing does not mean the lab is ready. Every instance runs a user data script that installs and configures its software stack. Wait 5-10 minutes after
terraform apply completes before attempting to connect.What each instance installs during user data:Mythic C2 server (mythic) — Debian 12
Mythic C2 server (mythic) — Debian 12
- Sets a descriptive hostname and populates
/etc/hostswith all lab machine IPs and hostnames - Installs Docker Engine
- Clones the Mythic repository to
/opt/Mythic - Installs the Apollo agent and HTTP C2 profile
- Starts approximately 10 Mythic Docker containers
- Configures Mythic as a systemd service for auto-start on reboot (when
enable_mythic_autostart = true)
Sliver C2 server (sliver) — Debian 12
Sliver C2 server (sliver) — Debian 12
- Sets hostname and
/etc/hostsentries - Downloads and installs the Sliver C2 server binary
- Configures the Sliver daemon as a systemd service
- Generates a pre-configured C2 profile with the correct
X-Request-IDtoken at/home/admin/redstack-c2-profile.json - Configures firewall rules
Havoc C2 server (havoc) — Debian 12
Havoc C2 server (havoc) — Debian 12
- Sets hostname and
/etc/hostsentries - Installs Go (required to build Havoc from source)
- Clones the Havoc repository and builds the teamserver binary
- Installs an XFCE4 desktop environment and VNC server for GUI access through Guacamole
- Configures the Havoc teamserver
Guacamole access portal (guac) — Debian 12
Guacamole access portal (guac) — Debian 12
- Sets hostname and
/etc/hostsentries - Installs Docker Engine
- Deploys PostgreSQL, Guacamole daemon (
guacd), and the Guacamole web application as Docker containers - Installs and configures Nginx as a reverse proxy for the Guacamole web UI
- Pre-configures 7 Guacamole connections: Windows RDP, Mythic SSH, Guacamole SSH, Redirector SSH, Sliver SSH, Havoc SSH, and Havoc VNC desktop
- Decrypts and stores the Windows Administrator password so RDP auto-connects without a password prompt
- Configures WireGuard (when
enable_external_vpn = true) for internal VPN routing
Windows operator workstation (WIN-OPERATOR) — Windows Server 2022
Windows operator workstation (WIN-OPERATOR) — Windows Server 2022
- Disables Windows Defender real-time protection and Windows Firewall
- Enables and configures RDP
- Populates
C:\Windows\System32\drivers\etc\hostswith lab machine hostnames - Installs Chromium, VS Code, MobaXterm, and 7-Zip via PowerShell
- Pre-configures MobaXterm SSH sessions for all lab machines in a
redStack Sessionsfolder (Mythic C2 (SSH),Sliver C2 (SSH),Havoc C2 (SSH),Apache Redirector (SSH),Guacamole Server (SSH))
Apache redirector (redirector) — Debian 12
Apache redirector (redirector) — Debian 12
- Sets hostname and
/etc/hostsentries - Installs Apache2 with
mod_rewrite,mod_proxy,mod_proxy_http,mod_headers, andmod_ssl - Configures HTTP and HTTPS VirtualHosts with URI prefix routing for Mythic, Sliver, and Havoc
- Configures header validation (
X-Request-ID) — requests without the correct token receive a decoy CloudEdge CDN maintenance page - Downloads
redirect.rulesfrom the redRules repository (AV/scanner blocklist) whenenable_redirector_htaccess_filtering = true - Generates a self-signed certificate with the redirector’s public IP as the Subject Alternative Name (used until Certbot replaces it)
- Installs a connectivity test script at
/home/admin/test_redirector.sh - Configures OpenVPN client and WireGuard server (when
enable_external_vpn = true)
Review deployment outputs
After the post-deploy wait, retrieve all IPs, credentials, and SSH commands:This prints a formatted block for each instance containing:
- Public and private IP addresses
- SSH commands (external via Elastic IP, internal via private IP)
- Usernames and auto-generated passwords
- Guacamole URL and admin credentials
- C2 header name and token value
- URI routing table (which prefix routes to which backend)
Point your domain to the redirector (open environments only)
Closed environment (HTB/VL/PG): Skip this step entirely. No domain or DNS record is needed. Proceed to verification.
| Type | Host | Value | TTL |
|---|---|---|---|
| A | @ | <Redirector Elastic IP> | Automatic |
| A | www | <Redirector Elastic IP> | Automatic |
| A | <subdomain> | <Redirector Elastic IP> | Automatic |
@ record is required. Add www or custom subdomains if you want agents to call back over those hosts. Custom subdomains (e.g., cdn.yourdomain.tld, chat.yourdomain.tld) blend beacon traffic into patterns that look like legitimate service traffic.Verify DNS propagation after creating the records:- Linux / macOS
- Windows (PowerShell)
AWS EC2 Dashboard
The AWS EC2 Dashboard is your primary visibility tool for what Terraform has built. Use it to verify deployments and confirm clean teardowns afterterraform destroy.
| Section | Location | What to check |
|---|---|---|
| Instances | EC2 → Instances → Instances | All 6 redStack instances should show running after apply. After terraform destroy, all should show terminated. |
| Elastic IPs | EC2 → Network & Security → Elastic IPs | Two EIPs allocated at deploy time — one for Guacamole, one for the redirector. After terraform destroy, both should be released. Unreleased EIPs incur charges even with no instances attached. |
| Key Pairs | EC2 → Network & Security → Key Pairs | Confirm rs-rsa-key exists. Terraform does not create this — if it is missing, terraform apply will fail. |
| VPCs | VPC → Your VPCs | Two VPCs are created: team server VPC (10.50.0.0/16) and redirector VPC (10.60.0.0/16). After terraform destroy, both should be removed. |
