My EC2 Playground: Building a Secure Hack Lab on AWS
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.
- Launch base Ubuntu 22.04 instance
- Install all tools (nmap, metasploit, BurpSuite, gobuster, etc.)
create_imageβ myhacker-tools-amiAMI- 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>