AWS Lab · Hands-on60–90 min lab

Lab: Your First Working Site-to-Site VPN

Not a read-along. By the end you'll have a real IPsec tunnel UP between two networks you built, and you'll prove it with a ping that crosses it.

From your mentor

You can't borrow a corporate data centre, so we fake one: a second VPC running strongSwan becomes your 'on-prem' router. Everything else is the real AWS VPN you'd build at work. Follow each step, check the checkpoint, and you'll get there.

00

What you’ll build

Time

60–90 minutes

Cost

~$0.10 if done in one sitting (2× t3.micro + a VPN connection at ~$0.05/hr)

Difficulty

Intermediate, you’ve done the Networking Foundations + Site-to-Site VPN lessons

What you’ll build

A 'cloud' VPC (10.0.0.0/16) and a separate 'fake on-prem' VPC (172.16.0.0/16) whose public EC2 runs strongSwan as your Customer Gateway. You then build the AWS VPN side by hand and bring a tunnel UP.

Download the CloudFormation scaffold (.yaml)

Before you start

You need: an AWS account (your IAM admin user), an existing EC2 key pair in the region you’ll use, and the AWS CLI configured (or you can do every step in the console). We’ll use static routing, the simplest path to a tunnel that actually comes UP.

In plain English

Here’s the whole plan in a sentence: the scaffold builds both networks and the fake on-prem router; you create the Customer Gateway, the Virtual Private Gateway, and the VPN connection, turn on route propagation, paste AWS’s tunnel details into strongSwan, and watch the tunnel light up. Then you ping across it. That ping is the whole point.
01

Deploy the scaffold

Download the template above, then create the stack (give it your key pair name). This builds both VPCs, the strongSwan host with an Elastic IP, and a cloud instance to ping.

aws cli
# or upload the .yaml in the CloudFormation console
$ aws cloudformation create-stack --stack-name s2s-vpn-lab --template-body file://s2s-vpn-lab-scaffold.yaml --parameters ParameterKey=KeyName,ParameterValue=YOUR_KEY_PAIR --capabilities CAPABILITY_IAM
AWS docs: cloudformation create-stack

Checkpoint, what you should see

The stack reaches CREATE_COMPLETE. On the stack’s Outputs tab, note four values you’ll use below: StrongSwanPublicIp, CloudInstancePrivateIp,CloudRouteTableId, and the two CIDRs.
02

Create the Customer Gateway

The Customer Gateway tells AWS where your “on-prem” device is, that’s the strongSwan host’s public IP. Use a private ASN like 65000 (it’s only used by BGP, but the field is required).

aws cli
$ aws ec2 create-customer-gateway --type ipsec.1 --public-ip STRONGSWAN_PUBLIC_IP --bgp-asn 65000
AWS docs: create-customer-gateway

Checkpoint, what you should see

You get a CustomerGatewayId (cgw-…). It shows available in the VPC console under Customer Gateways.
03

Create & attach the Virtual Private Gateway

The VGW is the AWS end of the tunnel. Create it, then attach it to the cloud VPC.

aws cli
$ aws ec2 create-vpn-gateway --type ipsec.1
AWS docs: create-vpn-gateway
aws cli
$ aws ec2 attach-vpn-gateway --vpn-gateway-id vgw-XXXX --vpc-id CLOUD_VPC_ID
AWS docs: attach-vpn-gateway

Checkpoint, what you should see

The VGW state becomes attached to the cloud VPC.
04

Enable route propagation

This is the step beginners forget, without it the tunnel can be UP and traffic still won’t flow. Turn on route propagation so the VPN’s routes land in the cloud subnet’s route table automatically.

aws cli
$ aws ec2 enable-vgw-route-propagation --route-table-id CLOUD_ROUTE_TABLE_ID --gateway-id vgw-XXXX
AWS docs: enable-vgw-route-propagation

Checkpoint, what you should see

On the cloud route table’s Route propagation tab, the VGW shows Yes. (Routes appear once the tunnel is UP.)
05

Create the VPN connection (static)

Now the connection itself, static routing, with the on-prem network (172.16.0.0/16) as the static route. This provisions the two tunnels.

aws cli
$ aws ec2 create-vpn-connection --type ipsec.1 --customer-gateway-id cgw-XXXX --vpn-gateway-id vgw-XXXX --options StaticRoutesOnly=true
AWS docs: create-vpn-connection
aws cli
# tell AWS the on-prem network reachable over this VPN
$ aws ec2 create-vpn-connection-route --vpn-connection-id vpn-XXXX --destination-cidr-block 172.16.0.0/16
AWS docs: create-vpn-connection-route

