Testing WireGuard Vpn
2 june 2025
Testing wireguard vpn with an aws EC2 instance and a raspberry-pi.
Required : an aws account, a pi, time
Architecture used for the test
1 server with some client(s).
The server is a EC2 instance t2 micro with 1 vcpu and 1 GIB of ram and 1 EBS volume of 8Gb on ssd.
You can deploy the instance with the web console or with terraform or OpenTofu (opentofu.org) or with python by using
Aws SDK for python, Aws Boto3.
You can look at an example of python and Aws Boto3 script at the bottom of this page.
If you are using an EC2 instance on aws you can use the default security group and a create a new Key Pair or use
an existing one. If you chose to use an existing one make sure to select them during the launch instances process
or in your terraform/OpenTofu or python script.
For raspibian check the page https://www.raspberrypi.com/software/ or
https://www.raspberrypi.com/software/operating-systems/
OS : Debian 12 for server. AWS provide an AMI with Debian 12 (HVM). The default user is admin.
the last version of rapsibian for the client
Firewall UFW :
If you are using a firewall on the server, we have to open the used udp port for the client :
In this example we use the udp port 51800 and is open.
The command use to open the udp port with UFW is
ufw allow 51800/udp
make sure to reload the rule with the command ufw reload
than use ufw status to make sure the port is open
root@vm:/etc/wireguard# ufw status
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
51800/udp ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
51800/udp (v6) ALLOW Anywhere (v6)
root@vm:/etc/wireguard#
You can also check the udp port is really open with nmap :
nmap -sU -p 51800 IPSRVWIREGUARD
If the rule as not be applied run this command :
sudo ufw disable
sudo ufw enable
and check again if this step is not complete the client cannot reach the server.
If you want to remove a particular rule in ufw list use this command ufw status numbered
to list rules with number :
root@vm:/etc/wireguard# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 51800/udp ALLOW IN Anywhere
[ 3] 22/tcp (v6) ALLOW IN Anywhere (v6)
[ 4] 51800/udp (v6) ALLOW IN Anywhere (v6)
root@vm:/etc/wireguard# ufw delete 4
Deleting:
allow 51800/udp
Proceed with operation (y|n)? y
Rule deleted (v6)
root@vm:/etc/wireguard# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 51800/udp ALLOW IN Anywhere
[ 3] 22/tcp (v6) ALLOW IN Anywhere (v6)
root@vm:/etc/wireguard#
ANSIBLE :
Ansible is used to update server and raspibian and install required packages.
the playbook for the update/upgrade for the server and the raspberrypi is really basic :
Playbook for the updates :
---
- name: update vmtmp
hosts: all
become: true
vars:
ansible_python_interpreter: /usr/bin/python3
tasks:
- debug: var=ansible_host
- name: Install Updates for linux distro like debian Ubuntu Linux Mint or Rocky Linux
dnf:
update_only: yes
update_cache: yes
when: ansible_distribution == "Rocky"
- name: Update apt-get repo and cache
package: update_cache=yes force_apt_get=yes cache_valid_time=300
when: ansible_distribution in ["Linux Mint", "Debian", "Ubuntu"]
- name: Update all packages to their latest version
package: upgrade=dist force_apt_get=yes
when: ansible_distribution in ["Linux Mint", "Debian", "Ubuntu"]
- name: Autoremove no longer required package
package: autoremove=yes
when: ansible_distribution in ["Linux Mint", "Debian", "Ubuntu"]
- name: Check if a reboot is needed for Debian and Ubuntu boxes
ansible.builtin.stat:
path: /var/run/reboot-required
get_checksum: no
register: reboot_required_file
changed_when: reboot_required_file.stat.exists
- debug:
var: reboot_required_file.stat.exists
- name: Reboot the server if required
ansible.builtin.reboot:
when: reboot_required_file.stat.exists == true
Playbook for installing the wireguard packages on debian based distro :
---
- name: Install Wireguard
hosts: all
become: true
vars:
ansible_python_interpreter: /usr/bin/python3
tasks:
- debug: var=ansible_host
# update repo cache and install packages
- name: Update repo cache
ansible.builtin.apt:
update_cache: yes
- name: install packages for wireguard
ansible.builtin.apt:
pkg:
- wireguard
- wireguard-tools
Please pay attention playbook use yaml format, html as possibly altered the spacing
How to Update/upgrade without Ansible :
on server and client simply run the apt command :
sudo apt update
sudo apt upgrade
Reboot if necessary
sudo reboot
How to install if you don't use Ansible :
- For Debian like distro :
on linux debian like distro use the command :
sudo apt install wireguard wireguard-tools
for the server and raspibian client
- For other distro :
Other distro can use dnf snap rpm yum or check the manual of your distro to know how to install a package.
Like this for dnf :
dnf install wireguard-tools
INSTALL SERVER :
- UPDATE THE SERVER :
- fisrt make sure the server is up to date with apt update, apt upgrade and reboot if necessary
or use ansibe and the playabook to do the update.
If you are using ansible the server must be add in the ansible inventory file with all info and sudo
required configuration, for debian AMI the default user is admin for example a EC2 instance in aws :
[aws]
srv-aws ansible_host=publicipinstance ansible_user=admin
the user can sudo without password
- run the playbook for an EC2 instance in aws (the private-key is the generated in aws console) :
ansible-playbook update_os.yml -l srv-aws --private-key ~/.ssh/xxxxxx.pem
or without Ansible
- use apt with this command (with sudo or as root) :
sudo apt update
than
sudo apt upgrade
reboot if required
sudo reboot
- INSTALL WIREGUARD :
With ansible run the playbook : install_wireguard_debian.yml
- If EC2 instance in aws :
ansible-playbook install_wireguard_debian.yml -l srv-aws --private-key ~/.ssh/xxxxxx.pem
or without Ansible
- use apt with this command :
sudo apt install wireguard wireguard-tools
In both case he should install wireguard and wireguard-tools
You can check if you have the directory /etc/wireguard/ if not as root you can create the directory
INSTALL ON THE CLIENT :
- UPDATE THE CLIENT :
- fisrt make sure the client is up to date with apt update, apt upgrade and reboot if necessary
or use ansible and the playabook.
If you are using ansible the client must be add in the ansible inventory file with all info and sudo
required configuration , for example a local pi client :
[myclienttest]
client-wg-client ansible_host=iphost ansible_user=theuser
the user can sudo with password
- use ansible playbook
ansible-playbook update_os.yml -l clientnameourip --ask-pass
or
- use apt with this command (with sudo or as root) :
apt update
than
apt upgrade
reboot if required
sudo reboot
On a PI 4 this step can be really long.
- INSTALL WIREGUARD :
- use ansible playbook
ansible-playbook install_wireguard_debian.yml -l clientnameourip --ask-pass
or
- use apt with this command :
sudo apt install wireguard wireguard-tools
In both case he should install wireguard and wireguard-tools
You can check if you have the directory /etc/wireguard/ if not as root you can create the directory
CONFIGURE WIREGUARD ON SERVER :
- CREATE THE PRIVATE AND PUBLIC KEY ON SERVER :
On server generate the private and public key by running this command as root in /etc/wireguard :
wg genkey |tee privatekey |wg pubkey > publickey
you must have this result on the server :
root@ip:/etc/wireguard#
root@ip:/etc/wireguard# wg genkey |tee privatekey |wg pubkey > publickey
root@ip:/etc/wireguard# ls -la
total 16
drwx------ 2 root root 4096 Apr 25 16:11 .
drwxr-xr-x 65 root root 4096 Apr 25 15:40 ..
-rw-r--r-- 1 root root 45 Apr 25 16:11 privatekey
-rw-r--r-- 1 root root 45 Apr 25 16:11 publickey
root@ip:/etc/wireguard#
REMEMBER NEVER EVER SHARE OR SHOW THE PRIVATE KEY and set permission to remove any permissions
on the files for users and groups other than root to ensure that only root can acces the private key.
sudo chmod go= privatekey publickey
- CREATE THE WG INTERFACE ON SERVER :
as root (sudo) create a file under /etc/wireguard/wg0.conf
add the interface info
Make sure to use the correct network interface not just eth0
you can check the interfce name with ip route list table main default
[interface]
PrivateKey=theprivatekeypreviuoslycreatedwithwggenkey
Address=10.0.0.2/24
SaveConfig=true
# iptables must be available
# add iptables required rules to allow traffic on wg0 and the network interface could be other than eth0
PostUp=iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;
# remove iptables rules when vpn is stopped
PostDown=iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE;
ListenPort=443 #or any other port like default wireguard port 51820
When done start the wg inerface
wg-quick up wg0
return something like this on aws ec2
[root@ip]# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1/24 dev wg0
[#] ip link set mtu 8921 up dev wg0
[root@ip]#
check result with wg command and ifconfig or ip a or ip -brief a command
[root@ip wireguard]# ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enX0: mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 06:bd:05:55:0a:69 brd ff:ff:ff:ff:ff:ff
altname eni-079431efaba15e462
altname device-number-0.0
inet 172.31.9.232/20 metric 512 brd 172.31.15.255 scope global dynamic enX0
valid_lft 2461sec preferred_lft 2461sec
inet6 fe80::4bd:5ff:fe55:a69/64 scope link
valid_lft forever preferred_lft forever
5: wg0: mtu 8921 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.0.0.1/24 scope global wg0
valid_lft forever preferred_lft forever
[root@ip wireguard]#
- CREATE THE PRIVATE AND PUBLIC KEY ON CLIENT :
On client generate the private and public key by running this command as root in /etc/wireguard :
wg genkey |tee privatekey |wg pubkey > publickey
you must have this result on the client :
root@testpidev:/etc/wireguard#
root@testpidev:/etc/wireguard# wg genkey |tee privatekey |wg pubkey > publickey
root@testpidev:/etc/wireguard# ls -la
total 24
drwx------ 2 root root 4096 Apr 25 12:27 .
drwxr-xr-x 137 root root 12288 Apr 25 12:07 ..
-rw-r--r-- 1 root root 45 Apr 25 12:27 privatekey
-rw-r--r-- 1 root root 45 Apr 25 12:27 publickey
root@testpidev:/etc/wireguard#
REMEMBER NEVER EVER SHARE OR SHOW THE PRIVATE KEY and set permission to remove any permissions
on the files for users and groups other than root to ensure that only root can acces the private key.
sudo chmod go= privatekey publickey
- CREATE THE WG INTERFACE ON CLIENT :
as root (sudo) create a file uder /etc/wireguard/wg0.conf
[interface]
PrivateKey=sdsdfasdfasdfadsfasdfasdfadsf
Address=10.0.0.3/24
SaveConfig=true
[Peer]
# publickey of the public interface of the server
PublicKey=kljsalfdkjga;gj;alksfgl;akfsg
# endpoint is wireguard vpn server public ip check on the server
# and the port number set in the server like 443 or 51820
Endpoint=xxx.xxx.xxx.xxx:port
# specify the ip or all traffic, in this case we send all traffic to the server even web traffic
# because we need to control this client even update. Web traffic can be routed via the server with ip-forwarding
# set to 1 if it's really necessary
AllowedIPs=0.0.0.0/0
PersistentKeepalive=20
start the interface as root with the command wg-quick up wg0
we should see something like this on the client :
root@raspberrypi:/etc/wireguard# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
[#] nft -f /dev/fd/63
root@raspberrypi:/etc/wireguard#
check the status of the interface with sudo wg
root@raspberrypi:/etc/wireguard# wg
interface: wg0
public key: SlPQ1Bn1kZl9L/yZPLgx6yRcGTM6CYedQw1fNzycyAc=
private key: (hidden)
listening port: 50047
fwmark: 0xca6c
peer: OfCL3SxQkrFxvz8OXflTWGFBM6iyaIvK525XqS4mUm8=
endpoint: 3.99.221.82:443
allowed ips: 0.0.0.0/0
transfer: 0 B received, 296 B sent
persistent keepalive: every 20 seconds
root@raspberrypi:/etc/wireguard#
As root or with sudo, now we must copy the publickey of this client into the server with this command :
wg set wg0 peer keykeykeyekeykeykeykeykeykeykey allowed-ips 10.0.0.2/24
on the client check if the connection is available
root@raspberrypi:/etc/wireguard# wg
interface: wg0
public key: SlPQ1Bn1kZl9L/yZPLgx6yRcGTM6CYedQw1fNzycyAc=
private key: (hidden)
listening port: 50047
fwmark: 0xca6c
peer: OfCL3SxQkrFxvz8OXflTWGFBM6iyaIvK525XqS4mUm8=
endpoint: 3.99.221.82:443
allowed ips: 0.0.0.0/0
latest handshake: 5 seconds ago
transfer: 4.34 KiB received, 9.51 KiB sent
persistent keepalive: every 20 seconds
root@raspberrypi:/etc/wireguard#
root@raspberrypi:/etc/wireguard# ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=127 time=17.8 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=127 time=15.9 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=127 time=25.3 ms
--- 10.0.0.1 ping statistics ---
4 packets transmitted, 3 received, 25% packet loss, time 3005ms
rtt min/avg/max/mdev = 15.850/19.638/25.281/4.067 ms
root@raspberrypi:/etc/wireguard#
Stop - start the wg0 interface on the client and server manually :
as root (sudo)
wg-quick down wg0
wg-quick up wg0
or with systemctl to get your connection to the server at reboot
root@dev:/etc/wireguard# systemctl enable wg-quick@wg0.service
Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick@.service.
root@dev:/etc/wireguard#
Test if port is open with nmap :
nmap -sU -p 51820 WGIPSRV
AWS SDK Boto3 simple script example to create an EC2 instance :
import boto3
import boto3.session
import sys
import datetime
from datetime import datetime
# if you have different env in your aws config file. ARG to get env DEV or PROD
print('DEBUG number of arg :', len(sys.argv), 'args')
print('Arg list is :', str(sys.argv))
print('Arg0 list is :', str(sys.argv[0]))
# THE REGION
AWS_REGION = "ca-central-1"
# Specify the parameters for the instance
image_id = 'ami-xxxxxxxxxxxxx' # AMI ID for Debian 12 (HVM), SSD Volume Type
instance_type = 't2.micro'
key_name = 'key-pair' # key pair name used for the test
security_group_ids = ['sg-asecuritygroup'] # my sg with only ssh https from my ip
monitoring_status = {'Enabled': False}
instance_name = 'srv_name'
iops = 3000 # int
vol_Size = 8 # int
vol_Type = 'gp3'
Thput = 125 # int
ec2 = boto3.resource('ec2', region_name=AWS_REGION)
instances = ec2.create_instances(
BlockDeviceMappings=[
{
'Ebs': {
'DeleteOnTermination': True,
'Iops': iops,
'VolumeSize': vol_Size,
'VolumeType': vol_Type,
'Throughput': Thput,
'Encrypted': False,
},
'DeviceName': '/dev/xvda',
}
],
ImageId=image_id,
MinCount=1,
MaxCount=1,
InstanceType=instance_type,
KeyName=key_name,
SecurityGroupIds=security_group_ids,
Monitoring=monitoring_status,
TagSpecifications=[
{
'ResourceType': 'instance',
'Tags': [
{
'Key': 'Name',
'Value': instance_name
}
]
}
]
)
# Get the instance ID
print(instances)