Skip to content

AWS Kali attack box support - #418

Merged
mkultraWasHere merged 6 commits into
mainfrom
codex/aws-attacker-box
Aug 7, 2026
Merged

AWS Kali attack box support#418
mkultraWasHere merged 6 commits into
mainfrom
codex/aws-attacker-box

Conversation

@mkultraWasHere

@mkultraWasHere mkultraWasHere commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Added

  • Optional AWS Kali attack-box provisioning through dreadgoad infra plan/apply/destroy --with-kali.
  • Private SSM-only access and scoring-tool bootstrap from the official Kali Marketplace image.
  • Role-based attack-box discovery for live scoring and dreadgoad ssm connect attack-box.

Changed

  • AWS discovery now scopes instances by Project and Environment tags and preserves provider tags.
  • dreadgoad doctor now checks for the AWS Session Manager plugin.
  • AWS infrastructure teardown includes the optional Kali unit so deployed attack boxes are not orphaned.

Fixed

  • Out-of-inventory AWS hosts can now be reached through provider discovery.
  • PowerShell fan-out no longer targets the Linux attack box when selecting all lab hosts.

Notes

  • The target AWS account must accept the official Kali Marketplace image terms before its first deployment.

Provision an optional tagged Kali host on AWS so scoring and SSM workflows can discover it without inventory or public ingress. Bootstrap the SSM agent and required tooling from the official Marketplace image.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds optional AWS Kali “attack box” provisioning and updates the CLI/provider discovery logic so the attack box (and other AWS instances) can be found via tags instead of relying solely on the Ansible inventory.

Changes:

  • Introduces a new Terraform module and Terragrunt units for provisioning an optional Kali attack box on AWS, bootstrapped for SSM access and scoring tooling.
  • Updates AWS discovery to scope instances by Project + Environment tags and preserves instance tags for role-based selection.
  • Enhances CLI workflows: infra --with-kali, doctor checks for Session Manager plugin, ssm connect can resolve out-of-inventory hosts, and scoring can auto-discover the attack box.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
modules/terraform-aws-kali/versions.tf Defines Terraform/provider constraints for the new AWS Kali module.
modules/terraform-aws-kali/variables.tf Adds inputs for attack box deployment, AMI selection, and tagging.
modules/terraform-aws-kali/user_data.sh.tpl Bootstraps SSM agent and scoring tools on first boot.
modules/terraform-aws-kali/README.md Documents module behavior, tagging, and public IP guidance.
modules/terraform-aws-kali/outputs.tf Exposes instance identifiers, IPs, SG, and selected AMI.
modules/terraform-aws-kali/main.tf Composes the instance-factory module to deploy/tag the Kali instance.
modules/terraform-aws-instance-factory/main.tf Allows public IP association independent of SSM enablement.
infra/goad-deployment/test/us-east-2/kali/terragrunt.hcl Adds optional AWS Kali unit (test env) gated by env var.
infra/goad-deployment/test/env.hcl Adds kali_instance_type env setting (test).
infra/goad-deployment/staging/us-west-1/kali/terragrunt.hcl Adds optional AWS Kali unit (staging env) gated by env var.
infra/goad-deployment/staging/env.hcl Adds kali_instance_type env setting (staging).
docs/scoring.md Documents auto-discovery behavior for the AWS attack box.
docs/mkdocs/docs/providers/aws.md Documents Session Manager plugin + Marketplace subscription + connect flow.
docs/mkdocs/docs/cli-reference.md Documents dreadgoad infra --with-kali.
cli/internal/provider/provider.go Adds tag support to provider instances + role-based selection helper.
cli/internal/provider/provider_test.go Tests role-based selection helper.
cli/internal/provider/factory.go Extends constructor options with AWS profile support.
cli/internal/doctor/checks.go Adds Session Manager plugin check for AWS.
cli/internal/azure/provider.go Preserves instance tags when converting to provider instances.
cli/internal/aws/provider.go Plumbs AWS profile into client creation; preserves tags.
cli/internal/aws/provider_test.go Tests that AWS provider conversion preserves tags.
cli/internal/aws/ec2.go Switches discovery filters to Project + Environment; captures tags.
cli/internal/aws/ec2_test.go Tests the new discovery filter behavior.
cli/cmd/ssm.go Adds out-of-inventory resolution + attack-box alias; excludes attack box from PowerShell fan-out.
cli/cmd/ssm_test.go Tests ssm connect resolution and attack-box exclusion.
cli/cmd/score.go Adds AWS attack-box auto-discovery for live verification; refactors region/profile resolution.
cli/cmd/score_test.go Tests AWS region/profile resolution behavior.
cli/cmd/infra_cmd.go Adds AWS --with-kali wiring and ensures destroy cleans up existing Kali unit.
cli/cmd/env_cmd.go Adds kali_instance_type to generated env.hcl templates.
Suppressed comments (1)

