Optional Kali Linux attack box for Azure deployments - #354
Conversation
Adds an opt-in Kali VM that deploys on its own subnet inside the lab VNet, accessible only via Azure Bastion SSH. Marketplace image, headless, no provisioning needed — stock Kali tooling for AD attack exercises. - New `terraform-azure-kali` module (subnet, NSG, NIC, VM with marketplace plan) - `--with-kali` flag on infra apply/plan/destroy - Destroy always includes kali to prevent orphaned VMs - `dreadgoad bastion ssh kali` auto-detects AttackBox role, user, and key - Doctor check warns if marketplace terms not accepted - `env create` auto-derives kali subnet CIDR from VNet - Fix: bastion PersistentPreRunE now calls config.Init() (pre-existing bug) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an optional Azure “attack box” (headless Kali VM) that can be deployed alongside existing Azure lab infrastructure, wired through Terragrunt and surfaced via CLI flags and Bastion SSH conveniences.
Changes:
- Introduces a new
terraform-azure-kalimodule that provisions a Kali VM on a dedicated subnet with Bastion-scoped SSH and emits connection outputs. - Adds
--with-kaliwiring to Azure infra plan/apply/destroy, plus environment config generation for the derived Kali subnet. - Enhances CLI UX: Bastion SSH auto-detects the Kali “AttackBox” role/key/user, and
doctorchecks Kali marketplace terms acceptance.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| modules/terraform-azure-kali/versions.tf | Defines Terraform/provider constraints for the new Kali module. |
| modules/terraform-azure-kali/variables.tf | Declares inputs for Kali subnet, VM sizing, SSH key handling, and image/plan metadata. |
| modules/terraform-azure-kali/README.md | Documents module requirements/inputs/outputs (terraform-docs-style). |
| modules/terraform-azure-kali/outputs.tf | Exposes VM/subnet/NSG IDs, private IP, and SSH key info for consumers. |
| modules/terraform-azure-kali/main.tf | Implements subnet + NSG + NIC + Kali VM provisioning and optional ephemeral SSH key generation. |
| infra/azure/goad-deployment/test/env.hcl | Adds Kali subnet/SSH source/instance size locals for the test environment. |
| infra/azure/goad-deployment/test/centralus/kali/terragrunt.hcl | Adds gated Terragrunt module wiring for the optional Kali deployment. |
| cli/internal/doctor/checks.go | Adds a doctor warning/pass check for Kali Azure Marketplace terms acceptance. |
| cli/cmd/infra_cmd.go | Adds --with-kali and ensures destroy includes Kali when the kali/ module directory exists. |
| cli/cmd/env_cmd.go | Extends Azure subnet derivation to include Kali and emits Kali locals in generated env.hcl. |
| cli/cmd/env_cmd_test.go | Updates subnet derivation tests to validate the new Kali subnet output. |
| cli/cmd/bastion.go | Fixes config initialization and auto-detects Kali AttackBox role for Bastion SSH defaults. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude <noreply@anthropic.com>
Match the controller module's description pattern. Co-Authored-By: Claude <noreply@anthropic.com>
Extract role-based SSH defaults into resolveRoleDefaults() to bring runBastionSSH below the gocyclo threshold of 15. Add BEGIN/END_TF_DOCS markers to the kali README to match the controller module pattern. Co-Authored-By: Claude <noreply@anthropic.com>
Match the pattern of other Azure modules that track their lock files. Co-Authored-By: Claude <noreply@anthropic.com>
Same pre-existing bug as bastion — child commands override the root PersistentPreRunE without calling config.Init(), so the config file is never loaded. Co-Authored-By: Claude <noreply@anthropic.com>
Replace tofu-generated lock file (registry.opentofu.org) with terraform registry hashes to match the other modules. Also only set authType to ssh-key when the ephemeral key file actually exists on disk. Co-Authored-By: Claude <noreply@anthropic.com>
…ms check Only apply role-based username/authType when the ephemeral key file exists, preventing password-auth fallback on key-only VMs. Also use EqualFold for the marketplace terms check since az CLI may return "True" instead of "true". Co-Authored-By: Claude <noreply@anthropic.com>
| validation { | ||
| condition = can(cidrhost(var.kali_subnet_cidr, 0)) | ||
| error_message = "kali_subnet_cidr must be a valid IPv4 CIDR block." | ||
| } |
There was a problem hiding this comment.
Refuted. The controller module uses the identical validation pattern and error message (variables.tf:31-34). Azure subnets reject IPv6 at the API level anyway — tightening HCL validation for an impossible scenario that existing modules also don't guard against is unnecessary churn.
| // Auto-pick the ephemeral key for known VM roles. A failed live lookup | ||
| // is non-fatal — we just fall back to the user-supplied flag values. | ||
| if defaults := resolveRoleDefaults(client, ctx, cfg.Env, args[0]); defaults != nil && defaults.sshKey != "" { |
There was a problem hiding this comment.
Refuted. resolveAzureHost (line 195) already called FindInstanceByHostname to resolve the VM ID moments earlier — the data is cached or at worst one extra API call (~200ms) before an interactive SSH session. The pre-existing controller code made the same unconditional lookup. Adding flag-changed guards would add complexity for negligible gain.
Added
modules/terraform-azure-kali/— Terraform module deploying a headless Kali VM on a dedicated/28subnet with NSG locked to bastion-only SSH--with-kaliflag oninfra apply/plan/destroyto opt in to the attack boxdreadgoad bastion ssh kaliauto-detectsAttackBoxrole tag, SSH key, and usernamedreadgoad doctorwarns if Kali marketplace terms are not acceptedenv createauto-derives kali subnet CIDR (10.X.4.0/28) from the VNet CIDRinfra/azure/goad-deployment/test/centralus/kali/gated byDREADGOAD_ENABLE_AZURE_KALIChanged
deriveAzureSubnetsreturns anazureSubnetsstruct (bastion, controller, kali) instead of two stringscreateAzureEnvHCLtemplate includes kali subnet/instance variablesStandard_D2s_v3(2 vCPU, 8 GB) —B2sis capacity-restricted in centralusFixed
bastioncommandPersistentPreRunEnow callsconfig.Init()— previously skipped config file loading entirely (pre-existing bug)infra destroyalways includes the kali module when thekali/directory exists, preventing orphaned VMsNotes
kali-linux:kali:kali-2026-2) requires one-timeaz vm image terms acceptper subscription