Checkpoint, what you should see

The VPN connection becomes available. Both tunnels show DOWN for now, expected, because the strongSwan side isn’t configured yet.
06

Configure strongSwan (the customer side)

Pull the two tunnel outside IPs and pre-shared keys AWS generated, then feed them to the helper script the scaffold placed on the strongSwan host. First, grab the values:

aws cli
# read the XML: two <tunnel_outside_address><ip_address> and two <pre_shared_key>
$ aws ec2 describe-vpn-connections --vpn-connection-id vpn-XXXX --query 'VpnConnections[0].CustomerGatewayConfiguration' --output text
AWS docs: describe-vpn-connections

Then SSH to the strongSwan host and run the helper with those four values:

aws cli
$ ssh ubuntu@STRONGSWAN_PUBLIC_IP -i your-key.pem
aws cli
# writes /etc/ipsec.conf + secrets and restarts strongSwan
$ sudo /opt/configure-vpn.sh TUNNEL1_AWS_IP TUNNEL1_PSK TUNNEL2_AWS_IP TUNNEL2_PSK

Checkpoint, what you should see

Run sudo ipsec statusall, at least one connection shows ESTABLISHED. Within a minute, the AWS console shows Tunnel 1 UP(and the CloudWatch TunnelState metric flips to 1).

If the install/config needs a nudge

This customer-side step is the one most likely to vary by environment. If ipsec isn’t found, run sudo apt-get install -y strongswan-starter. If a tunnel won’t establish, jump to Troubleshooting below, it’s almost always a PSK/IP mismatch or a blocked UDP port.
07

Prove it, ping across the tunnel

From the strongSwan host (it’s on the 172.16 network), ping the cloud instance’s private IP:

aws cli
# 10.0.x.x, a private address in the OTHER VPC
$ ping -c 4 CLOUD_INSTANCE_PRIVATE_IP

🎉 You did it

Replies come back. A packet just left a host on 172.16.0.0/16, got encrypted, crossed the public internet inside an IPsec tunnel you built, was decrypted by AWS, and reached a private instance on 10.0.0.0/16, which replied the whole way back. That’s a real Site-to-Site VPN. You built it.
08

When it doesn’t work

When it doesn’t work (it happens, work down this list)

Both tunnels stay DOWN

PSKs/IPs must match the describe-vpn-connections output exactly. Confirm the strongSwan SG allows inbound UDP 500 + 4500. Re-run the configure script.

ipsec: command not found

sudo apt-get install -y strongswan strongswan-starter, then re-run the helper script.

Tunnel UP but ping fails

Is route propagation enabled on the cloud route table? Does the cloud SG allow ICMP from 172.16.0.0/16? Is the strongSwan instance’s Source/Dest check OFF (the scaffold sets this)?

ping works one direction only

Confirm the VPN connection’s static route 172.16.0.0/16 exists, and the on-prem route table sends 10.0.0.0/16 to the strongSwan instance (the scaffold adds this).

No replies at all, tunnels look fine

Generate traffic to bring the tunnel fully up (the ping itself does this), and give it ~60 seconds after ESTABLISHED.

09

Tear it down (so it stops billing)

Do this when you’re done

The VPN connection bills ~$0.05/hour while it exists. Delete the VPN pieces first, then the stack.
aws cli
$ aws ec2 delete-vpn-connection --vpn-connection-id vpn-XXXX
AWS docs: delete-vpn-connection
aws cli
$ aws ec2 detach-vpn-gateway --vpn-gateway-id vgw-XXXX --vpc-id CLOUD_VPC_ID && aws ec2 delete-vpn-gateway --vpn-gateway-id vgw-XXXX
aws cli
$ aws ec2 delete-customer-gateway --customer-gateway-id cgw-XXXX
aws cli
# removes both VPCs, instances, and the Elastic IP
$ aws cloudformation delete-stack --stack-name s2s-vpn-lab

Checkpoint, what you should see

The stack reaches DELETE_COMPLETE and the VPN connection/gateways are gone. No more hourly charges.

Next up

Next lab, Make it dynamic (BGP)

Swap static routing for BGP on strongSwan: watch routes auto-propagate and failover happen without you touching a route table.