cli/cmd/ssm.go:218

  • resolveSSMHost currently drops errors from FindInstanceByHostname and from the role-based DiscoverInstances lookup. If AWS discovery fails (e.g., credentials/permissions), the user will get a misleading "host not found" message instead of the real API error. Also, for the attack-box alias it’s cleaner to skip the hostname lookup entirely and go straight to role discovery.
	if inst, err := prov.FindInstanceByHostname(ctx, env, hostName); err == nil && inst.ID != "" {
		return inst, nil
	}

	// Also accept the stable role name so callers do not need to know whether
	// the attack box resource is named "kali", "attacker", or something else.
	if strings.EqualFold(hostName, "attack-box") || strings.EqualFold(hostName, "attackbox") {
		instances, err := prov.DiscoverInstances(ctx, env)
		if err == nil {
			if inst := provider.FindInstanceByRole(instances, "AttackBox"); inst != nil {
				return inst, nil
			}
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cli/cmd/ssm.go Outdated
AWS attack boxes use Systems Manager for access, so exposing a public-IP option adds unnecessary scope and weakens the intended private deployment model.
Preserve inventory fallback while reporting parse failures, and return AWS discovery errors instead of masking credential or permission problems as missing hosts.
@mkultraWasHere

Copy link
Copy Markdown
Contributor Author

Also addressed Copilot’s additional review-body finding in fce6eeb: the attack-box alias now goes directly to role-based discovery, avoiding the redundant hostname lookup, and both discovery paths preserve underlying AWS errors. Regression tests cover the alias routing and error propagation.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

Suppressed comments (2)

cli/cmd/ssm.go:228

  • The discovery fallback can return a stopped instance because the provider's FindInstanceByHostname contract includes instances in any state. ssm connect will then pass that ID to start-session, which can only connect to a running managed instance. Reject a non-running discovered target here so users get an actionable error instead of a failed Session Manager invocation.
	inst, err := prov.FindInstanceByHostname(ctx, env, hostName)
	if err != nil {
		return nil, fmt.Errorf("discover AWS host %q: %w", hostName, err)
	}
	if inst != nil && inst.ID != "" {
		return inst, nil

cli/internal/aws/ec2.go:129

  • This breaks the documented external-infrastructure discovery contract: docs/mkdocs/docs/providers/external-infrastructure.md:34-56 tells users that a matching Name tag is sufficient, but these filters now silently exclude those instances unless they also have Project=DreadGOAD and Environment=<env>. Update that guide and migration requirements as part of this change (or retain a compatibility path), otherwise inventory sync and the new out-of-inventory lookup stop working for configurations that satisfy the current documentation.
func discoveryFilters(env string, states []string) []types.Filter {
	filters := []types.Filter{
		{Name: Ptr("tag:Project"), Values: []string{"DreadGOAD"}},
		{Name: Ptr("tag:Environment"), Values: []string{env}},
	}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

Suppressed comments (1)

cli/cmd/ssm.go:185

  • The fallback discovery uses prov, which was constructed from cfg.ResolveRegion() before the inventory is parsed, while the session later uses ResolveRegionWithInventory(inv). When the inventory's deployed region differs from stale/fallback config, inventory hosts still connect correctly, but attack-box and other out-of-inventory names query EC2 in the wrong region and report that the host is missing. Resolve the authoritative region before constructing the AWS provider, and use that same region for both discovery and StartInteractiveShell.
	target, err := resolveSSMHost(ctx, prov, cfg.Env, inv, args[0])

Track the cloud-init template as executable and commit the generated Terraform module documentation expected by the repository hooks.
Match the repository shfmt configuration so pre-commit does not rewrite the cloud-init template in CI.
@mkultraWasHere

Copy link
Copy Markdown
Contributor Author

Confirmed and fixed in 7e48c8a — applied the exact shfmt output from job 92977469600 to modules/terraform-aws-kali/user_data.sh.tpl. The resulting blob matches CI’s formatter output (3850c9e…) and bash -n passes.

@mkultraWasHere
mkultraWasHere marked this pull request as ready for review August 7, 2026 20:36
@mkultraWasHere

Copy link
Copy Markdown
Contributor Author

Confirmed and fixed the three remaining Copilot review-body findings in c2a4a3a: AWS discovery now unions tag-based and legacy Name-pattern results with ID de-duplication; ssm connect constructs discovery and the session from the same inventory-authoritative region; and hostname fallback rejects known non-running instances with an actionable error. Added regression tests for all three paths.

@mkultraWasHere
mkultraWasHere added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 031d237 Aug 7, 2026
13 checks passed
@mkultraWasHere
mkultraWasHere deleted the codex/aws-attacker-box branch August 7, 2026 22:17
mkultraWasHere added a commit that referenced this pull request Aug 11, 2026
Brings in 32 commits, including #423 CLI deployment recovery hardening,
which independently implemented several findings from the stand-up audit:
--from-playbook resume, pointer-based retry overrides so --max-retries 0
means zero, a structural flag-forwarding test for the provision command,
and a doctor failure message that no longer advertises --skip-doctor.

Conflicts and how they were settled:

up.go -- main rewrote the resume path around provisionFailure and
upResumeOptions; kept all of it, and re-applied the Azure module fix
(d4d23a5) on top, since main's runUpInfraApply still omitted
--with-bastion/--with-controller and would have reintroduced the break.

up_test.go -- both sides added the file. No function names collide, so
this is the union: main's 11 provision/resume tests plus the 6 infra and
Azure-module tests, on a merged import block.

provider.go, aws/ec2.go, aws/provider.go, azure/provider.go -- both
sides added fields to Instance. Kept Account/Group and main's Tags. main
extracted discoverInstances, so the Account population moved into that
helper rather than staying in the old inline body.

.gitignore -- kept the console build artifacts and the ADCS zip entries;
main tracks the template sources but not the generated archives.

#418 gave Kali an AWS path, so --with-kali is no longer Azure-only and
the up flag help drops that qualifier.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Changes made to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants