Skip to content

Site-to-Site VPN implementation using AWS VPN Gateway and simulated on-premises environment. Demonstrates hybrid cloud connectivity with Pulumi IaC, IPSec tunnels, BGP routing, and multi-stack orchestration.

Notifications You must be signed in to change notification settings

Sparty-5A/aws-site-to-site-vpn

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” AWS Site-to-Site VPN with Hybrid Cloud Simulation

Python 3.12+ Pulumi AWS VPN IPSec License: MIT

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.


✨ Features

Core Capabilities

  • 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

Security & Reliability

  • 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

Infrastructure as Code

  • 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

πŸ—οΈ Architecture

High-Level Design

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  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)                    β”‚   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

VPN Tunnel Details

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

πŸš€ Quick Start

Prerequisites

  • Python 3.12 or higher
  • Pulumi CLI installed
  • AWS account with credentials configured
  • AWS CLI configured (optional but recommended)

Installation

# 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.txt

Deployment

Step 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 up

Step 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 configuration

πŸ“ Project Structure

aws-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

πŸ’» Usage Examples

Deploy to AWS (Production)

# 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

Deploy to LocalStack (Testing)

# 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)

Verify Connectivity

# 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

Configure strongSwan (On-Premises Side)

# 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

πŸ”§ Configuration

AWS Stack Configuration Options

# 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

On-Premises Stack Configuration

# 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

πŸ›‘οΈ Security Features

VPN Encryption

  • 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

Security Groups

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

Network Configuration

  • 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

πŸ“Š Key Design Patterns

Multi-Stack Orchestration

# 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

Modular VPN Functions

# 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"
)

Route Propagation (Dynamic Routing)

# 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)

πŸŽ“ Technologies Used

  • 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

πŸ’° Cost Estimates

AWS Deployment (4-hour test)

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

πŸ§ͺ Testing

Connectivity Tests

# 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

VPN Tunnel Status

# 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)

strongSwan Status

# 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]: ESTABLISHED

πŸ†˜ Troubleshooting

VPN Tunnels Show "Down"

Cause: 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 strongswan

Can't Ping Across VPN

Checks:

  1. VPN tunnels UP in AWS Console?
  2. BGP session established?
  3. Route propagation enabled?
  4. Security groups allow traffic?
  5. 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 -f

BGP Not Establishing

Cause: 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)

πŸ“š Additional Documentation


🀝 Contributing

This is a portfolio project demonstrating hybrid cloud VPN patterns. Contributions and feedback welcome!


πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments

  • Based on AWS Site-to-Site VPN documentation
  • strongSwan VPN software
  • Pulumi for excellent IaC framework

πŸ“ž Contact

Author: Scott Penry
Email: scottpenry@comcast.net
GitHub: @Sparty-5A


⭐ Show Your Support

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

About

Site-to-Site VPN implementation using AWS VPN Gateway and simulated on-premises environment. Demonstrates hybrid cloud connectivity with Pulumi IaC, IPSec tunnels, BGP routing, and multi-stack orchestration.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages