Repository navigation
Providers
Node kinds declare what they need (compute, a public address, a Windows or Linux
image, outbound routing). Providers declare what they can do. When a topology asks
for something the target cannot deliver, the canvas raises an error at design time
(CAP001, CAP002, CAP004), which is what keeps one provider from forking into
many.
| Provider | Target ranges | Attack infrastructure |
|---|---|---|
| GCP | Tested | Tested |
| AWS | Tested | Tested |
| Azure | Tested | Tested |
| Proxmox | Roadmap | Roadmap |
| ESXi | Roadmap | Roadmap |
GCP, AWS, and Azure are tested end to end for both target ranges and attack infrastructure. Proxmox and ESXi are on the roadmap, not yet supported.
What has been deployed and validated on Azure:
-
Attack infrastructure:
minimalc2-cli, and the full 8-VMredstack(Mythic, Sliver, and Adaptix teamservers, a collector, a Windows operator, and a Kali operator). Both passedverify.py doorsandverify.py stack, with a Let's Encrypt certificate on the redirector. -
Target ranges:
minilab(domain controller promotion over WinRM),goad-light, andgoad-wazuh(child domain promotion, the parent and child trust, MSSQL, and the Wazuh manager and agents). - Full GOAD and dracarys: both deployed clean and validated. Full GOAD: both forests and the cross-forest trust, ADCS on ESSOS-CA, and MSSQL. dracarys: the KeePass vault and a Linux host joined to the domain.
- The beacon path: from an Azure offense range, a Sliver beacon launched as patient zero in full GOAD called back through the redirector, and the GOAD playbook checks for recon, AS-REP roasting, Kerberoasting, ADCS enumeration, and MSSQL impersonation ran through it.
Notes that differ from GCP and AWS:
-
One resource group per range.
resource_groupindeploy.tfvarsnames it (default{prefix}-{topology name}-rg), and the default location iseastus2. The preflight stops the deploy if that group already exists and this range's Terraform state does not own it. -
Sizes and quotas are checked before apply. Subscriptions differ in which
VM sizes they may use and how many vCPUs each family gets, so
deploy.shruns a read-only preflight and stops before anything is created when the topology will not fit. See Azure sizes and quotas and The Azure preflight in Cloud Prerequisites. - Kali. The Kali operator starts from a Debian 13 image and the operator role converts it to Kali, so there is no Marketplace plan to accept, unlike AWS.
- .NET on Windows Server 2019. Azure's Windows Server 2019 image ships .NET Framework 4.7.2. Where chocolatey needs 4.8 (MSSQL, the KeePass vault), the roles install it.
-
Auto-stop is a Microsoft.DevTestLab per-VM shutdown schedule, which
needs that resource provider registered. It takes a Windows timezone id, not
an IANA name: the compiler translates common IANA zones (for example
America/New_YorktoEastern Standard Time) and refuses one it has no mapping for. UseUTC, a mapped zone, or a Windows id such as"GMT Standard Time". -
Stop deallocates.
manage.sh stopdeallocates the VMs, which halts compute billing, and the jumpbox keeps its public IP.manage.shstatus,start,stop, andteardown --forceneed the Azure CLI installed and logged in;teardown --forcedeletes the whole resource group. -
No poisoning or relay path. Like GCP and AWS, Azure's software-defined
network drops L2 broadcast and multicast, which name-resolution poisoning and
the relay path need, so
llmnr_poisoning,nbtns_poisoning,responder,ntlm_relay, andldap_signing_offare not planted on any of the three: the canvas greys them out and the compiler skips them. -
Windows client licensing. Windows 10 and 11 hosts boot Microsoft's client
images, which Microsoft licenses on Azure only for customers with a
qualifying license. redStackPRO does not set
license_type, so eligibility is yours to confirm. See Windows client licensing on Azure in Cloud Prerequisites.
Name resolution is the same on Azure as on GCP and AWS: the export creates no managed private DNS zone on any provider, so do not rely on resolving range hosts by name from your operator box. Use static hosts entries, which the solution guides set up.
A new workstation (wks) defaults to Windows 11: the canvas seeds windows_11,
and a workstation with no os compiles to it. A domain controller or a plain
server dropped from the palette, or one with no os, is Windows Server 2022; the
SQL Server, Web Server, File Server, and AD CS Server presets start as Windows
Server 2019. The shipped blueprints' former Windows 10 workstations are now
Windows 11.
What a Windows 10 or 11 host boots depends on the provider:
| Provider | Windows 11 | Windows 10 |
|---|---|---|
| Azure | the windows-11 Marketplace image, SKU win11-26h2-pro
|
the Windows-10 Marketplace image, SKU win10-22h2-pro-g2
|
| GCP, AWS | Windows Server 2022 | Windows Server 2022 |
GCP and AWS publish no Windows client image, so a Windows 10 or 11 host there
boots Windows Server 2022, and the validator notes it with CAP005 (info). Windows
10 is past end of support: Microsoft ended it on 2025-10-14. The Inspector lists it
as "Windows 10 (past end of support)", and a host set to windows_10 on Azure,
Proxmox, or ESXi gets the info note CAP006. Neither note blocks an export, and
windows_10 stays available for labs that need it.
On a client edition of Windows (Azure, and Proxmox or ESXi with a client image),
a user's scheduled tasks start only after someone has signed in at the console
since boot. So a host whose user activity bots run as a user (the logon bot, the
scheduled task, the stored credential, the poisoning bots) signs that user in at
the console at every boot, with the password kept as an LSA secret rather than in
the Winlogon registry key. The host reboots once when this is first set, and
DEFENSE-BRIEFING.md lists the sign-in for that host. Server editions, and GCP
and AWS workstations, which boot Windows Server 2022, are unchanged.
- Proxmox and ESXi are on the roadmap and not yet supported. Neither can allocate a public address on its own, so redirector reachability there depends on a network redStackPRO does not control.
AWS quotas are per region, and a compile does not check them against your account. Scope them before you deploy, so an apply does not fail partway and leave partial infrastructure running and billing. Every item below is per region: a new region means re-checking these.
The AWS default is 5 Elastic IPs per region (Service Quotas code L-0263D0A3,
"EC2-VPC Elastic IPs"). The export allocates one for each host that takes a public
address (the jumpbox and, on the attack side, the redirector) and one for each
network that builds a NAT gateway.
| Range | Elastic IPs | What takes them |
|---|---|---|
| Defense/AD range | about 2 | jumpbox, NAT gateway |
| Offense range | about 3 | jumpbox, redirector, NAT gateway |
At the default of 5, a region holds only one or two concurrent ranges. Request an increase per region in advance:
aws service-quotas request-service-quota-increase --service-code ec2 --quota-code L-0263D0A3 --desired-value <n> --region <region>
An EIP shortfall surfaces at provisioning, after the instances are already up, so a failed apply leaves them running. Destroy the partial range explicitly (see Managing a range: status, start, stop, teardown in Deploying a Range) rather than leaving it to bill.
AWS caps running On-Demand instance vCPUs per region under "Running On-Demand
Standard (A, C, D, H, I, M, R, T, Z) instances". Hosts are mostly t3.large and
t3.medium (2 vCPU each), so a large AD range (for example full GOAD plus a SIEM)
can exceed the default. Check the quota and raise it before large deploys.
If a topology includes a Kali operator, the Kali AMI needs a one-time Marketplace subscription on the account. A subscription covers the whole account, every region. redStackPRO generates code and never performs account actions, so you subscribe once yourself.
An EC2 key pair name is region-global (unique per region across the account). The
export names the key pair <prefix>-key plus a random suffix per deployment, kept in
that deployment's Terraform state (the auto-stop IAM role and schedule get the same
suffix), so two ranges in one region never collide on it, even with the same
prefix. A distinct prefix per range still keeps host names clear.
A GCP deploy needs a project with billing enabled, the right API turned on, and enough quota. A compile does not check any of this against your account.
GCP resources live inside a project, and a project needs billing enabled before it will create anything. If you do not already have one:
gcloud projects create <project-id> --organization=<org-id>
gcloud billing projects link <project-id> --billing-account=<billing-account-id>
Or create the project and link a billing account from the console
(console.cloud.google.com). Either way, project in deploy.tfvars is this
project's id, not its display name or number.
The export's GCP module only creates Compute Engine resources (instances, networks, addresses, routers, NAT), so that is the one API to enable:
gcloud services enable compute.googleapis.com --project <project-id>
The export does not touch Cloud DNS or any other GCP API. The redirector's DNS A record is something you create by hand at your own registrar, not a GCP resource. See Deploying a Range.
GCP caps running Compute Engine vCPUs per project under CPUS_ALL_REGIONS
(default 32 per project, not per region). This is the quota that actually bites: a
topology with enough hosts can exceed it well before any single-region or
machine-type limit does. Check it, and raise it if needed, under IAM & Admin >
Quotas in the console, filtered to the Compute Engine API, before a large deploy.
The export reserves a static external address for the jumpbox, and for a redirector on the attack side, so each range holds one or two per region. GCP also caps external IP addresses per region. Check the same Quotas page, filtered to Compute Engine "IP addresses", before running more than a couple of ranges in one region.
Flag: unlike AWS's service-quotas CLI, a GCP quota increase is normally
requested through the console UI, not a single scriptable command. Confirm the
current process in the console before planning around a CLI-only flow.
An Azure deploy needs a subscription, the resource providers the export uses, and enough quota. Unlike AWS and GCP, the deploy checks the quota itself before it applies. In short:
-
Subscription.
subscription_idindeploy.tfvars. Find it withaz account show --query id -o tsv. - Resource providers. Microsoft.DevTestLab has to be registered for the auto-stop schedule (every shipped blueprint sets one). The core set (Microsoft.Compute, Microsoft.Network, Microsoft.Storage, Microsoft.ManagedIdentity, and the rest) is registered by Terraform at apply when your identity may register providers.
-
vCPUs. Azure caps vCPUs per region in total and per size family. A new
or trial subscription often has only 4 to 10 regional vCPUs and family
quotas around 10; the full
redstackneeds 16. -
Size restrictions. Some subscriptions are barred from a size or a whole
size family in a region (
SkuNotAvailable). Override the size per role withvm_sizesindeploy.tfvars. - Public IPs. The tested blueprints take 2 (defense) or 3 (offense): the jumpbox, the NAT gateway, and on offense the redirector. Each further redirector or NAT gateway adds one.
The commands, the default size per role, and what the preflight checks are in Cloud Prerequisites.
Exposure is a ceiling, not a default. A segment declares how exposed its hosts may be,
and the compiler will not emit a host more exposed than its segment allows. If a host
asks for a public address in a segment that permits none, the validator raises EXP005
rather than deploying something that reads as exposed and is actually unreachable.
See Validation.
Every rule that admits traffic from outside the range exists because an edge called
for it or because of operator_source_ranges; nothing is open to the internet by
default. Inside, an offense stack is open to itself: every host accepts all traffic
from the stack's own networks, peered ones included. A defense range stays
segmented: domain hosts and the SIEM accept all traffic from their own segment, and
everything else is edge derived. Egress is
a separate question: the cloud providers leave the security group or firewall open at
egress, and what actually leaves is decided by a segment's egress field and its NAT
or internet gateway routing, not by a port rule.
| Node kind | Source | Port | Protocol | Purpose |
|---|---|---|---|---|
| jumpbox | operator_source_ranges |
22 | tcp | operator ssh access |
| jumpbox | operator_source_ranges |
443 | tcp | the Guacamole portal, access_mode: public (default) only |
| jumpbox | operator_source_ranges |
the vpn_port overlay (default 51820 WireGuard, 1194 OpenVPN) |
the vpn_protocol overlay (udp or tcp) |
operator VPN access, access_mode: wireguard or openvpn only; the portal then sits behind this tunnel instead of on 443 |
| jumpbox | its own segment | all | all | the foothold rule: accepts traffic range hosts initiate back to it, such as a coerced or relayed NTLM callback, or a pivot return |
| redirector | 0.0.0.0/0 |
the fronts edge's listen_port (443 in every shipped example) |
tcp, or udp when the edge's protocol is dns
|
the fronted C2 traffic |
| redirector | 0.0.0.0/0 |
80 | tcp | the Certbot ACME http-01 challenge, and the http to https redirect |
| teamserver | the redirector on a fronts edge |
the edge's upstream_port (443 in every shipped example) |
tcp, or udp when the edge's protocol is dns
|
fronted C2 traffic forwarded by the redirector |
| teamserver | the operator(s) that drive it | Mythic 7443, Sliver 31337, Adaptix 4321 | tcp | the C2's own operator control plane (a none teamserver opens none; see the note below) |
| collector | senders on a logs_to edge |
the collector's ingest_port overlay, default 5044 |
tcp | log shipping ingest, Logstash's beats input by default |
The jumpbox also reaches every host it manages, on 22 (ssh) or 5986 (WinRM), plus 3389 when the Guacamole portal needs an RDP tile (a Windows host, or a desktop mode Kali operator). Those rules sit on the managed host's own firewall entry, not the jumpbox's, so they are not in the table above.
The none teamserver. A none teamserver stands up no service. It is a plain
Debian box for a custom, operator-supplied C2 (OC2, Cobalt Strike, or any C2 you
bring), so redStackPRO opens no control port for it.