Wireguard: Peer IP check

Anyone using Wireguard with peers whose IP address changes (e.g. because they are on a private DSL connection) must regularly check the IP address.

Wireguard itself only does DNS resolution when the interface is started to get the current IP address of the peer. However, if, for example, the server's address changes, the connection will eventually fail.

Fortunately, this can be intercepted with a small script, which is executed e.g. every 10 minutes. One approach is the Script in the Ubuntu documentation for Wireguardwhich didn't work on my Debian client (and also seemed a bit complicated).

#!/bin/bash
# Check status of the interface
# wg0-client: Name of the interface to be checked
# mypeer.dyndns.net: the name of the peer whose IP should be checked
        
cip=$(wg show wg0-client endpoints | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
echo "$cip"
digIP=$(dig +short mypeer.dyndns.net) # The address of the peer must be adjusted
   echo "$digIP"
     if [ "$digIP" != "$cip" ]
       then
          echo "Data is different"
          /usr/sbin/service wg-quick@wg0-client restart
            
        else
	  echo "Data are equal"
	  1TP3Nothing to do
	 fi

The main elements of the script above: in line 6, using the wireguard-tools and grep, the current IP address used by the interface is determined. In line 8 it checks which IP address the peer currently has. If these two values differ, the wireguard interface is restarted in line 13 - then the IP address is also resolved again and the connection is established again.

The restart of the Wireguard interface on line 13 may need to be adjusted, depending on what the interface is actually called in your setup.

There are of course other approaches to IP resolution, but for my setup this is a very simple way. One disadvantage: if there are several peers, this approach does not work that easily - then you would have to determine the right peer via regex first, and then read out the corresponding data there.

This script is necessary for maintaining the connection in my FreeNAS Replications Setupwhich now works with Wireguard.

Addition: IP check with multiple peers

If you have entered several peers in your wireguard configuration (e.g. a static connection and once the dial-in via a smartphone for a VPN), you may only want to perform the IP check described here for one peer. The easiest way is to additionally check for a specific peer. Starting from the following peer configuration (whose values do not correspond to a real peer):

[Peer]
PublicKey = mhzqblUOAzxVhOypkidxLnxp5rfYD7uRtvyj0sOi4GE=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = mypeer.dyndns.net:51820

The first line in the check script must be adjusted accordingly:

cip=$(wg show wg0-client endpoints | grep -E "mhzqblUO" | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}") 	

Completely it looks like this:

#!/bin/bash
# Check status of the interface
# wg0-client: Name of the interface to be checked
# mypeer.dyndns.net: the name of the peer whose IP should be checked
        
cip=$(wg show wg0-client endpoints | grep -E "mhzqblUO" | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
echo "$cip"
digIP=$(dig +short mypeer.dyndns.net) # The address of the peer must be adjusted
   echo "$digIP"
     if [ "$digIP" != "$cip" ]
       then
          echo "Data is different"
          /usr/sbin/service wg-quick@wg0-client restart
            
        else
	  echo "Data are equal"
	  1TP3Nothing to do
	 fi

It is important to note that the check script will not work in the variant with certain peers if you do not replace the corresponding value with the one from your setup. In my setup I run it every 5min, so far it has served its purpose so far. Since it is only a pure comparison of the IP address, it should also work in an IPv6 setup - but since I still use a classic telecom connection with IPv4, I didn't deal with this topic in detail. If anyone of the readers has any experience with this (also concerning DynDNS and IPv6 on the Fritzbox) feel free to leave something in the comments!

14 comments

    1. The script is actually also available for Debian, is then in

      /usr/share/doc/wireguard-tools/examples/reresolve-dns/reresolve-dns.sh
      (the path is slightly different than under Arch)

      Do I have to take a closer look again, when I set this up there was some problem with the script as far as I remember.

  1. This should be easier to solve with the "PersistentKeepalive =" option.

    I initiate the connection from on the road (for example from the iPhone) to a DynDNS address. Thanks to Keepalive a packet is sent to the other side every 25 seconds. You can assume that it is highly unlikely that the address on both sides will change at the same time. So at least one side of the tunnel is always reached.

    If the respective peer receives the packet from a different external IP address, but the Crypto Key Routing otherwise matches (coherent combination of private IP address and public key), the assignment of the peer's external IP is updated and subsequent replies arrive correctly again.

    I would have to force address changes and test them explicitly to be able to say it for sure, but in everyday use I have never had problems with broken or dead tunnels with iPhone/iPad, Linux or macOS.

  2. This works because the IP addresses are synchronized when the connection is established. A mobile device initializes more often, e.g. if the reception was too bad etc.

    If you work with a setup of two fixed DSL connections, it is quite possible that one end changes without the connection being reestablished. Then the resolution of the DynDNS address does not fit anymore and the connection goes to a wrong IP. For this you need to adjust the resolution, either as posted here or with the script from Michael.

    With the iPhone, I never have to do that either, but the replication will be in vain on the third day at the latest.

  3. It's just not right now. The DynDNS address that you have stored in wg0.conf is used for the initial connection setup. However, while a connection is established, the assignment of peer to public-IP is constantly updated on the server by newly arriving packets (see https://www.wireguard.com/#cryptokey-routingsection "Built-In Roaming"; see https://www.wireguard.com/papers/wireguard.pdfSection 2.1). Wireguard behaves like mosh and should be very robust when it comes to changing IP addresses. What is really important, however, is that the peer whose public address has changed always starts a communication promptly to inform the others about the change. In fact, the server should not do this very often.

    What I just learned is that the keep-alive is only sent when a peer has received packets, but actually does not need to respond. An empty Wireguard response packet is then sent before the KeepAlive timer expires. So the peer must actually send something regularly for the mechanism to work reliably. On its own, it is therefore not suitable for keeping the peers' address tables permanently up to date. So I was wrong. So you are right about the iPhone, too. Here, connections are certainly often completely reestablished, so the problem of changing server IPs is not as noticeable. But it feels like the tunnel is always there as long as there is an internet connection.

    However, if your DSL-NAT setup with changing server IP ensures that a message is sent regularly from the networks of both peers, for example by regular pings on both sides every minute (cron job on the two FreeNAS?!), then the VPN connection must never or only briefly go dead, because the current IP address will be known shortly. How fast, that depends on the frequency of the pings.

    This way you don't have to regularly remove the network interface and change the routing. This feels a bit more elegant (my opinion).

    1. Another problem is that in the most stupid case of a setup with two DSL connections one peer tries to resolve the IP of the other while the automatic reconnect (which some providers still do) is performed - Wireguard gives up here quite quickly: https://lists.zx2c4.com/pipermail/wireguard/2019-February/003866.html

      Once it has exited, rebooting the interface is quite reliable to get a connection again. In my case data is only transferred once per hour.

      But that's the beauty of it: depending on the concrete purpose of the application, different paths lead to the goal 😉

  4. Hello,
    I also have this exact scenario.
    I have installed the Android app on my phone. In the conf file for this peer is the DynDns of my FritzBox. The Wireguard server is located at home in the network behind the FritzBox on a Raspberry. On both KeepAlive is registered with 25th app started and everything works fine.
    Until the forced disconnection and reconnect by the DSL provider. The tunnel's down, no connection, nothing.
    I set up the timer unit with the above script reresolve-dns.sh - no success.
    I'm a little confused.

    Greetings

    Harald

    1. I can confirm the observation of Harald in this way.
      IMHO the Android client would have to test and reinitialize the connectivity with new resolution of the 'server' peers. So we would have to have an Android app that implements what we said here under Ubuntu / Debian.
      Or am I completely wrong? In any case, it seems to me that we can't achieve behavioral change without changing the Android App.
      Greetings
      Markus

  5. Hi Falk, thanks for the script! The second to last line can't be right, I guess it is supposed to say "echo 'Nothing to do'". Or am I missing something? Cheers Philipp

    1. It's just a comment in the wrong line - of course, you can echo an additional text, but it's not necessary.

  6. Hello Falk,

    I was facing the problem with the changing IP addresses and have read some tips / scripts. Am not yet so experienced in the Linux area and most tips were just totally confusing. But I was able to use your script with two small changes on the Raspberry with pivpn and wireguard. And so I just wanted to say THANK YOU once for taking the trouble to create this post.
    It just comes up way short during this time. You have saved with a few gray hairs 😉

    Many thanks for this entry - Matthias

  7. A restart is not necessary, it is possible to change the address of the endpoint with:
    wg set wg0 endpoint :
    reset.
    With:
    PUB=$(wg | grep peer | awk '{print $2}')
    this can be determined. If the address is an IPv6 address it should be enclosed with '[' and ']'.

Leave a Reply

Your email address will not be published. Required fields are marked *