In our previous post How to automatically re-resolve DNS in Wireguard on Linux we explored how to use the reresolve-dns.sh
script that is included with wireguard-tools on Ubuntu to re-resolve DNS entries in wireguard config files.
In our previous post How to automatically re-resolve DNS in Wireguard on Linux we explored how to use the reresolve-dns.sh
script that is included with wireguard-tools on Ubuntu to re-resolve DNS entries in wireguard config files.
If you have a Wireguard config file such as /etc/wireguard/wghome.conf
, which you can start manually using wg-quick up wghome
, this is how you autostart it on boot. Thanks to Justin Ludwig on Serverfault for the template for that init script
Create /etc/init.d/wg-quick-wghome
:
#!/sbin/openrc-run description="wg-quick wghome" depend() { need net need localmount } start() { wg-quick up schlaftier } stop() { wg-quick down schlaftier }
Then make it executable:
chmod a+x /etc/init.d/wg-quick-wghome
and enable it to start on boot:
rc-update add wg-quick-wghome default
and start it right now if desired:
/etc/init.d/wg-quick-wghome start
Of course you can add multiple scripts like this. Just ensure to name them differently and perform all the steps required to enable startup on boot.
While trying to install wg-quick
on Alpine Linux, you see the following error message:
ERROR: unable to select packages: wg-quick (no such package): required by: world[wg-quick]
You need to install the wireguard-tools
package which also contains wg-quick
:
apk add wireguard-tools
Just run these commands in your Shell in Ubuntu to install netclient
curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list sudo apt update sudo apt install netclient
Source: Netmaker installation page
In our setup, a virtual machine (running on an XCP-NG host) on was connected to my Desktop (HP Z240, i7-6700 @3.4 GHz running Ubuntu 22.04) in a purely switched network with 1Gbit links. Both devices were connected using a MikroTik 10G switch (Marvell chip
I ran iperf3 -s
on the VM and ran iperf3 -c [IP address]
on the desktop. Reverse tests have not been performed.
Connecting to host 10.9.2.103, port 5201 [ 5] local 10.9.2.10 port 56848 connected to 10.9.2.103 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 92.8 MBytes 779 Mbits/sec 0 444 KBytes [ 5] 1.00-2.00 sec 90.7 MBytes 761 Mbits/sec 0 543 KBytes [ 5] 2.00-3.00 sec 88.6 MBytes 743 Mbits/sec 0 816 KBytes [ 5] 3.00-4.00 sec 90.0 MBytes 755 Mbits/sec 0 816 KBytes [ 5] 4.00-5.00 sec 90.0 MBytes 755 Mbits/sec 0 856 KBytes [ 5] 5.00-6.00 sec 88.8 MBytes 744 Mbits/sec 0 946 KBytes [ 5] 6.00-7.00 sec 88.8 MBytes 745 Mbits/sec 0 946 KBytes [ 5] 7.00-8.00 sec 90.0 MBytes 755 Mbits/sec 0 993 KBytes [ 5] 8.00-9.00 sec 90.0 MBytes 755 Mbits/sec 0 993 KBytes [ 5] 9.00-10.00 sec 88.8 MBytes 744 Mbits/sec 0 993 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 898 MBytes 754 Mbits/sec 0 sender [ 5] 0.00-10.01 sec 896 MBytes 751 Mbits/sec receiver
Connecting to host 10.80.246.34, port 5201 [ 5] local 10.80.246.38 port 35474 connected to 10.80.246.34 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 59.9 MBytes 503 Mbits/sec 338 102 KBytes [ 5] 1.00-2.00 sec 60.2 MBytes 505 Mbits/sec 313 188 KBytes [ 5] 2.00-3.00 sec 63.9 MBytes 536 Mbits/sec 176 99.3 KBytes [ 5] 3.00-4.00 sec 74.3 MBytes 623 Mbits/sec 174 113 KBytes [ 5] 4.00-5.00 sec 67.7 MBytes 568 Mbits/sec 197 83.2 KBytes [ 5] 5.00-6.00 sec 72.5 MBytes 609 Mbits/sec 218 228 KBytes [ 5] 6.00-7.00 sec 61.3 MBytes 514 Mbits/sec 281 77.8 KBytes [ 5] 7.00-8.00 sec 72.0 MBytes 604 Mbits/sec 213 91.2 KBytes [ 5] 8.00-9.00 sec 65.4 MBytes 549 Mbits/sec 309 156 KBytes [ 5] 9.00-10.00 sec 53.9 MBytes 453 Mbits/sec 190 121 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 651 MBytes 546 Mbits/sec 2409 sender [ 5] 0.00-10.01 sec 650 MBytes 545 Mbits/sec receiver
Netmaker internally uses a normal (kernel-based) wireguard connection, so in some respect this is a test of Wireguard performance
Connecting to host 10.230.113.3, port 5201 [ 5] local 10.230.113.1 port 35534 connected to 10.230.113.3 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 105 MBytes 881 Mbits/sec 0 1.01 MBytes [ 5] 1.00-2.00 sec 104 MBytes 870 Mbits/sec 86 422 KBytes [ 5] 2.00-3.00 sec 101 MBytes 849 Mbits/sec 0 488 KBytes [ 5] 3.00-4.00 sec 98.8 MBytes 828 Mbits/sec 0 535 KBytes [ 5] 4.00-5.00 sec 98.8 MBytes 828 Mbits/sec 0 584 KBytes [ 5] 5.00-6.00 sec 104 MBytes 870 Mbits/sec 0 615 KBytes [ 5] 6.00-7.00 sec 97.5 MBytes 818 Mbits/sec 7 472 KBytes [ 5] 7.00-8.00 sec 104 MBytes 870 Mbits/sec 0 522 KBytes [ 5] 8.00-9.00 sec 101 MBytes 849 Mbits/sec 0 580 KBytes [ 5] 9.00-10.00 sec 102 MBytes 860 Mbits/sec 0 606 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 1016 MBytes 852 Mbits/sec 93 sender [ 5] 0.00-10.00 sec 1014 MBytes 850 Mbits/sec receiver
Tailscale 1.28.0 has been used for this test.
During this test, I ensured that the tailscale connection was established using the switched network and was not going through a DERP server or the routed network.
$ tailscale ping 100.64.0.3 pong from vm (fd5d:7b60:4742::3) via 10.9.2.103:41641 in 1ms
Results:
Connecting to host 100.64.0.3, port 5201 [ 5] local 100.64.0.2 port 40690 connected to 100.64.0.3 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 38.3 MBytes 321 Mbits/sec 389 60.0 KBytes [ 5] 1.00-2.00 sec 37.6 MBytes 315 Mbits/sec 366 43.2 KBytes [ 5] 2.00-3.00 sec 36.7 MBytes 308 Mbits/sec 431 52.8 KBytes [ 5] 3.00-4.00 sec 38.5 MBytes 323 Mbits/sec 488 80.3 KBytes [ 5] 4.00-5.00 sec 29.3 MBytes 246 Mbits/sec 356 38.4 KBytes [ 5] 5.00-6.00 sec 31.0 MBytes 260 Mbits/sec 351 86.3 KBytes [ 5] 6.00-7.00 sec 27.1 MBytes 227 Mbits/sec 287 50.4 KBytes [ 5] 7.00-8.00 sec 26.1 MBytes 219 Mbits/sec 210 46.8 KBytes [ 5] 8.00-9.00 sec 27.1 MBytes 227 Mbits/sec 261 39.6 KBytes [ 5] 9.00-10.00 sec 27.5 MBytes 231 Mbits/sec 222 40.8 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 319 MBytes 268 Mbits/sec 3361 sender [ 5] 0.00-10.01 sec 318 MBytes 267 Mbits/sec receiver
The approximate performance expectation in this specific scenario is:
300 Mbit/s
550 Mbit/s
850 Mbit/s
750 Mbit/s
Curiously, netmaker performed better than the direct connection. The reason for this is not known at this point, but a similar effect has been observed in this medium.com article.
Generally, one can see that Tailscale (which internally uses software wireguard) is approximately half the speed of ZeroTier, which in turn is outperformed significantly by Netmaker.
In a followup post I will describe advantages and disadvantages of those solutions and explore under which scenarios I would use the solutions.
Just run this sequence of commands to install tailscale. This will automatically determine the correct Ubuntu version
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/$(lsb_release -sc).gpg | sudo apt-key add - curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/$(lsb_release -sc).list | sudo tee /etc/apt/sources.list.d/tailscale.list sudo apt-get update sudo apt-get install tailscale
This headscale setup is using sqlite
– with a much lighter memory & CPU footprint than PostgreSQL for simple usecases, I recommend this for almost any installation: Headscale doesn’t have to manage that many requests and using sqlite3 is fine for all but the most demanding setups.
First, create the directory where headscale and all the data will reside in (we use /opt/headscale
in this example).
sudo mkdir -p /opt/headscale
Now run the following script in /opt/headscale
to initialize the files and directories headscale requires:
mkdir -p ./config touch ./config/db.sqlite curl https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml -o ./config/config.yaml
Note: We have an alternate docker-compose
config for use with Traefik as an reverse proxy, see Headscale docker-compose config for Traefik HTTPS reverse proxy
Now it’s time to create /opt/headscale/docker-compose.yml
:
version: '3.5' services: headscale: image: headscale/headscale:latest volumes: - ./config:/etc/headscale/ - ./data:/var/lib/headscale ports: - 27896:8080 command: headscale serve restart: unless-stopped
This will configure headscale to run its HTTP server on port 27896
. You can reverse proxy this port to the domain of your choice.
Now we should edit the server name in config/config.yaml
:
server_url: https://headscale.mydomain.com
Note that you need to restart tailscale after each
Next, see How to create namespace on headscale server for details on how you can create a namespace. Once you have created a namespace (comparable to an account on the commercial tailscale service), you can continue connecting clients (the client software is called tailscale), see e.g. How to connect tailscale to headscale server on Linux
Using the method described in our previous post Create a systemd service for your docker-compose project in 10 seconds we will now setup autostart on boot for headscale using systemd
. This command will also start it immediately:
curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin
Use this command to view & follow the logs:
docker-compose logs -f
Example output
headscale_1 | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. headscale_1 | headscale_1 | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. headscale_1 | - using env: export GIN_MODE=release headscale_1 | - using code: gin.SetMode(gin.ReleaseMode) headscale_1 | headscale_1 | [GIN-debug] GET /metrics --> github.com/zsais/go-gin-prometheus.prometheusHandler.func1 (4 handlers) headscale_1 | [GIN-debug] GET /health --> github.com/juanfont/headscale.(*Headscale).Serve.func2 (4 handlers) headscale_1 | [GIN-debug] GET /key --> github.com/juanfont/headscale.(*Headscale).KeyHandler-fm (4 handlers) headscale_1 | [GIN-debug] GET /register --> github.com/juanfont/headscale.(*Headscale).RegisterWebAPI-fm (4 handlers) headscale_1 | [GIN-debug] POST /machine/:id/map --> github.com/juanfont/headscale.(*Headscale).PollNetMapHandler-fm (4 handlers) headscale_1 | [GIN-debug] POST /machine/:id --> github.com/juanfont/headscale.(*Headscale).RegistrationHandler-fm (4 handlers) headscale_1 | [GIN-debug] GET /oidc/register/:mkey --> github.com/juanfont/headscale.(*Headscale).RegisterOIDC-fm (4 handlers) headscale_1 | [GIN-debug] GET /oidc/callback --> github.com/juanfont/headscale.(*Headscale).OIDCCallback-fm (4 handlers) headscale_1 | [GIN-debug] GET /apple --> github.com/juanfont/headscale.(*Headscale).AppleMobileConfig-fm (4 handlers) headscale_1 | [GIN-debug] GET /apple/:platform --> github.com/juanfont/headscale.(*Headscale).ApplePlatformConfig-fm (4 handlers) headscale_1 | [GIN-debug] GET /swagger --> github.com/juanfont/headscale.SwaggerUI (4 handlers) headscale_1 | [GIN-debug] GET /swagger/v1/openapiv2.json --> github.com/juanfont/headscale.SwaggerAPIv1 (4 handlers) headscale_1 | [GIN-debug] GET /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] POST /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] PUT /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] PATCH /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] HEAD /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] OPTIONS /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] DELETE /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] CONNECT /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | [GIN-debug] TRACE /api/v1/*any --> github.com/gin-gonic/gin.WrapF.func1 (5 handlers) headscale_1 | 2022-01-16T19:04:04Z WRN Listening without TLS but ServerURL does not start with http:// headscale_1 | 2022-01-16T19:04:04Z INF listening and serving (multiplexed HTTP and gRPC) on: 0.0.0.0:8080 headscale_1 | 2022-01-16T19:04:04Z INF Setting up a DERPMap update worker frequency=86400000
TechOverflow tested Wireguard bandwidth / throughput on the TPLink WDR3600 with OpenWRT 21.02, based on a standard iperf3
TCP benchmark. We did not use Pre-shared keys in this setup.
So far we were able to verify that the Wireguard bandwidth is approximately 27Mbit/s
(unidirectional), measured using iperf3
.
Connecting to host 192.168.239.254, port 5201 [ 5] local 10.9.1.104 port 57502 connected to 192.168.239.254 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 3.48 MBytes 29.2 Mbits/sec 0 215 KBytes [ 5] 1.00-2.00 sec 3.86 MBytes 32.4 Mbits/sec 0 387 KBytes [ 5] 2.00-3.00 sec 3.13 MBytes 26.2 Mbits/sec 0 470 KBytes [ 5] 3.00-4.00 sec 3.37 MBytes 28.3 Mbits/sec 0 470 KBytes [ 5] 4.00-5.00 sec 3.31 MBytes 27.8 Mbits/sec 0 470 KBytes [ 5] 5.00-6.00 sec 3.31 MBytes 27.8 Mbits/sec 0 470 KBytes [ 5] 6.00-7.00 sec 3.31 MBytes 27.8 Mbits/sec 0 470 KBytes [ 5] 7.00-8.00 sec 2.76 MBytes 23.1 Mbits/sec 0 470 KBytes [ 5] 8.00-9.00 sec 3.31 MBytes 27.8 Mbits/sec 0 470 KBytes [ 5] 9.00-10.00 sec 2.76 MBytes 23.1 Mbits/sec 0 470 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 32.6 MBytes 27.4 Mbits/sec 0 sender [ 5] 0.00-10.14 sec 32.0 MBytes 26.4 Mbits/sec receiver
During the test, top
consistently showed 0%
idle CPU load, with the load being approximately 51%
sys and 49%
sirq.
The commands in use were
iperf3 -s
on the WDR3600 and
iperf -c [IP address of WDR3600]
on the client.
Run this on your OpenWRT router to automatically re-resolve DNS names for peers.
/usr/bin/wireguard_watchdog
is automatically installed with the standard wireguard package, so you only need to enable it to run every minute:
echo '* * * * * /usr/bin/wireguard_watchdog' >> /etc/crontabs/root
Source: This commit message.
Install using
opkg update opkg install luci-proto-wireguard
You can use wg show
to check if a client is connected:
interface: Computer public key: X6NJW+IznvItD3B5TseUasRPjPzF0PkM5+GaLIjdBG4= private key: (hidden) listening port: 19628 peer: H3KaL/X94984cLDNWFsM4Hx6Rs/Ku0bW2ECkDUn7wFw= endpoint: 10.9.1.108:19628 allowed ips: 10.217.59.2/32 latest handshake: 27 seconds ago transfer: 13.19 MiB received, 12.70 MiB sent persistent keepalive: every 1 minute
Look for this line:
latest handshake: 27 seconds ago
If it’s less than two minutes old, the client is connected.
If the latest handshake
line is missing entirely, the peer has never connected successfully!
If in doubt, you can often ping
the client to verify. It depends on the client configuration and possibly firewall settings if it will answer the ping but it never hurts to try.
In this example we will use Wireguard-ESP32-Arduino in order to make HTTP requests over Wireguard on the ESP32.
[env:esp32-gateway] platform = espressif32 board = esp32-gateway framework = arduino monitor_speed = 115200 lib_deps = ciniml/[email protected]^0.1.5
#include <WiFi.h> #include <WireGuard-ESP32.h> // WiFi configuration --- UPDATE this configuration for your WiFi AP char ssid[] = "MyWifiESSID"; char password[] = "my-wifi-password"; // WireGuard configuration --- UPDATE this configuration from JSON char private_key[] = "gH2YqDa+St6x5eFhomVQDwtV1F0YMQd3HtOElPkZgVY="; IPAddress local_ip(10, 217, 59, 2); char public_key[] = "X6NJW+IznvItD3B5TseUasRPjPzF0PkM5+GaLIjdBG4="; char endpoint_address[] = "192.168.178.133"; // IP of Wireguard endpoint to connect to. int endpoint_port = 19628; static WireGuard wg; void setup() { Serial.begin(115200); Serial.println("Connecting to the AP..."); WiFi.begin(ssid, password); while( !WiFi.isConnected() ) { delay(100); } Serial.println(WiFi.localIP()); Serial.println("Adjusting system time..."); configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com"); Serial.println("Connected. Initializing WireGuard..."); wg.begin( local_ip, private_key, endpoint_address, public_key, endpoint_port); } void loop() { WiFiClient client; /** * Connect to * python3 -m http.server */ if( !client.connect("10.217.59.1", 8000) ) { Serial.println("Failed to connect..."); delay(1000); return; } else { // Client connected successfully. Send dummy HTTP request. client.write("GET /wireguard-test HTTP/1.1\r\n"); client.write("Host: wireguard.test.com\r\n"); client.write("\r\n\r\n"); } }
Remember to replace 192.168.238.133
by the IP address of the computer your ESP32 should connect to (i.e. the computer running WireGuard). You also need to enter the correct Wifi credentials.
On the computer, deploy this WireGuard config:
[Interface] # Name = Computer PrivateKey = ONj6Iefel47uMKtWRCSMLan2UC5eW3Fj9Gsy9bqcyEc= Address = 10.217.59.1/24 ListenPort = 19628 [Peer] # Name = ESP32 PublicKey = H3KaL/X94984cLDNWFsM4Hx6Rs/Ku0bW2ECkDUn7wFw= AllowedIPs = 10.217.59.2/32 PersistentKeepalive = 60
which is auto-generated by the following GuardMyWire config:
{ "rules": { "Node": { "connect_to": ["*"], "keepalive": 60 } }, "peers": [ { "name": "Computer", "endpoint": "192.168.178.233:19628", "addresses": [ "10.217.59.1/24" ], "type": "Node", "interface_name": "wg0" }, { "name": "ESP32", "addresses": [ "10.217.59.2/24" ], "type": "Node", "interface_name": "wg0" } ] }
Enable this config and start a Python HTTP server to receive the requests using
python3 -m http.server
Now flash the firmware on the ESP32.
Using wg show
you should see the ESP connecting:
interface: Computer public key: X6NJW+IznvItD3B5TseUasRPjPzF0PkM5+GaLIjdBG4= private key: (hidden) listening port: 19628 peer: H3KaL/X94984cLDNWFsM4Hx6Rs/Ku0bW2ECkDUn7wFw= endpoint: 10.9.1.108:19628 allowed ips: 10.217.59.2/32 latest handshake: 5 seconds ago transfer: 11.71 MiB received, 10.43 MiB sent persistent keepalive: every 1 minute
Look for the
latest handshake: 5 seconds ago
line.
On the shell running python3 -m http.server
you should see the dummy HTTP requests:
10.217.59.2 - - [31/Dec/2021 02:36:48] "GET /wireguard-test HTTP/1.1" 404 - 10.217.59.2 - - [31/Dec/2021 02:36:48] code 404, message File not found 10.217.59.2 - - [31/Dec/2021 02:36:48] "GET /wireguard-test HTTP/1.1" 404 - 10.217.59.2 - - [31/Dec/2021 02:36:48] code 404, message File not found 10.217.59.2 - - [31/Dec/2021 02:36:48] "GET /wireguard-test HTTP/1.1" 404 - 10.217.59.2 - - [31/Dec/2021 02:36:48] code 404, message File not found
Update 2022-12-30: Updated code, now uses variables
Assuming your peer comment is peer1
and the correct endpoint DNS record is peer1.mydomain.com
, you can use this RouterOS script to update the endpoint based on the DNS record:
:local PEERCOMMENT :local DOMAIN :set PEERCOMMENT "peer1" :set DOMAIN "peer1.mydomain.com" :if ([interface wireguard peers get number=[find comment=$PEERCOMMENT] value-name=endpoint-address] != [/resolve $DOMAIN]) do={ interface wireguard peers set number=[find comment=$PEERCOMMENT] endpoint-address=[/resolve $DOMAIN] }
Modify the variables to suit your Wireguard config: Set PEERCOMMENT
to the comment of the peer that should be updated and set DOMAIN
to the DNS domain name that should be used to update the peer’s IP address
After that, add it as a new script in System -> Scripts, then add a Scheduler to run the script e.g. every 30 seconds under System -> Scheduler
Related posts which might make the process easier to understand:
Assuming your peer comment is peer1
and the correct endpoint DNS record is peer1.mydomain.com
:
([interface wireguard peers get number=[find comment=peer1] value-name=endpoint-address] = [resolve peer1.mydomain.com])
This will return true
if the peer endpoint is the same as the DNS record.
[[email protected]] > :put ([interface wireguard peers get number=[find comment=peer1] value-name=endpoint-address] = [resolve peer1.mydomain.com]) true
We assume that the peer you want to find info about has comment=peer1.mydomain.com
. Use
Use
interface wireguard peers get number=[find comment=peer1.mydomain.com] value-name=endpoint-address
or use :put [...]
to print the value:
:put [interface wireguard peers get number=[find comment=peer1.mydomain.com] value-name=endpoint-address]
[[email protected]] > :put [interface wireguard peers get number=[find comment=peer1.mydomain.com] value-name=endpoint-address] 12.245.102.141
When installing wireguard-tools
on Linux, it includes a script called reresolve-dns.sh
. This will take care of automatically re-resolving.
According to its documentation, you should run it every 30 seconds or so.
So we can just create a systemd timer to run it every 30 seconds.
Use our script
wget -qO- https://techoverflow.net/scripts/install-wireguard-reresolve-dns.sh | sudo bash /dev/stdin
Now you need to enable it for each relevant interface separately, for example for wg0
:
systemctl enable --now [email protected]
Do manually what our script does.
Create /etc/systemd/system/[email protected]
:
[Unit] [email protected] [Service] Type=oneshot ExecStart=/usr/share/doc/wireguard-tools/examples/reresolve-dns/reresolve-dns.sh %i
Create /etc/systemd/system/[email protected]
:
[Unit] [email protected] timer [Timer] [email protected]%i.service OnCalendar=*-*-* *:*:00,30 Persistent=true [Install] WantedBy=timers.target
Now you need to enable it for each relevant interface separately, for example for wg0
:
systemctl enable --now [email protected]
Wireguard doesn’t really use the concept of client and server the same way OpenVPN does. A wireguard interface does not have a fixed role as client or server – think about it like this:
For a single connection:
Endpoint
set in your wireguard config like this:Endpoint = vpn.mydomain.com:31265
A client will take the initiative and send packets to the server without having received any packet from the server beforehand – just like in classical VPNs.
Endpoint
set to connect to. A server will learn which IP address to send packets to once a client has completed the handshake. If a client IP address changes, the server will learn the new IP address as soon as it receives a validated packet from the client.Most real-world wireguard connections have one client and one server. There are exceptions to this, namely if both endpoints have a static IP address or fixed host name, so both wireguard instances always know which IP address or hostname to send packets to.
This is an example wg show
output:
interface: MyVPN public key: xJ+A//t9RbOU4ISIr61tsZwc8SPLbLONXhknnU1QvBQ= private key: (hidden) listening port: 12073 peer: xgmml6wPoe9auL5oGhqScQXLByfrI/1xq3sOJzYaNhE= endpoint: 77.55.81.22:23711 allowed ips: 10.178.212.1/32, 10.39.24.0/24 latest handshake: 37 seconds ago transfer: 948 B received, 1.40 KiB sent persistent keepalive: every 30 seconds
This is what I look for:
persistent keepalive
enabled? Without persistent keepalive
, you will not be able to properly debug Wireguard because no packets will be sent unless some traffic is going through the interface. Therefore, I strongly recommend to always enable persistent keepalive
even if you plan to disable it later!latest handshake
listed and recent? Not being able to handshake with a remote peer typically indic-ates either a network problem or a configuration problem, but in some cases it’s also a system-related problem:host
)transfer
should show >0 bytes received and sent! This is typically equivalent to the latest handshake
debugging method. Bytes being sent but no bytes being received typically indicates that the Wireguard interface is trying to perform an handshake but does not get any reply back.Also see my WireguardConfig project which makes this kind of configuration much easier
We will assume that you already have a wireguard config file, e.g. MyVPN.conf
MyVPN.conf
to /etc/wireguard/MyVPN.conf
:sudo cp MyVPN.conf /etc/wireguard/MyVPN.conf
sudo systemctl enable --now [email protected]
sudo wg show
Example wg-show
output
interface: MyVPN public key: xJ+A//t9RbOU4ISIr61tsZwc8SPLbLONXhknnU1QvBQ= private key: (hidden) listening port: 12073 peer: xgmml6wPoe9auL5oGhqScQXLByfrI/1xq3sOJzYaNhE= endpoint: 77.55.81.22:23711 allowed ips: 10.178.212.1/32, 10.39.24.0/24 latest handshake: 37 seconds ago transfer: 948 B received, 1.40 KiB sent persistent keepalive: every 30 seconds
Wireguard’s allowed_ips
field does two different things. Let’s consider the following WireGuard config (generated by the WireguardConfig Site2Site example):
[Interface] # Name = office1.mydomain.org PrivateKey = ...... Address = 10.82.85.1/24 ListenPort = 19628 [Peer] # Name = office2.mydomain.org PublicKey = ... AllowedIPs = 10.82.85.2/32, 192.168.200.0/24 PersistentKeepalive = 60
We can see that for the peer office2.mydomain.org
the AllowedIPs
field is set to 10.82.85.2/32, 192.168.200.0/24
.
AllowedIPs
does two things:
10.82.85.2/32
or to 192.168.200.0/24
will be routed through the WireGuard interface to that peer10.82.85.2/32
or 192.168.200.0/24
to be routed from the given peer on the WireGuard interfaceNote especially the second point. Any packet from the given peer with a source IP address which is not listed in AllowedIPs
will be discarded! While this does not replace a firewall, it serves a an integral part of Wireguard’s security model.