Every hacker needs a playground β€” a place to break things without breaking real things. After one too many β€œoops I nuked the router” incidents on my home network, I decided it was time for a proper isolated lab.

Enter: AWS EC2. Not just one instance, but an entire throwaway network β€” ephemeral, disposable, and completely cut off from my actual LAN.

Why EC2 for a Playground?

Option Cost Isolation Flexibility
Raspberry Pi cluster ~$200 upfront Physical (good) Low (fixed hardware)
VM on home server $0 (electricity only) Software-only (risky) Medium
EC2 spot instances ~$0.02/hr Excellent Very high

EC2 wins on cost efficiency (pay only when running) and isolation (different VPC, security groups, no LAN access). Plus, I can spin up 10 identical labs in parallel for testing distributed systems.

Architecture: Thelab

VPC: 10.100.0.0/16
β”œβ”€β”€ Public Subnet (10.100.1.0/24)
β”‚   β”œβ”€β”€ Jump/Bastion Host (t3.micro) β€” SSH entry point
β”‚   └── NAT Gateway β€” egress only
β”œβ”€β”€ Private Subnet (10.100.2.0/24)
β”‚   β”œβ”€β”€ Target 1: Kali Linux (t3.small) β€” attack host
β”‚   β”œβ”€β”€ Target 2: Metasploitable3 (t3.small) β€” vulnerable victim
β”‚   β”œβ”€β”€ Target 3: Windows Server 2022 (t3.medium) β€” AD playground
β”‚   └── ELB/ALB β€” for load balancer testing
└── Security Groups
    β”œβ”€β”€ jump-sg: SSH from MyIP only
    β”œβ”€β”€ kali-sg: All egress, no ingress (except from jump)
    β”œβ”€β”€ victim-sg: SSH from jump only, HTTP/HTTPS open
    └── windows-sg: RDP from jump only, SMB from kali subnet

Core principle: Nothing talks to the internet except through the NAT. Nothing talks into the lab except through the jump host.

Building It (Terraform Edition)

Of course I didn’t click through the AWS console. Infrastructure-as-Code all the way β€” Terraform modules for repeatability.

Key Terraform Resources

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.0.0"
  
  cidr = "10.100.0.0/16"
  azs  = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.100.2.0/24"]
  public_subnets  = ["10.100.1.0/24"]
}

resource "aws_instance" "kali" {
  ami           = data.aws_ami.kali.id
  instance_type = "t3.small"
  subnet_id     = module.vpc.private_subnets[0]
  
  vpc_security_group_ids = [aws_security_group.kali.id]
  key_name               = aws_key_pair.deployer.key_name
  
  user_data = <<-EOF
              #!/bin/bash
              apt update && apt install -y metasploit-framework
              EOF
}

Spot instance strategy: Use ` Spot with instance_interruption_behavior = terminate for ephemeral targets. If AWS reclaims the instance? Who cares β€” just terraform apply` again and a fresh VM appears.

Cost Control: The Art of Not Going Broke

EC2 can get expensive if you leave instances running. My guardrails:

β‘  Auto-Shutdown Lambda (10PM UTC)

CloudWatch Events β†’ Lambda function that stops all instances in the lab VPC at 10 PM every night. No zombie instances.

def lambda_handler(event, context):
    ec2 = boto3.client('ec2')
    response = ec2.describe_instances(
        Filters=[{'Name': 'tag:Lab', 'Values': ['playground']}]
    )
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            if instance['State']['Name'] == 'running':
                ec2.stop_instances(InstanceIds=[instance['InstanceId']])

β‘‘ Spot Instances with Max Price Cap

Set spot_max_price = "0.02" (the current spot price for t3.small). If price spikes, AWS won’t launch β€” better than getting surprise bills.

β‘’ Budget Alerts

AWS Budgets β†’ Email alert at $5/month. I’ve never hit it, but the alert is my canary.

SSH Jump Host: The Only Door In

No direct SSH to private instances. All connections route through a bastion host in the public subnet.

# ~/.ssh/config
Host bastion
    HostName ${BASTION_IP}
    User ubuntu
    IdentityFile ~/.ssh/lab-key.pem

