Wireguard use Hostname in endpoint

Agree with the reboot thing. But a Change of local WAN IP in my case does not toggle the conntrack entries for the wg interface going out to the (fixed) remote Server IP.
So the local WG interface is still using the existing connection, trying to reach the other side, even when peer toggled, as it seems.
I can see the traffik trying to go out (tx on peer)…only when the conntrack entry gets removed, the connection goes trough afterwards.

The only built-in way for a WireGuard (“Peer” client) to detect a change to an endpoint’s IP address is if the endpoint proactively initiates a connection to the (“Peer” client) from its new IP address (which NAT or other firewall rules make impossible in a typical (“Peer” client)/(“Peer” server scenario) — many found that you’d have to restart the (“Peer” client) in order to force it to look up the new IP. address of the (“Peer” server).

To solve this issue wireguard-tools have a script that solves this problem … all that is needed is for MikroTik to make it available within Tik as Tik Speak … perhaps that will be done in the future. The script {see below} can be fount here … the script name is reresolve-dns.sh

#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.

set -e
shopt -s nocasematch
shopt -s extglob
export LC_ALL=C

CONFIG_FILE="$1"
[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
[[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]]
INTERFACE="${BASH_REMATCH[1]}"

process_peer() {
	[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
	[[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\	([0-9]+) ]] || return 0
	(( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0
	wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
	reset_peer_section
}

reset_peer_section() {
	PEER_SECTION=0
	PUBLIC_KEY=""
	ENDPOINT=""
}

reset_peer_section
while read -r line || [[ -n $line ]]; do
	stripped="${line%%\#*}"
	key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
	value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
	[[ $key == "["* ]] && { process_peer; reset_peer_section; }
	[[ $key == "[Peer]" ]] && PEER_SECTION=1
	if [[ $PEER_SECTION -eq 1 ]]; then
		case "$key" in
		PublicKey) PUBLIC_KEY="$value"; continue ;;
		Endpoint) ENDPOINT="$value"; continue ;;
		esac
	fi
done < "$CONFIG_FILE"
process_peer

I don’t see it that way.

We’ll assume here the server has a fixed IP so that part does not change. In which case it is required to have keep-alive being sent from client peer to server peer.
The way I understood WG works, the keepalive send to the other side will at a certain point be coming from another IP when roaming or whatever reason.
That’s the key for the receiving end to change its tables towards that other IP.
This should all be done transparently. No toggling of peer should be required.

(1) Holvoetn I think mozerd is referring to the commonly known issue for the single case that the ENDPOINT is via mynetname or DYNDNS name.
Nothing else makes sense as you point out, if the endpoint is a static fixed IP, it doesnt matter what happens at the client device (change of IP, reboot etc…)
In this case the ability for the MT Device (client) to resolve the new IP address takes too long.
The keep alive attempts for some reason stop after not connecting after 1? 2? 3? iterations …

Thus the scripts noted here to “BUMP” the MT CLIENT device to relook at the wireguard connection. Note that these solutions are very close to the “procustodibus agent”, described here
https://www.procustodibus.com/blog/2021/06/dns-updates-to-wireguard-endpoints/

Sol/n 1 - Use the script (courtesy of gdanov) on the Remote (originating) end of the tunnel to detect when the endpoint IP address or subnet is not available. In other words ‘when down’. The Local_IP refers to the server end of the connection and is any local IP reachable through the tunnel. If that IP is reachable through the tunnel then the tunnel is working!

:local wgcheckip Local_IP
:local endpointip xxxyyy.sn.mynetname.net
#:log info "wg check-ip $wgcheckip "
:if ([/ping $wgcheckip interval=1 count=5] =0) do={
  :log info "WG down $wgcheckip"
  /interface/wireguard/peers/disable [find endpoint-address=$endpointip];
  :delay 60
  /interface/wireguard/peers/enable [find endpoint-address=$endpointip];
  :log info "WG up again $wgcheckip"
}

Sol/n 2 - Easier method! (unknown author) Using the Netwatch Tool, using the “DOWN” tab, and the HOST IP is the Local IP ( Reachable subnet or IP address through the tunnel).

:delay 25
/interface wireguard peer disable 0
:delay 5
/interface wireguard peer enable 0
:log info "WGPeer toggled"

(2) However I think Mozerd is referring to a linux script either at the problem child site – AT THE WG SERVER device or at the client side ???.
Run this script from cron every thirty seconds or so, and it will ensure
that if, when using a dynamic DNS service, the DNS entry for a hosts
changes, the kernel will get the update to the DNS entry

Since the WG server may know its public IP has changed and because of peer roaming it should have the latest peer client IP and port, it really should be the wireguard server that updates the client device, etc. I see the potential issue happening if both change near the same time, but we can discuss that later.
I see that the linked procustodibus article above comes to the same conclusion as myself… bloody plagiarizers…

The only built-in way for a WireGuard client to detect a change to an endpoint’s IP address is if the endpoint proactively initiates a connection to the client from its new IP address (which NAT or other firewall rules make impossible in a typical client-server scenario) — so normally you’d have to restart the client in order to force it to look up the new IP address of the server.
(Note: They solve the issue with their agent on the client side.)

HERE IS THE ANSWER ---- >

This is similar to the functionality of the reresolve-dns.sh script from the wireguard-tools source code repository, which you can manually install and run as a cron job on each client. With Pro Custodibus, however, this feature is automatically built into the agent, so you don’t have to install and configure a separate script to manage it.

The WireGuard Tools script addresses the following scenario

When the WireGuard interface of the (“Peer” client) starts up, it will resolve the DNS record for myvpn.myddns.com, and select one of the IP addresses to use as its endpoint for the (“Peer” server). Let’s say it selects 1.2.3.4

In many CGNAT networks – let’s say the WireGuard (“Peer” server) at 1.2.3.4 becomes unavailable, and your DNS servers remove it from their myvpn.myddns.com responses. Your (“Peer” client) will continue to try to access the WireGuard (“Peer” server) at 1.2.3.4 even though the DNS record for vmyvpn.myddns.com now only contains 13.10.199.6

Hope that explains it :slight_smile:

You typed to soon look above for the answer!!! It is simply a script that runs on the client, similar to the procustodibus AGENT, and no different from the two scripts I pointed to above that are on the user article…

So in summary, this thread is simply a LInux script to
a. a problem we are already aware
b. that has solutions in MT scripts

But thanks Mozerd, I really enjoy looking at linux crap :wink:
Seriously perhaps there is something in the linux script worth pulling out and into the MT scripts??
I will let my Lover of Chocolate and Horses deal with that noise!!!

Another script:

:foreach i in=[/interface/wireguard/peers/find where disabled=no endpoint-address~"[a-z]\$"] do={
  :if ([/interface/wireguard/peers/get $i last-handshake] > [:totime "5m"]) do={
    /interface/wireguard/peers/set $i endpoint-address=[/interface/wireguard/peers/get $i endpoint-address]
  }
}

It takes all enabled peers with endpoint-address ending with letter (which means it’s non-empty and not numeric address) and last handshake older than 5 minutes (“5m” on second line), takes current hostname and sets it again as endpoint-address, which makes RouterOS resolve it again. It should be safe to run it unconditionally from scheduler.

@Sob - very elegant!

Here’s a slightly improved script:

:foreach i in=[/interface/wireguard/peers/find where disabled=no endpoint-address~"[a-z]\$"] do={
  :local LastHandshake [/interface/wireguard/peers/get $i last-handshake]
  :if (([:tostr $LastHandshake] = "") or ($LastHandshake > [:totime "5m"])) do={
    /interface/wireguard/peers/set $i endpoint-address=[/interface/wireguard/peers/get $i endpoint-address]
  }
}

If the peer has never connected (or if the peer is disabled and reenabled while the endpoint is down), then the last-handshake is blank and the > 5m condition is false. This change will trigger the endpoint-address reset for blank last-handshake as well.

Good stuff !
Going to play with this …

Interesting… and it sends all my money to Sob, just cant figure out where that part is in the script. :wink:

Will see if Mikrotik can add this as a checkbox option!

what do you mean safe to run unconditionally from scheduler??
Is there a time option of unconditionally??

@DaveN: Ooops, good catch.

@anav: It doesn’t. I like that idea, but even if it was somehow possible, I probably wouldn’t be able to do it using RouterOS scripting, that thing doesn’t like me. And yes, this should be built-in in some form. By “unconditionally from scheduler” I meant compared to other solutions. Like if you disable peer for five seconds, it will break tunnel for five seconds (although if it was linked to netwatch, it was already broken, so no big deal). But you can run this e.g. every minute, even without netwatch, and it shouldn’t break any working tunnel.

Note (to self and to everyone that might run into the same issue): WebFig on RouterOS 7.2.3 (stable) will not accept FQDN (fully qualified domain name = host name) as endpoint. The input validation will still force you to use an IP address instead.

You have to use the terminal commands to set a FQDN, like in this example:

/interface/wireguard/peers
add allowed-address=10.1.101.0/24 endpoint-address=192.168.80.1 endpoint-port=13231 interface=wireguard1 \
public-key="v/oIzPyFm1FPHrqhytZgsKjU7mUToQHLrW+Tb5e601M="

Using this command will allow you to use a FQDN in place of an IP address.

hostname issue is still not resolved
so i have created my own script which resolves the host name & puts the latest ip in peer’s endpoint-address

:local ddnshost "host.mywire.org"
:local peerip [/interface wireguard peers get [find comment="home"] endpoint-address ];
:local hostip [:resolve $ddnshost];
#:log info "peer endpoint ip address is $peerip";
#:log info "resolved ip address is $hostip"
:if ($peerip != $hostip) do={
/interface wireguard peers set [find comment="home"] endpoint-address=$hostip;
:log warning "Wireguard Peer endpoint IP Updated to: $hostip old IP was $peerip";
} else={
#:log info "WG: no need to update";
}

Is this a script you run on a schedule?

Here is a „small“ guide: https://blog.spaps.de/mikrotik-routeros-wireguard-dynamic-dns-endpoint-refresh/

Yes i run it after 1 minute interval
here is the export

/system script
add dont-require-permissions=no name=wg owner=zone policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":lo\
    cal ddnshost \"host.mywire.org\"\r\
    \n:local peerip [/interface wireguard peers get [find comment=\"Office\"] en\
    dpoint-address ];\r\
    \n:local hostip [:resolve \$ddnshost];\r\
    \n#:log info \"peer endpoint address is \$peerip\";\r\
    \n#:log info \"resolved address is \$hostip\"\r\
    \n:if (\$peerip != \$hostip) do={\r\
    \n/interface wireguard peers set [find comment=\"Office\"] endpoint-address=\
    \$hostip;\r\
    \n:log warning \"Wireguard Peer endpoint IP Updated to: \$hostip old IP was \
    \$peerip\";\r\
    \n} else={\r\
    \n#:log info \"WG: no need to update\";\r\
    \n}"

& for Schedule

/system scheduler
add interval=1m name=wireguard on-event="/system/script/run wg" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-date=nov/12/2022 start-time=14:39:59

I like this one more: http://forum.mikrotik.com/t/wireguard-use-hostname-in-endpoint/143014/29

Because you have FQDN in WG Peer, not only in a script, and you only update it when there’s no connectivity. WG can automatically detect new peer IP if there are packets from new IP with the same signature, so you don’t need to update the endpoint in that case.

Here is another one for you. It is a bit more brutal, but it is versatile as it does not rely on finding any comments, and you can put it on a router with a number of peers and it will just pick out the ones that need killing and restarting without affecting the ones that are OK.

It checks

  • if handshake >3min (no peer should ever have handshake more than 00:02:10)


  • It also checks if there are any peers with 0 tx or 0 rx (which sometimes happens after reboot, and if the peer has never started it will never get handshake >3min)


  • it disables any peer that meets the above two critera


  • THEN in addition looks up the ports in use by each peer, and clears any connections in the firewall (useful if traffic is trying to go out the wrong interface)


  • and finally logs a bit to the log file, only if it has found something to do (so you don’t end up with log spam)


  • Then re-enables the peer


# Wireguard check script

#Declare variables
:local wgport
:local wgpeer

# Disable any peers that have last handshake > 3mins
/interface/wireguard/peers/set disabled=yes [find last-handshake > [:totime "3m"]]
# Disable any peers that have tx or rx 0 (sometimes happens after router restart) 
/interface/wireguard/peers/set disabled=yes [find rx=0 or tx=0]

:delay 2

# For any disabled peer get the port and clear any connections in the firewall
:foreach i in=[/interface/wireguard/peers find disabled=yes] do={
   :set wgport ([/interface/wireguard/peers get $i endpoint-port])

   :foreach j in=[/ip firewall connection find dst-address~":$wgport\$" protocol=udp] do={
   /ip firewall connection remove $j
: log info "** Wiregiuard Check Script ** firewall connections on port $wgport cleared"
   }

# and re-enable any disabled peers
/interface/wireguard/peers set $i disabled=no

# and let the log file know what has happened
:set wgpeer [/interface/wireguard/peers get $i interface]
:log info "** Wireguard Check Script ** wireguard peer $wgpeer restarted"

}

It IS brutal :slight_smile:

Btw,

s/:$wgport/:$wgport\$" protocol="udp/

? So if port is 9090, no connections with port 90907 would be killed.