Production-grade Site-to-Site VPN implementation demonstrating hybrid cloud connectivity between AWS and simulated on-premises infrastructure. Features dual-tunnel IPSec VPN, BGP dynamic routing, multi-stack Pulumi orchestration, and comprehensive network security. Perfect for learning enterprise VPN patterns without physical infrastructure.
- Site-to-Site VPN - Production-grade IPSec VPN tunnels between AWS and on-premises
- Dual-Tunnel Architecture - High availability with two redundant VPN tunnels
- BGP Dynamic Routing - Automatic route exchange using Border Gateway Protocol
- Hybrid Cloud Simulation - Complete on-premises environment using second AWS VPC
- Multi-Stack Orchestration - Two coordinated Pulumi stacks working together
- IPSec Encryption - Industry-standard tunnel encryption (IKEv2)
- strongSwan VPN - Production-grade open-source VPN software on customer gateway
- Security Groups - Properly configured ingress/egress rules for VPN protocols
- Source/Dest Check Disabled - Enables routing through VPN gateway instance
- Pulumi Python - Modern IaC with type safety and familiar syntax
- Modular Design - Separate modules for VPC, networking, and VPN components
- Reusable Functions - Clean abstractions for common patterns
- Configuration Management - Environment-specific settings via Pulumi config
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ON-PREMISES ENVIRONMENT (Simulated) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β VPC: 10.10.0.0/16 β β
β β ββββββββββββββββββββββ ββββββββββββββββββββ β β
β β β strongSwan Gateway βββββββββββ€ Test Instance β β β
β β β 10.10.1.10 β β 10.10.1.20 β β β
β β β Public IP: X.X.X.X β ββββββββββββββββββββ β β
β β ββββββββββββββββββββββ β β
β β β β β
β β β Customer Gateway β β
β ββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β IPSec VPN Tunnels (2x for HA)
β - Tunnel 1: 169.254.10.0/30
β - Tunnel 2: 169.254.10.4/30
β - Encryption: AES-256
β - Routing: BGP (AS 65000 β AWS ASN)
β
βββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AWS ENVIRONMENT β
β ββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββ β
β β VPC: 10.0.0.0/16 β β
β β βΌ β β
β β ββββββββββββββββββββββ ββββββββββββββββββββ β β
β β β VPN Gateway βββββββββββ€ Test Instance β β β
β β β (AWS Managed) β β 10.0.1.10 β β β
β β β β ββββββββββββββββββββ β β
β β ββββββββββββββββββββββ β β
β β β β β
β β β Route Propagation Enabled β β
β β βΌ β β
β β ββββββββββββββββββββββ β β
β β β Route Table β β β
β β β - 0.0.0.0/0 β IGW β β β
β β β - 10.10.0.0/16 β VGW (propagated) β β
β β ββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tunnel 1:
- Inside CIDR: 169.254.10.0/30
- AWS Endpoint: 169.254.10.1
- Customer Endpoint: 169.254.10.2
- Status: Active (Primary)
Tunnel 2:
- Inside CIDR: 169.254.10.4/30
- AWS Endpoint: 169.254.10.5
- Customer Endpoint: 169.254.10.6
- Status: Active (Secondary/Failover)
Routing:
- Protocol: BGP (Border Gateway Protocol)
- AWS ASN: Automatically assigned by AWS
- Customer ASN: 65000 (Private ASN range)
- Route Propagation: Automatic
- Python 3.12 or higher
- Pulumi CLI installed
- AWS account with credentials configured
- AWS CLI configured (optional but recommended)
# Clone repository
git clone https://github.com/Sparty-5A/aws-site-to-site-vpn.git
cd aws-site-to-site-vpn
# Install dependencies
pip install -r requirements.txtStep 1: Deploy On-Premises Stack
cd onprem-stack
# Initialize stack
pulumi stack init onprem-aws
# Configure
pulumi config set vpc_cidr 10.10.0.0/16
pulumi config set subnet_cidr 10.10.1.0/24
pulumi config set aws:region us-east-1
# Deploy
pulumi up
# Save customer gateway IP for next step
export CUSTOMER_IP=$(pulumi stack output customer_gateway_ip)Step 2: Deploy AWS Stack
cd ../aws-stack
# Initialize stack
pulumi stack init aws-vpn-aws
# Configure
pulumi config set vpc_cidr 10.0.0.0/16
pulumi config set customer_gateway_ip $CUSTOMER_IP
pulumi config set customer_bgp_asn 65000
pulumi config set vpn_use_bgp true
pulumi config set enable_test_instance true
pulumi config set aws:region us-east-1
# Deploy
pulumi upStep 3: Verify VPN Connection
# Check VPN tunnel status in AWS Console
# VPC β Site-to-Site VPN Connections β Tunnel Details
# Both tunnels should show status "UP" after strongSwan configurationaws-site-to-site-vpn/
βββ README.md # This file
βββ LICENSE # MIT License
βββ requirements.txt # Python dependencies
β
βββ aws-stack/ # AWS-side VPN infrastructure
β βββ __main__.py # Main Pulumi program (AWS side)
β βββ vpc.py # VPC creation
β βββ networking.py # Subnets, route tables, gateways
β βββ vpn.py # VPN components (VGW, CGW, connection)
β βββ Pulumi.yaml # Pulumi project config
β βββ Pulumi.aws-vpn-aws.yaml # Stack-specific config
β
βββ onprem-stack/ # On-premises simulator
β βββ __main__.py # Main Pulumi program (on-prem)
β βββ Pulumi.yaml # Pulumi project config
β βββ Pulumi.onprem-aws.yaml # Stack-specific config
β
βββ docs/ # Documentation
βββ ARCHITECTURE.md # Detailed architecture
βββ VPN_QUICK_REF_GUIDE.md # Quick reference
βββ DEPLOYMENT_GUIDE.md # Step-by-step deployment
# Deploy on-prem stack
cd onprem-stack
pulumi stack init onprem-aws
pulumi config set aws:region us-east-1
pulumi up
# Get customer gateway IP
export CUSTOMER_IP=$(pulumi stack output customer_gateway_ip)
# Deploy AWS stack
cd ../aws-stack
pulumi stack init aws-vpn-aws
pulumi config set customer_gateway_ip $CUSTOMER_IP
pulumi up# Start LocalStack
docker run -d --name localstack -p 4566:4566 localstack/localstack
# Set environment
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
export AWS_ENDPOINT_URL=http://localhost:4566
# Deploy stacks (same commands as above, different stack names)# SSH to AWS test instance
ssh -i your-key.pem ec2-user@<aws-test-instance-ip>
# Ping on-premises network
ping 10.10.1.10 # On-prem VPN gateway
ping 10.10.1.20 # On-prem test instance
# Check routes
ip route
# Traceroute
traceroute 10.10.1.10# SSH to on-premises VPN gateway
ssh -i your-key.pem ec2-user@<customer-gateway-ip>
# View configuration script
cat /root/configure-vpn.sh
# Edit with AWS VPN details
sudo vim /root/configure-vpn.sh
# Run configuration
sudo bash /root/configure-vpn.sh
# Check strongSwan status
sudo strongswan status
# View logs
sudo journalctl -u strongswan -f# Pulumi.aws-vpn-aws.yaml
config:
# VPC CIDR (must be different from on-prem!)
aws-vpn:vpc_cidr: 10.0.0.0/16
# Customer Gateway IP (from on-prem stack output)
aws-vpn:customer_gateway_ip: <IP_ADDRESS>
# BGP Configuration
aws-vpn:customer_bgp_asn: 65000
aws-vpn:vpn_use_bgp: true
# Tunnel Inside CIDRs (must be from 169.254.0.0/16 with /30)
aws-vpn:tunnel1_inside_cidr: 169.254.10.0/30
aws-vpn:tunnel2_inside_cidr: 169.254.10.4/30
# Optional test instance
aws-vpn:enable_test_instance: true
aws-vpn:instance_type: t2.micro # Free tier eligible
# AWS Region
aws:region: us-east-1# Pulumi.onprem-aws.yaml
config:
# On-Prem Network CIDR (must be different from AWS!)
onprem-simulator:vpc_cidr: 10.10.0.0/16
onprem-simulator:subnet_cidr: 10.10.1.0/24
# Instance type (free tier eligible)
onprem-simulator:instance_type: t2.micro
# AWS Region (should match AWS stack)
aws:region: us-east-1- Protocol: IPSec with IKEv2
- Encryption: AES-256-CBC (tunnel mode)
- Authentication: Pre-shared keys (automatically generated by AWS)
- Perfect Forward Secrecy: Enabled
- Dead Peer Detection: Enabled
On-Premises VPN Gateway:
- SSH (22/tcp) - For management
- IKE (500/udp) - IPSec key exchange
- NAT-T (4500/udp) - IPSec NAT traversal
- ESP (Protocol 50) - Encrypted payload
- AH (Protocol 51) - Authentication header
- ICMP - For connectivity testing
AWS Test Instance:
- SSH (22/tcp) - From anywhere
- ICMP - From on-premises network (10.10.0.0/16)
- All traffic - From on-premises network
- Source/Dest Check: Disabled on VPN gateway (allows routing)
- IP Forwarding: Enabled on on-premises gateway
- Route Propagation: Automatic via VPN gateway
- Redundancy: Dual tunnels for high availability
# Stack 1: On-Premises (Deploy first)
pulumi stack init onprem-aws
pulumi up
export CUSTOMER_IP=$(pulumi stack output customer_gateway_ip)
# Stack 2: AWS (Uses output from stack 1)
pulumi stack init aws-vpn-aws
pulumi config set customer_gateway_ip $CUSTOMER_IP
pulumi up# Create complete VPN setup with one function call
from vpn import create_vpn_gateway, create_customer_gateway, create_vpn_connection
# Virtual Private Gateway (AWS side)
vpn_gateway = create_vpn_gateway(
name="aws-vgw",
vpc_id=vpc.id,
amazon_side_asn=None # AWS assigns automatically
)
# Customer Gateway (represents on-prem)
customer_gateway = create_customer_gateway(
name="customer-gw",
ip_address=customer_gateway_ip,
bgp_asn=65000
)
# VPN Connection (creates 2 tunnels automatically)
vpn_connection = create_vpn_connection(
name="site-to-site-vpn",
vpn_gateway_id=vpn_gateway.id,
customer_gateway_id=customer_gateway.id,
static_routes_only=False, # Use BGP
tunnel1_inside_cidr="169.254.10.0/30",
tunnel2_inside_cidr="169.254.10.4/30"
)# Enable automatic route propagation from VPN gateway
enable_vgw_route_propagation(
name="vpn-propagation",
route_table_id=route_table.id,
vpn_gateway_id=vpn_gateway.id
)
# Routes automatically added to route table:
# 10.10.0.0/16 β VPN Gateway (Propagated: Yes)- Python 3.12+ - Modern Python with type hints
- Pulumi - Infrastructure as Code framework
- AWS VPN Gateway - Managed VPN service
- strongSwan - Open-source IPSec VPN software
- IPSec/IKEv2 - VPN protocols
- BGP - Dynamic routing protocol
- Amazon Linux 2023 - EC2 instances
- AWS VPC - Virtual Private Cloud
| Resource | Cost |
|---|---|
| VPN Connection | ~$0.20 ($0.05/hour Γ 4 hours) |
| EC2 Instances (2Γ t2.micro) | $0.00 (Free tier) |
| Data Transfer | ~$0.02 (minimal testing) |
| Total | ~$0.22 |
Annual Cost (if left running):
- VPN Connection: ~$438/year ($0.05/hour)
- EC2 Instances: $146/year (after free tier)
- Total: ~$584/year
Cost Savings:
- β Use t2.micro (free tier eligible)
- β No NAT Gateway ($0.045/hour saved)
- β Destroy after testing
- β LocalStack for free testing
# From AWS test instance
ping 10.10.1.10 # On-prem VPN gateway
ping 10.10.1.20 # On-prem test instance
traceroute 10.10.1.10
# From on-prem test instance
ping 10.0.1.10 # AWS test instance
traceroute 10.0.1.10# Check tunnel status in AWS Console
# VPC β Site-to-Site VPN Connections β Tunnel Details
# Both tunnels should show:
# Status: UP
# BGP Status: UP (if using BGP)# SSH to on-prem VPN gateway
ssh ec2-user@<customer-gateway-ip>
# Check status
sudo strongswan status
# Expected output:
# Security Associations (2 up, 0 connecting):
# aws-vpn-tunnel1[1]: ESTABLISHED
# aws-vpn-tunnel2[2]: ESTABLISHEDCause: strongSwan not configured on customer gateway yet
Solution:
# SSH to customer gateway
ssh ec2-user@<customer-gateway-ip>
# Configure strongSwan with AWS VPN details
sudo bash /root/configure-vpn.sh
# Restart strongSwan
sudo systemctl restart strongswanChecks:
- VPN tunnels UP in AWS Console?
- BGP session established?
- Route propagation enabled?
- Security groups allow traffic?
- Source/dest check disabled on VPN gateway?
Debug:
# Check routes
ip route
# Check security groups
aws ec2 describe-security-groups --group-ids <sg-id>
# Check strongSwan logs
sudo journalctl -u strongswan -fCause: Incorrect ASN configuration
Solution:
# Verify ASNs match
pulumi stack output vpn_tunnel1_bgp_asn # AWS side
# Compare with customer_bgp_asn in config (should be 65000)- Architecture Deep Dive - Detailed technical architecture
- VPN Quick Reference - Quick commands
- Deployment Guide - Step-by-step deployment
This is a portfolio project demonstrating hybrid cloud VPN patterns. Contributions and feedback welcome!
This project is licensed under the MIT License - see the LICENSE file for details.
- Based on AWS Site-to-Site VPN documentation
- strongSwan VPN software
- Pulumi for excellent IaC framework
Author: Scott Penry
Email: scottpenry@comcast.net
GitHub: @Sparty-5A
Give a βοΈ if this project helped you learn about Site-to-Site VPNs!
Built for hybrid cloud networking | Production-grade VPN patterns | Infrastructure as Code