Host kali-private
    HostName 10.100.2.10
    User kali
    IdentityFile ~/.ssh/lab-key.pem
    ProxyJump bastion

Now ssh kali-private automatically tunnels through the bastion. Clean, secure, no open ports on targets.

Snapshots & AMI Baking

I’m lazy β€” I don’t want to reinstall tools every time I spin up a new lab.

Solution: Create custom AMIs with tools pre-installed.

  1. Launch base Ubuntu 22.04 instance
  2. Install all tools (nmap, metasploit, BurpSuite, gobuster, etc.)
  3. create_image β†’ my hacker-tools-ami AMI
  4. Future instances launch from my AMI in 45 seconds instead of 15 minutes

Golden images are the key to rapid lab turnover.

Lessons Learned (Pain Points Catalog)

1. Security Group Hell

AWS SGs are stateful, but the default VPC flow logs are not enabled by default. I spent 2 hours debugging β€œwhy can’t I ping this instance?” only to realize outbound ICMP was blocked at the subnet’s NACL.

Fix: Explicitly allow ICMP in both SGs and NACLs for lab use.

2. EIP Costs

Elastic IPs are free only when attached to a running instance. I allocated 5 EIPs for static IP assignments, then forgot about them when instances were terminated. $3.65/month Γ— 5 = $18/month for nothing.

Fix: Use DNS records in Route 53 with TTL 60 instead of static IPs. Let AWS assign private IPs; public IPs are assigned dynamically and released on stop.

3. IAM Over-Privilege

My first IAM role had AdministratorAccess policy attached. Don’t do that. A compromised instance with that role can do anything β€” create more instances with IAM roles, delete CloudTrail logs, you name it.

Fix: Least privilege. Created LabInstanceRole with only:

  • ec2:Describe* (read-only)
  • ssm:SendCommand (for SSM agent)
  • s3:GetObject (pull tooling from a bucket)

4. Snapshots Are Not Free

EBS snapshots go to S3 and cost money. A 20GB instance snapshot = ~$0.10/month. Multiply by 10 lab instances and it adds up. Plus, snapshots are incremental but they chain β€” deleting the base snapshot forces a full recompute.

Fix: Automated snapshot lifecycle policy: keep only last 3 snapshots per instance. Tag all lab resources with Lab=playground, use lifecycle rules on that tag.

5. Data Transfer Costs

Big one: Data transfer out of AWS is expensive ($0.09/GB after free tier). I was downloading exploit tools from GitHub through the NAT, then pulling to my laptop via SCP. That’s double billing β€” once for internet egress, once for my ISP.

Fix: Use AWS Systems Manager Session Manager for shell access (no SSH, no data transfer costs). For file transfers, use aws s3 sync with a bucket in the same region (free intra-region transfer).

The Toolchain

  • Terraform β€” IaC for everything (VPC, instances, SGs, IAM)
  • Ansible β€” Provision instances post-boot (install tools, configure users)
  • Git β€” Everything versioned (including my custom AMI build scripts)
  • AWS CLI β€” Manual ad-hoc operations
  • Session Manager β€” No-SSH access (browser-based console also available)

Cost Breakdown (Monthly, Lab Idle Most of Time)

Resource Qty Cost/Mo
t3.small spot (8 hrs/day) 3 ~$4.50
t3.micro bastion (24/7) 1 ~$7.50
EBS gp3 (20GB each) 4 ~$2.00
EIP (attached) 1 $0 (attached)
Total Β  ~$14/month

Worth every penny.

Future Plans

  • VPC peering to connect lab to my home lab via Tailscale (routed VPN)
  • Custom AMI baking pipeline with Packer (auto-build on tool updates)
  • Automated lab teardown after every CTF event (no manual cleanup)
  • Grafana dashboard showing lab status (up/down, costs, IP assignments)

Final Thoughts

An EC2 playground is the ultimate hacker sandbox β€” you get root on real hardware without the guilt of breaking something important. Isolation is rock-solid. Costs are manageable with spot instances and automation.

And when you inevitably rm -rf / something? Just terraform destroy and start fresh. No tears, no recovery mode. Just a clean slate.

Now if you’ll excuse me, I have a Windows Server 2022 domain controller that needs pwning.

  • Tiri

</content>