Wireguard
This is the official WireGuard quickstart setup in a GNS3 netlab. Both linux WireGuard peers are connected using a routed IP connection via encrypted interface wg0. The IP addressing on both peers interface eth0 represent public IP addressing.
In the final step procedure, both linux peers automatically start up the wg0 tunnel connection at boot up sequence, and stop the service when the related wg0 interface has been stopped due to a system shutdown or manually reset as system root user. The procedure explained in this blog entry uses 2 linux system commands only, to configure a secure connection:
wgip
Topology
IP addressing
| hostname | eth0 | wg0 |
|---|---|---|
| peerA | 192.0.2.10/24 |
10.0.0.0/31 |
| peerB | 198.51.100.10/24 |
10.0.0.1/31 |
State
Traceroute mtr command from peerA to peerB in the current state of network, before interface wg0 is created on both peers.
My traceroute [v0.96]
peerA (192.0.2.10) -> 198.51.100.10 (198.51.100.10) 2026-01-21T12:51:42+0100
Keys: Help Display mode Restart statistics Order of fields quit
Packets Pings
Host Loss% Snt Last Avg Best Wrst StDev
1. 192.0.2.1 0.0% 5 1.1 1.2 1.1 1.3 0.1
2. 10.100.0.0 0.0% 4 1.9 1.9 1.7 2.1 0.2
3. 198.51.100.10 0.0% 4 2.2 2.3 2.2 2.4 0.3
The full setup is explained if you
Configuration
- Create public/private keys
- Setup interface
wg0 - Setup site-to-site VPN using P-t-P link
- Packet capture verification
- Create
wg0system service
Create a private/public key
On both peers create the encryption related file using the wg genkey system command:
peerA
Use the umask 077 prior to generating the wg0 key, this sets the correct file settings on key file creation:
Generate the key
Show the key
Ready.
peerB
It the umask 077 command has not been set, before using wg genkey command, following Warning appear:
Warning: writing to world accessible file. Consider setting the umask to 077 and trying again.
Once again, the same warning as above but this time in red:
Writing to world accessible file. Consider setting the umask to 077 and trying again.
Fix the Warning shown above by using chmod command on the file:
Verify the current setting are set to 600
Show the key
Create a wg0 interface
Of course assign a static IP address to the wg0 interface too. This example will be using a /31 ip addressing. See Using 31-Bit Prefixes on IPv4 Point-to-Point Links. Since only 2 Peers will have a secure VPN session, this IP prefix is perfect to that use-scenario, a Point-to-Point IP Link.
Any IP network size can be configured for the
wg0 interface like f.e: /24 or /29. In this example a /31 IP prefix size is used for the encrypted connection.This example below shows how to take sure, on the IPv4 level, that only 2 participants can communicate using encrypted protocols by using this specific RFC3021 IPv4 prefix /31. Connecting another 3-rd participant, by configuration mistake is excluded by using the right connection prefix for the VPN communication, peer-to-peer only.
peerA
linux IP state before activating interface wg0
default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 1002 192.0.2.0/24 dev eth0 proto dhcp scope link src 192.0.2.10 metric 1002
Activate the wg0 interface
Verify linux IP state, note that setting the wg0 link to UP, installed the configured P-t-P IP link into the ruoting table:
default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 1002 10.0.0.0/31 dev wg0 proto kernel scope link src 10.0.0.0 192.0.2.0/24 dev eth0 proto dhcp scope link src 192.0.2.10 metric 1002
peerB
At this point the wg0 interface is ACTIVE and UP, but still not operative or working.
Verify state
Commands to verify the current networking state:
peerA
Show the operational state of the wg0 interface:
interface: wg0 public key: UmoVma63KHqrn4lmebpCf77eDt0rKqzM0HKLWkQhA00= private key: (hidden) listening port: 51001
peerB
Show the operational state of the wg0 interface:
interface: wg0 public key: B3g2vEnmcWhBDfpN11th8YbFKWCIr9AuxaBy2OZR1TE= private key: (hidden) listening port: 51002
setup VPN site-to-site P-t-P
This is the a site-to-site VPN, only 2 peers involved. Notify, that the allowed peer has a /32 host netmask in the below command:
peerA
peerB
Now the wg0 interface tunnel connection is established.
Verify connectivity again using the wg command shows the peers and the current state:
interface: wg0 public key: UmoVma63KHqrn4lmebpCf77eDt0rKqzM0HKLWkQhA00= private key: (hidden) listening port: 51001 peer: B3g2vEnmcWhBDfpN11th8YbFKWCIr9AuxaBy2OZR1TE= endpoint: 198.51.100.10:51002 allowed ips: 10.0.0.0/32 latest handshake: 22 seconds ago transfer: 348 B received, 436 B sent
Use the wg show all dump command to receive the above output in more compact manner:
wg0 kHbnpVxsV6N3woOwn5MyJ9hJIFskeCXBFMzV+Y9cJ2I= UmoVma63KHqrn4lmebpCf77eDt0rKqzM0HKLWkQhA00= 51001 off wg0 B3g2vEnmcWhBDfpN11th8YbFKWCIr9AuxaBy2OZR1TE= (none) 198.51.100.10:51002 10.0.0.1/32 0 0 0 off
Check interface wg0 and the configured subnet 10.0.0.0/31 is in the kernel IP routing table
default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 1002 10.0.0.0/31 dev wg0 proto kernel scope link src 10.0.0.0 192.0.2.0/24 dev eth0 proto dhcp scope link src 192.0.2.10 metric 1002
Make the ICMP test from peerA to directly connected peerB's IP interface:
PING 10.0.0.1 (10.0.0.1): 56 data bytes 64 bytes from 10.0.0.1: seq=0 ttl=64 time=1.848 ms 64 bytes from 10.0.0.1: seq=1 ttl=64 time=1.946 ms --- 10.0.0.1 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 1.848/1.897/1.946 ms
IP packet caputure
Create packet caputure using tshark or wireshark.
ICMP packet capture
Ping command sent from peerA to peerB
- source IP 192.0.2.10
- dest IP 198.51.100.10
Command
Packet capture
tshark -r peerA-peerB_ICMP_connectivity.pcapng | grep ICMP
9 15.289637 192.0.2.10 → 198.51.100.10 ICMP 98 Echo (ping) request id=0x205c, seq=0/0, ttl=63
10 15.290465 198.51.100.10 → 192.0.2.10 ICMP 98 Echo (ping) reply id=0x205c, seq=0/0, ttl=63 (request in 9)
11 16.290107 192.0.2.10 → 198.51.100.10 ICMP 98 Echo (ping) request id=0x205c, seq=1/256, ttl=63
12 16.290992 198.51.100.10 → 192.0.2.10 ICMP 98 Echo (ping) reply id=0x205c, seq=1/256, ttl=63 (request in 11)
13 17.290617 192.0.2.10 → 198.51.100.10 ICMP 98 Echo (ping) request id=0x205c, seq=2/512, ttl=63
14 17.291597 198.51.100.10 → 192.0.2.10 ICMP 98 Echo (ping) reply id=0x205c, seq=2/512, ttl=63 (request in 13)
15 18.291135 192.0.2.10 → 198.51.100.10 ICMP 98 Echo (ping) request id=0x205c, seq=3/768, ttl=63
16 18.291884 198.51.100.10 → 192.0.2.10 ICMP 98 Echo (ping) reply id=0x205c, seq=3/768, ttl=63 (request in 15)
18 19.291636 192.0.2.10 → 198.51.100.10 ICMP 98 Echo (ping) request id=0x205c, seq=4/1024, ttl=63
19 19.292445 198.51.100.10 → 192.0.2.10 ICMP 98 Echo (ping) reply id=0x205c, seq=4/1024, ttl=63 (request in 18)
WireGuard ICMP packet capture
This is the same ping command as above from peerA to peerB when wireguard is active.
- source IP 10.0.0.0
- destination IP 10.0.0.1
Command:
Output:
tshark -r peerA-peerB_ICMP_wg0_connectivity.pcapng | rg -i wire
8 12.799917 192.0.2.10 → 198.51.100.10 WireGuard 190 Handshake Initiation, sender=0xEBBA41F1
9 12.801505 198.51.100.10 → 192.0.2.10 WireGuard 134 Handshake Response, sender=0x7F463EDD, receiver=0xEBBA41F1
10 12.802548 192.0.2.10 → 198.51.100.10 WireGuard 170 Transport Data, receiver=0x7F463EDD, counter=0, datalen=96
11 12.803425 198.51.100.10 → 192.0.2.10 WireGuard 170 Transport Data, receiver=0xEBBA41F1, counter=0, datalen=96
12 13.799941 192.0.2.10 → 198.51.100.10 WireGuard 170 Transport Data, receiver=0x7F463EDD, counter=1, datalen=96
13 13.801002 198.51.100.10 → 192.0.2.10 WireGuard 170 Transport Data, receiver=0xEBBA41F1, counter=1, datalen=96
14 14.800166 192.0.2.10 → 198.51.100.10 WireGuard 170 Transport Data, receiver=0x7F463EDD, counter=2, datalen=96
15 14.801295 198.51.100.10 → 192.0.2.10 WireGuard 170 Transport Data, receiver=0xEBBA41F1, counter=2, datalen=96
16 15.800456 192.0.2.10 → 198.51.100.10 WireGuard 170 Transport Data, receiver=0x7F463EDD, counter=3, datalen=96
17 15.801539 198.51.100.10 → 192.0.2.10 WireGuard 170 Transport Data, receiver=0xEBBA41F1, counter=3, datalen=96
25 26.101463 192.0.2.10 → 198.51.100.10 WireGuard 74 Keepalive, receiver=0x7F463EDD, counter=4
The first 2 WireGuard packets are Handshake, both side initiate a encrypted communication, key verification. After the Handshake the data transport begins, encrypted. Last packet is a WireGuard Keepalive packet, send from source IP 192.0.2.10. The initiator of the encrypted communication. The ICMP packets has been sent from peerA.
Verify wg0 MTU
Notice, using WireGuard lowers the MTU to 1420 for transport. This is the IP MTU size set on the wg0 interface.
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.0.0.0/31 scope global wg0
valid_lft forever preferred_lft forever
service wg0
On a alpinelinux use following commands, configure local static port 51001 on peerA using open-rc
This command will read the /etc/local.d/ directory and on start execute all *.start scripts. Likewise using the stop will execute all *.stop scripts in that directory.
peerA
Create a local.d wg0.start script:
cat <<EOF > /etc/local.d/00_wg0.start
#!/bin/sh
ip link add dev wg0 type wireguard
ip address add 10.0.0.0/31 dev wg0
wg set wg0 private-key /root/private listen-port 51001
wg set wg0 peer B3g2vEnmcWhBDfpN11th8YbFKWCIr9AuxaBy2OZR1TE= allowed-ips 10.0.0.1/32 endpoint 198.51.100.10:51002
ip link set wg0 up
EOF
Create a local.d wg0.stop script:
cat <<EOF > /etc/local.d/00_wg0.stop
ip link set wg0 down
ip link del dev wg0
EOF
Change the created files be executable:
Restart the daemon
peerB
on peerB configure the local port to static 51002, the far end hosts peerA uses destination port 51001
cat <<EOF > /etc/local.d/00_wg0.start
#!/bin/sh
ip link add dev wg0 type wireguard
ip address add 10.0.0.1/31 dev wg0
wg set wg0 private-key /root/private listen-port 51002
wg set wg0 peer UmoVma63KHqrn4lmebpCf77eDt0rKqzM0HKLWkQhA00= allowed-ips 10.0.0.0/32 endpoint 192.0.2.10:51001
ip link set wg0 up
EOF
Create a wg0 stop script:
cat <<EOF > /etc/local.d/00_wg0.stop
ip link set wg0 down
ip link del dev wg0
EOF
Change both files to be have the execute bit set:
Restart the daemon
Summary
wireguard as a system tool is quickly setup, loads its own kernel module. It does not need additional big software suite to install. And is configured on the command line only if used vanilla. If you rely on ad-hoc setup of encrypted connections then wg is the right tool for it.
