Setup WireGuard VPN on AWS
- Authors
- Name
- upvpn LLC
- @upvpnapp
Table of Contents
- Introduction
- Infrastructure Setup
- WireGuard Installation and Configuration
- Client Configuration
- Complete Infrastructure Script
- Complete WireGuard Script
- Using the VPN
- Adding Additional Clients
Introduction
WireGuard is a modern, fast, and secure VPN protocol that offers excellent performance and easy setup. This guide will walk you through setting up a WireGuard VPN server on AWS with both IPv4 and IPv6 support from scratch.
Infrastructure Setup
Configure your local aws cli to run commands.
1. Create VPC and Subnets
First, we'll create a VPC with IPv6 support and necessary subnets:
# Create VPC
VPC_ID=$(aws ec2 create-vpc \
--region us-west-2 \
--cidr-block 10.0.0.0/16 \
--amazon-provided-ipv6-cidr-block \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=wireguard-vpc}]' \
--output text --query 'Vpc.VpcId')
# Get IPv6 CIDR of VPC
IPV6_CIDR_BLOCK=$(aws ec2 describe-vpcs \
--vpc-ids $VPC_ID \
--output text \
--query 'Vpcs[0].Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock')
# Create IPv6 block of size /60
IPV6_SUBNET_CIDR=$(echo $IPV6_CIDR_BLOCK | cut -d '/' -f1)/60
# Create public subnet
SUBNET_ID=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.1.0/24 \
--ipv6-cidr-block ${IPV6_SUBNET_CIDR} \
--availability-zone us-west-2a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=wireguard-subnet}]' \
--output text --query 'Subnet.SubnetId')
# Create and attach Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=wireguard-igw}]' \
--output text --query 'InternetGateway.InternetGatewayId')
aws ec2 attach-internet-gateway \
--vpc-id $VPC_ID \
--internet-gateway-id $IGW_ID
# Get Route table ID so that we can setup connection to IGW
ROUTE_TABLE_ID=$(aws ec2 describe-route-tables \
--filters "Name=vpc-id,Values=$VPC_ID" \
--output text \
--query 'RouteTables[0].RouteTableId')
# Create routes for IPv4 to internet
aws ec2 create-route \
--route-table-id $ROUTE_TABLE_ID \
--destination-cidr-block "0.0.0.0/0" \
--gateway-id $IGW_ID
# Create routes for IPv6 to internet
aws ec2 create-route \
--route-table-id $ROUTE_TABLE_ID \
--destination-ipv6-cidr-block "::/0" \
--gateway-id $IGW_ID
2. Create Security Group
Create a security group for the WireGuard server:
SG_ID=$(aws ec2 create-security-group \
--group-name wireguard-sg \
--description "Security group for WireGuard VPN" \
--vpc-id $VPC_ID \
--output text --query 'GroupId')
# Allow WireGuard UDP port
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol udp \
--port 51820 \
--cidr 0.0.0.0/0
# Allow SSH (for initial setup)
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
# Allow WireGuard UDP port from IPv6
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--ip-permissions IpProtocol=udp,FromPort=51820,ToPort=51820,Ipv6Ranges="[{CidrIpv6=::/0}]"
# Allow SSH (for initial setup) from IPv6
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--ip-permissions IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges="[{CidrIpv6=::/0}]"
3. Launch EC2 Instance
Launch an Ubuntu 24.04 EC2 instance:
# Create key pair to SSH into instance
aws ec2 create-key-pair \
--key-name wireguard-key \
--query 'KeyMaterial' \
--output text > wireguard-key.pem
chmod 400 wireguard-key.pem
# Get Ubuntu 24.04 AMI ID
AMI_ID=$(aws ec2 describe-images \
--owners amazon \
--filters 'Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20240423' \
--output text \
--query 'Images[0].ImageId')
# Launch instance
INSTANCE_ID=$(aws ec2 run-instances \
--image-id $AMI_ID \
--instance-type t3.micro \
--key-name wireguard-key \
--subnet-id $SUBNET_ID \
--security-group-ids $SG_ID \
--associate-public-ip-address \
--enable-primary-ipv6 \
--ipv6-address-count 1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wireguard-server}]' \
--output text --query 'Instances[0].InstanceId')
4. SSH into EC2 Instance
IPV4_ADDRESS=$(aws ec2 describe-instances \
--instance-ids $INSTANCE_ID \
--output text \
--query 'Reservations[0].Instances[0].PublicIpAddress')
ssh -i wireguard-key.pem ubuntu@$IPV4_ADDRESS
WireGuard Installation and Configuration
1. Install WireGuard
SSH into your instance and install WireGuard:
sudo apt update
sudo apt install -y wireguard qrencode
2. Enable IP Forwarding
# Enable IPv4 forwarding
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
# Enable IPv6 forwarding
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
3. Generate Server and Client Keys
sudo mkdir -p /etc/wireguard
# Generate Server Keys
wg genkey | sudo tee /etc/wireguard/server_private.key
sudo chmod 600 /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
# Generate Client Keys
wg genkey | sudo tee /etc/wireguard/client_private.key
sudo cat /etc/wireguard/client_private.key | wg pubkey | sudo tee /etc/wireguard/client_public.key
4. Create Server Configuration
Create /etc/wireguard/wg0.conf
:
SERVER_PRIVATE_KEY=$(sudo cat /etc/wireguard/server_private.key)
CLIENT_PUBLIC_KEY=$(sudo cat /etc/wireguard/client_public.key)
cat << EOF | sudo tee /etc/wireguard/wg0.conf
[Interface]
PrivateKey = ${SERVER_PRIVATE_KEY}
Address = 10.8.0.1/24, fd00:1234:5678:9abc::1/64
ListenPort = 51820
PostUp = iptables -t nat -I POSTROUTING -o ens5 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o ens5 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
PostDown = ip6tables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
[Peer]
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = 10.8.0.2/32, fd00:1234:5678:9abc::2/64
EOF
5. Start WireGuard
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Confirm status by sudo wg show
Client Configuration
1. Create Client Configuration
Create a file named client.conf
:
SERVER_PUBLIC_KEY=$(sudo cat /etc/wireguard/server_public.key)
CLIENT_PRIVATE_KEY=$(sudo cat /etc/wireguard/client_private.key)
SERVER_IPV6=$(ip -6 addr show dev ens5 | grep -oP '(?<=inet6 )([0-9a-f:]+)' | head -1)
# Or use IPv4 if your client doesn't have Ipv6 network
# SERVER_IPV4=$(curl checkip.amazonaws.com)
cat << EOF | sudo tee /etc/wireguard/client.conf
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = 10.8.0.2/32, fd00:1234:5678:9abc::2/64
DNS = 1.1.1.1, 2606:4700:4700::1111
[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
# Or use IPV4 address if your client doesn't support IPv6
Endpoint = [$SERVER_IPV6]:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
2. Connect Client
Share client.conf with the client or scan QR code on mobile devices:
# Generate QR code
sudo cat /etc/wireguard/client.conf | qrencode -t ansiutf8
Complete Infrastructure Script
Here's the complete set of commands to setup VPC, Subnet, Internet Gateway, Route Table, Security Group, and EC2 instance:
#!/bin/bash
# Create VPC
VPC_ID=$(aws ec2 create-vpc \
--region us-west-2 \
--cidr-block 10.0.0.0/16 \
--amazon-provided-ipv6-cidr-block \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=wireguard-vpc}]' \
--output text --query 'Vpc.VpcId')
# Get IPv6 CIDR of VPC
IPV6_CIDR_BLOCK=$(aws ec2 describe-vpcs \
--vpc-ids $VPC_ID \
--output text \
--query 'Vpcs[0].Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock')
# Create IPv6 block of size /60
IPV6_SUBNET_CIDR=$(echo $IPV6_CIDR_BLOCK | cut -d '/' -f1)/60
# Create public subnet
SUBNET_ID=$(aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.1.0/24 \
--ipv6-cidr-block ${IPV6_SUBNET_CIDR} \
--availability-zone us-west-2a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=wireguard-subnet}]' \
--output text --query 'Subnet.SubnetId')
# Create and attach Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=wireguard-igw}]' \
--output text --query 'InternetGateway.InternetGatewayId')
aws ec2 attach-internet-gateway \
--vpc-id $VPC_ID \
--internet-gateway-id $IGW_ID
# Get Route table ID so that we can setup connection to IGW
ROUTE_TABLE_ID=$(aws ec2 describe-route-tables \
--filters "Name=vpc-id,Values=$VPC_ID" \
--output text \
--query 'RouteTables[0].RouteTableId')
# Create routes for IPv4 to internet
aws ec2 create-route \
--route-table-id $ROUTE_TABLE_ID \
--destination-cidr-block "0.0.0.0/0" \
--gateway-id $IGW_ID
# Create routes for IPv6 to internet
aws ec2 create-route \
--route-table-id $ROUTE_TABLE_ID \
--destination-ipv6-cidr-block "::/0" \
--gateway-id $IGW_ID
SG_ID=$(aws ec2 create-security-group \
--group-name wireguard-sg \
--description "Security group for WireGuard VPN" \
--vpc-id $VPC_ID \
--output text --query 'GroupId')
# Allow WireGuard UDP port
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol udp \
--port 51820 \
--cidr 0.0.0.0/0
# Allow SSH (for initial setup)
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
# Allow WireGuard UDP port from IPv6
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--ip-permissions IpProtocol=udp,FromPort=51820,ToPort=51820,Ipv6Ranges="[{CidrIpv6=::/0}]"
# Allow SSH (for initial setup) from IPv6
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--ip-permissions IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges="[{CidrIpv6=::/0}]"
# Create key pair to SSH into instance
aws ec2 create-key-pair \
--key-name wireguard-key \
--query 'KeyMaterial' \
--output text > wireguard-key.pem
chmod 400 wireguard-key.pem
# Get Ubuntu 24.04 AMI ID
AMI_ID=$(aws ec2 describe-images \
--owners amazon \
--filters 'Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20240423' \
--output text \
--query 'Images[0].ImageId')
# Launch instance
INSTANCE_ID=$(aws ec2 run-instances \
--image-id $AMI_ID \
--instance-type t3.micro \
--key-name wireguard-key \
--subnet-id $SUBNET_ID \
--security-group-ids $SG_ID \
--associate-public-ip-address \
--enable-primary-ipv6 \
--ipv6-address-count 1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wireguard-server}]' \
--output text --query 'Instances[0].InstanceId')
IPV4_ADDRESS=$(aws ec2 describe-instances \
--instance-ids $INSTANCE_ID \
--output text \
--query 'Reservations[0].Instances[0].PublicIpAddress')
ssh -i wireguard-key.pem ubuntu@$IPV4_ADDRESS
Complete WireGuard Script
Here's complete set of commands to setup WireGuard server:
#!/bin/bash
# Install WireGuard
sudo apt update
sudo apt install -y wireguard qrencode
# Enable IPv4 forwarding
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
# Enable IPv6 forwarding
echo "net.ipv6.conf.all.forwarding = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Generate Client And Server Keys
sudo mkdir -p /etc/wireguard
# Generate Server Keys
wg genkey | sudo tee /etc/wireguard/server_private.key
sudo chmod 600 /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
# Generate Client Keys
wg genkey | sudo tee /etc/wireguard/client_private.key
sudo cat /etc/wireguard/client_private.key | wg pubkey | sudo tee /etc/wireguard/client_public.key
# Create Server Configuration
SERVER_PRIVATE_KEY=$(sudo cat /etc/wireguard/server_private.key)
CLIENT_PUBLIC_KEY=$(sudo cat /etc/wireguard/client_public.key)
cat << EOF | sudo tee /etc/wireguard/wg0.conf
[Interface]
PrivateKey = ${SERVER_PRIVATE_KEY}
Address = 10.8.0.1/24, fd00:1234:5678:9abc::1/64
ListenPort = 51820
PostUp = iptables -t nat -I POSTROUTING -o ens5 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o ens5 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
PostDown = ip6tables -t nat -D POSTROUTING -o ens5 -j MASQUERADE
[Peer]
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = 10.8.0.2/32, fd00:1234:5678:9abc::2/64
EOF
# Start WireGuard
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
# Create Client Configuration
SERVER_PUBLIC_KEY=$(sudo cat /etc/wireguard/server_public.key)
CLIENT_PRIVATE_KEY=$(sudo cat /etc/wireguard/client_private.key)
SERVER_IPV6=$(ip -6 addr show dev ens5 | grep -oP '(?<=inet6 )([0-9a-f:]+)' | head -1)
# Or use IPv4 if your client doesn't have Ipv6 network
# SERVER_IPV4=$(curl checkip.amazonaws.com)
cat << EOF | sudo tee /etc/wireguard/client.conf
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = 10.8.0.2/32, fd00:1234:5678:9abc::2/64
DNS = 1.1.1.1, 2606:4700:4700::1111
[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
# Or use IPV4 address if your client doesn't support IPv6
Endpoint = [$SERVER_IPV6]:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
# Generate QR code
echo "Generating QR code..."
sudo cat /etc/wireguard/client.conf | qrencode -t ansiutf8
echo "Current WireGuard status:"
sudo wg show
Using the VPN
For Desktop Clients
Install WireGuard client for your operating system:
- Windows: WireGuard Windows Client
- macOS: WireGuard macOS Client
- Linux:
sudo apt install wireguard
(Ubuntu/Debian)
Copy the contents of
/etc/wireguard/client.conf
to your client machineImport the configuration into your WireGuard client
Enable the VPN connection
For Mobile Clients
Install WireGuard app:
Scan the QR code displayed in
sudo cat /etc/wireguard/client.conf | qrencode -t ansiutf8
Enable the VPN connection
Adding Additional Clients
To add more clients, run these commands on the server:
#!/bin/bash
# Generate keys for the new client
CLIENT_NUM=2 # Change this number for each new client
sudo wg genkey | sudo tee "/etc/wireguard/client${CLIENT_NUM}_private.key"
sudo cat "/etc/wireguard/client${CLIENT_NUM}_private.key" | wg pubkey | sudo tee "/etc/wireguard/client${CLIENT_NUM}_public.key"
# Get the keys and server info
NEW_CLIENT_PRIVATE_KEY=$(sudo cat "/etc/wireguard/client${CLIENT_NUM}_private.key")
NEW_CLIENT_PUBLIC_KEY=$(sudo cat "/etc/wireguard/client${CLIENT_NUM}_public.key")
SERVER_PUBLIC_KEY=$(sudo cat /etc/wireguard/server_public.key)
SERVER_IPV6=$(ip -6 addr show dev ens5 | grep -oP '(?<=inet6 )([0-9a-f:]+)' | head -1)
# Add peer to server config
sudo tee -a /etc/wireguard/wg0.conf << EOF
[Peer]
PublicKey = ${NEW_CLIENT_PUBLIC_KEY}
AllowedIPs = 10.8.0.$((CLIENT_NUM + 1))/32, fd00:1234:5678:9abc::$((CLIENT_NUM + 1))/64
EOF
# Create client config
cat << EOF | sudo tee "/etc/wireguard/client${CLIENT_NUM}.conf"
[Interface]
PrivateKey = ${NEW_CLIENT_PRIVATE_KEY}
Address = 10.8.0.$((CLIENT_NUM + 1))/32, fd00:1234:5678:9abc::$((CLIENT_NUM + 1))/64
DNS = 1.1.1.1, 1.0.0.1
[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
Endpoint = [$SERVER_IPV6]:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
# Generate QR code for the new client
sudo cat "/etc/wireguard/client${CLIENT_NUM}.conf" | qrencode -t ansiutf8
# Restart WireGuard to apply changes
sudo systemctl restart wg-quick@wg0
echo "New client configuration created:"
echo "Config file: /etc/wireguard/client${CLIENT_NUM}.conf"
Troubleshooting
If you encounter issues, check:
- WireGuard service status:
sudo systemctl status wg-quick@wg0
- Logs:
sudo journalctl -xeu wg-quick@wg0
- Interface status:
sudo wg show
ip addr show wg0
- IP forwarding status:
sysctl net.ipv4.ip_forward
sysctl net.ipv6.conf.all.forwarding