From ca6765616f8c583318f76a23a5ebcbf017b3fe86 Mon Sep 17 00:00:00 2001 From: Grische <2787581+grische@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:29:53 +0100 Subject: [PATCH 1/6] ffmuc-mesh-vpn-wireguard: add wgkex loadbalancing Add support for the server-side loadbalancing with wgkex v0.2.0+ Co-authored-by: DasSkelett --- ffmuc-mesh-vpn-wireguard-vxlan/Makefile | 4 +- ffmuc-mesh-vpn-wireguard-vxlan/README.md | 38 +++--- .../gluon-mesh-wireguard-vxlan/checkuplink | 112 +++++++++++++----- .../parse-wgkex-response.lua | 12 ++ 4 files changed, 121 insertions(+), 45 deletions(-) create mode 100644 ffmuc-mesh-vpn-wireguard-vxlan/luasrc/lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/Makefile b/ffmuc-mesh-vpn-wireguard-vxlan/Makefile index eb94d7d0..d74463ee 100644 --- a/ffmuc-mesh-vpn-wireguard-vxlan/Makefile +++ b/ffmuc-mesh-vpn-wireguard-vxlan/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ffmuc-mesh-vpn-wireguard-vxlan -PKG_VERSION:=1 +PKG_VERSION:=2 PKG_RELEASE:=1 PKG_MAINTAINER:=Annika Wickert @@ -11,7 +11,7 @@ include $(TOPDIR)/../package/gluon.mk define Package/ffmuc-mesh-vpn-wireguard-vxlan TITLE:=Support for connecting meshes via wireguard - DEPENDS:=+gluon-mesh-vpn-core +micrond +kmod-wireguard +wireguard-tools +ip-full + DEPENDS:=+gluon-mesh-vpn-core +micrond +kmod-wireguard +wireguard-tools +ip-full +lua-jsonc endef $(eval $(call BuildPackageGluon,ffmuc-mesh-vpn-wireguard-vxlan)) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/README.md b/ffmuc-mesh-vpn-wireguard-vxlan/README.md index c3686d32..ab302f48 100644 --- a/ffmuc-mesh-vpn-wireguard-vxlan/README.md +++ b/ffmuc-mesh-vpn-wireguard-vxlan/README.md @@ -1,6 +1,24 @@ # ffmuc-mesh-vpn-wireguard-vxlan -You can use this package for connecting with wireguard to the Freifunk Munich network. +This package adds support for WireGuard+VXLAN as Mesh VPN protocol stack as it is used in the Freifunk Munich network. + +### Dependencies + +This relies on [wgkex](https://github.com/freifunkMUC/wgkex), the FFMUC WireGuard key exchange broker running on the configured broker address. The broker programms the gateway to accept the WireGuard key which is transmitted during connection. +Starting with the key exchange API v2, the wgkex broker also returns WireGuard peer data for a gateway selected by the broker, which this package then configures as mesh VPN peer/endpoint. This can be enabled by setting the `loadbalancing` option to `1`. + +For the health-checks a webserver of some kind needs to listen to `HTTP GET` requests on the gateways. + +### How it works + +When `checkuplink` gets called (which happens every minute via cronjob), it checks if the gateway connection is still alive by calling `wget` and connecting to the WireGuard peer link address. If this address replies, we also start a `batctl ping` to the same address. If both checks succeed the connection just stays alive. + +If one of the checks above bails out with an error the reconnect cycle is started. This means `checkuplink` registers itself with `wireguard.broker` by sending the WireGuard public key over either HTTP or HTTPS (depending on the device support). +The broker responds with JSON data containing the gateway peer data (pubkey, address, port, allowed IPs aka link address). `checkuplink` adds the peer to the wg interface using this data, and sets up the VXLAN interface with the peer link address as remote endpoint. + +This script prefers to establish connections over IPv6 and falls back to IPv4 **only if there is no IPv6 default route**. + +### Configuration You should use something like the following in the site.conf: @@ -10,8 +28,9 @@ You should use something like the following in the site.conf: enabled = true, iface = 'wg_mesh_vpn', -- not 'mesh-vpn', this is used for the VXLAN interface mtu = 1406, - broker = 'broker.ffmuc.net/api/v1/wg/key/exchange', - peers = { + broker = 'broker.ffmuc.net', -- base path of broker, will be combined with API path + loadbalancing = '1' -- controls whether to use the loadbalancing/gateway assignment feature of the broker + peers = { -- only needed if 'loadbalancing = '0'' { publickey = 'TszFS3oFRdhsJP3K0VOlklGMGYZy+oFCtlaghXJqW2g=', endpoint = 'gw04.ext.ffmuc.net:40011', @@ -24,23 +43,10 @@ You should use something like the following in the site.conf: }, }, }, - ``` And you should include the package in the site.mk of course! -### Dependencies - -This relies on [wgkex](https://github.com/freifunkMUC/wgkex) the FFMUC wireguard broker running on the configured broker address. The broker programms the gateway to accept the WireGuard key which is transmitted during connection. - -For the health-checks a webserver of some kind needs to listen to `HTTP GET` requests on the gateways. - -### How it works - -When `checkuplink` gets called (which happens every minute via cronjob), it checks if the gateway connection is still alive by calling `wget` and connecting to `wireguard.peer.peer_[number].link_address`. If this address replies we also start a `batctl ping` to the same address. If both checks succeed the connection just stays alive. - -If one of the checks above bails out with an error the reconnect cycle is started. Which means `checkuplink` registers itself with `wireguard.broker` by sending the WireGuard public_key over either http or https (depending on the device support). After the key was sent the script tries to randomely connect to one of the `wireguard.peer`. This script prefers to establish connections over IPv6 and falls back to IPv4 only if there is no IPv6 default route. - ### Interesting Links - [FFMUC: Half a year with WireGuard](https://www.slideshare.net/AnnikaWickert/ffmuc-half-a-year-with-wireguard) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink index bcb830c1..7c7e8232 100755 --- a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink +++ b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink @@ -27,22 +27,37 @@ clean_port() { echo "$1" | sed -r 's/:[0-9]+$|\[|\]//g' } -check_address_family() { - local peer_endpoint="$2" - local gateway - gateway="$(clean_port "$peer_endpoint")" +extract_port() { + echo "$1" | awk -F: '{print $NF}' +} + +combine_ip_port() { + local ip="$1" + local port="$2" + + # Add brackets in case the IP is an IPv6 + case $ip in + *":"*) + ip="[${ip}]" + ;; + esac + + echo "$ip:$port" +} + +resolve_host() { + local gateway="$1" # Check if we have a default route for v6 if not fallback to v4 if ip -6 route show table 1 | grep -q 'default via' then local ipv6 - ipv6="$(gluon-wan nslookup "$gateway" | grep 'Address:\? [0-9]' | grep -E -o '([a-f0-9:]+:+)+[a-f0-9]+')" - echo "[$ipv6]$(echo "$peer_endpoint" | grep -E -oe ":[0-9]+$")" + ipv6="$(gluon-wan nslookup "$gateway" | grep 'Address:\? [0-9]' | grep -oE '([a-f0-9:]+:+)+[a-f0-9]+')" + echo "$ipv6" else local ipv4 - ipv4="$(gluon-wan nslookup "$gateway" | grep 'Address:\? [0-9]' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b")" - echo "$ipv4$(echo "$peer_endpoint" | grep -E -oe ":[0-9]+$")" + ipv4="$(gluon-wan nslookup "$gateway" | grep 'Address:\? [0-9]' | grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b')" + echo "$ipv4" fi - } force_wan_connection() { @@ -125,17 +140,6 @@ then exit 3 fi -# Get the number of configured peers and randomly select one -NUMBER_OF_PEERS=$(uci -q show wireguard | grep -E -ce "peer_[0-9]+.endpoint") -PEER="$(awk -v min=1 -v max="$NUMBER_OF_PEERS" 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')" -PEER_PUBLICKEY="$(uci get wireguard.peer_"$PEER".publickey)" - -logger -t checkuplink "Selected peer $PEER" - -endpoint="$(check_address_family "$PEER_PUBLICKEY" "$(uci get wireguard.peer_"$PEER".endpoint)")" - -logger -t checkuplink "Connecting to $endpoint" - # Delete Interfaces { ip link set nomaster dev mesh-vpn >/dev/null 2>&1 @@ -146,7 +150,7 @@ ip link delete dev "${MESH_VPN_IFACE}" >/dev/null 2>&1 || true PUBLICKEY=$(uci get wireguard.mesh_vpn.privatekey | wg pubkey) SEGMENT=$(uci get gluon.core.domain) -# Push public key to broker, test for https and use if supported +# Push public key to broker and receive gateway data, test for https and use if supported ret=0 wget -q "https://[::1]" || ret=$? # returns Network Failure =4 if https exists @@ -156,7 +160,64 @@ if [ "$ret" -eq 1 ]; then else PROTO=https fi -force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$PROTO://$(uci get wireguard.mesh_vpn.broker)" + +# Remove API path suffix if still present in config +WGKEX_BROKER_BASE_PATH="$(uci get wireguard.mesh_vpn.broker | sed 's|/api/v1/wg/key/exchange||')" + +if [ "$(uci get wireguard.mesh_vpn.loadbalancing)" = "1" ]; then + # Use /api/v2, get gateway peer details from broker response + WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/v2/wg/key/exchange" + logger -t checkuplink "Contacting wgkex broker $WGKEX_BROKER" + if ! WGKEX_DATA=$(force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"); then + logger -p err -t checkuplink "Contacting wgkex broker failed, response: $WGKEX_DATA" + exit 1 + fi + + logger -p info -t checkuplink "Got data from wgkex broker: $WGKEX_DATA" + + # Parse the returned JSON in a Lua script, returning the endpoint address, port, pubkey and first allowed IP, separated by newlines + if ! data=$(lua /lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua "$WGKEX_DATA"); then + logger -p err -t checkuplink "Parsing wgkex broker data failed" + exit 1 + fi + + logger -p debug -t checkuplink "Successfully parsed wgkex broker data" + + PEER_ADDRESS="$(echo "$data" | sed -n 1p)" + PEER_PORT="$(echo "$data" | sed -n 2p)" + PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" + PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) + + PEER_ADDRESS="$(resolve_host "$PEER_ADDRESS")" + PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + +else + # Use /api/v1, get gateway peer details from config + WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/v1/wg/key/exchange" + logger -p info -t checkuplink "Contacting wgkex broker $WGKEX_BROKER" + if ! force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"; then + logger -p err -t checkuplink "Contacting wgkex broker failed" + exit 1 + fi + + # Get the number of configured peers and randomly select one + NUMBER_OF_PEERS=$(uci -q show wireguard | grep -E -ce "peer_[0-9]+.endpoint") + PEER="$(awk -v min=1 -v max="$NUMBER_OF_PEERS" 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')" + + logger -p info -t checkuplink "Selected peer $PEER" + + PEER_HOSTPORT="$(uci get wireguard.peer_"$PEER".endpoint)" + PEER_HOST="$(clean_port "$PEER_HOSTPORT")" + PEER_ADDRESS="$(resolve_host "$PEER_HOST")" + PEER_PORT="$(extract_port "$PEER_HOSTPORT")" + PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + + PEER_PUBLICKEY="$(uci get wireguard.peer_"$PEER".publickey)" + PEER_LINKADDRESS="$(uci get wireguard.peer_"$PEER".link_address)" + +fi + +logger -p info -t checkuplink "Connecting to $PEER_ENDPOINT" # Bring up the wireguard interface ip link add dev "$MESH_VPN_IFACE" type wireguard @@ -168,10 +229,7 @@ LINKLOCAL="$(interface_linklocal)" # Add link-address and Peer ip address add "${LINKLOCAL}"/64 dev "$MESH_VPN_IFACE" -if [ "$endpoint" = "" ]; then - endpoint=$(uci get wireguard.peer_"$PEER".endpoint) -fi -gluon-wan wg set "$MESH_VPN_IFACE" peer "$(uci get wireguard.peer_"$PEER".publickey)" persistent-keepalive 25 allowed-ips "$(uci get wireguard.peer_"$PEER".link_address)/128" endpoint "$endpoint" +gluon-wan wg set "$MESH_VPN_IFACE" peer "$PEER_PUBLICKEY" persistent-keepalive 25 allowed-ips "$PEER_LINKADDRESS/128" endpoint "$PEER_ENDPOINT" # We need to allow incoming vxlan traffic on mesh iface sleep 10 @@ -184,7 +242,7 @@ then fi # Bring up VXLAN -if ! ip link add mesh-vpn type vxlan id "$(lua -e 'print(tonumber(require("gluon.util").domain_seed_bytes("gluon-mesh-vpn-vxlan", 3), 16))')" local "${LINKLOCAL}" remote "$(uci get wireguard.peer_"$PEER".link_address)" dstport 8472 dev "$MESH_VPN_IFACE" +if ! ip link add mesh-vpn type vxlan id "$(lua -e 'print(tonumber(require("gluon.util").domain_seed_bytes("gluon-mesh-vpn-vxlan", 3), 16))')" local "${LINKLOCAL}" remote "$PEER_LINKADDRESS" dstport 8472 dev "$MESH_VPN_IFACE" then logger -p err -t checkuplink "Unable to create mesh-vpn interface" exit 2 diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/luasrc/lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua b/ffmuc-mesh-vpn-wireguard-vxlan/luasrc/lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua new file mode 100644 index 00000000..b50aeee5 --- /dev/null +++ b/ffmuc-mesh-vpn-wireguard-vxlan/luasrc/lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua @@ -0,0 +1,12 @@ +local json = require 'jsonc' + +local input = assert(arg[1]) +local data = assert(json.parse(input)) +if not data.Endpoint or not data.Endpoint.Address or not data.Endpoint.Port + or not data.Endpoint.PublicKey or not data.Endpoint.AllowedIPs or not data.Endpoint.AllowedIPs[1] then + error("Malformed JSON response, missing required value") +end +print(data.Endpoint.Address) +print(data.Endpoint.Port) +print(data.Endpoint.PublicKey) +print(data.Endpoint.AllowedIPs[1]) From 160bb6338bd2dea8801cecc1f2bf538a27181e78 Mon Sep 17 00:00:00 2001 From: Grische <2787581+grische@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:41:30 +0100 Subject: [PATCH 2/6] fmuc-mesh-vpn-wireguard: rework wgkex loadbalance --- ffmuc-mesh-vpn-wireguard-vxlan/README.md | 13 +++- ffmuc-mesh-vpn-wireguard-vxlan/check_site.lua | 7 ++- .../gluon-mesh-wireguard-vxlan/checkuplink | 63 +++++++++++++++++-- .../lib/gluon/upgrade/400-mesh-vpn-wireguard | 12 ++-- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/README.md b/ffmuc-mesh-vpn-wireguard-vxlan/README.md index ab302f48..1a7c4b86 100644 --- a/ffmuc-mesh-vpn-wireguard-vxlan/README.md +++ b/ffmuc-mesh-vpn-wireguard-vxlan/README.md @@ -5,7 +5,7 @@ This package adds support for WireGuard+VXLAN as Mesh VPN protocol stack as it i ### Dependencies This relies on [wgkex](https://github.com/freifunkMUC/wgkex), the FFMUC WireGuard key exchange broker running on the configured broker address. The broker programms the gateway to accept the WireGuard key which is transmitted during connection. -Starting with the key exchange API v2, the wgkex broker also returns WireGuard peer data for a gateway selected by the broker, which this package then configures as mesh VPN peer/endpoint. This can be enabled by setting the `loadbalancing` option to `1`. +Starting with the key exchange API v2, the wgkex broker also returns WireGuard peer data for a gateway selected by the broker, which this package then configures as mesh VPN peer/endpoint. This can be enabled by setting the `loadbalancing` option accordingly. For the health-checks a webserver of some kind needs to listen to `HTTP GET` requests on the gateways. @@ -29,8 +29,15 @@ You should use something like the following in the site.conf: iface = 'wg_mesh_vpn', -- not 'mesh-vpn', this is used for the VXLAN interface mtu = 1406, broker = 'broker.ffmuc.net', -- base path of broker, will be combined with API path - loadbalancing = '1' -- controls whether to use the loadbalancing/gateway assignment feature of the broker - peers = { -- only needed if 'loadbalancing = '0'' + + -- loadbalancing controls whether the client can enable the loadbalancing/gateway assignment feature of the broker + -- on: the client will always use loadbalancing + -- off: the client cannot enable loadbalancing + -- on-by-default: the client can enable/disable loadbalancing and will use loadbalancing by default + -- off-by-default: the client can enable/disable loadbalancing and will not use loadbalancing by default + loadbalancing = 'on-by-default', -- optional + + peers = { -- not needed if loadbalancing = 'on' { publickey = 'TszFS3oFRdhsJP3K0VOlklGMGYZy+oFCtlaghXJqW2g=', endpoint = 'gw04.ext.ffmuc.net:40011', diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/check_site.lua b/ffmuc-mesh-vpn-wireguard-vxlan/check_site.lua index 5caf276d..d7b06820 100644 --- a/ffmuc-mesh-vpn-wireguard-vxlan/check_site.lua +++ b/ffmuc-mesh-vpn-wireguard-vxlan/check_site.lua @@ -6,6 +6,11 @@ local function check_peer(k) need_string(in_domain(extend(k, {'link_address'}))) end -need_table({'mesh_vpn', 'wireguard', 'peers'}, check_peer) need_number({'mesh_vpn', 'wireguard', 'mtu'}) need_string({'mesh_vpn', 'wireguard', 'broker'}) + +local loadbalancing = need_one_of({ 'mesh_vpn', 'wireguard', 'loadbalancing' }, + { 'on', 'off', 'on-by-default', 'off-by-default' }, false) +if loadbalancing ~= 'on' then -- peers are not required when loadbalancing is enforced + need_table({'mesh_vpn', 'wireguard', 'peers'}, check_peer) +end diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink index 7c7e8232..fe1d752f 100755 --- a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink +++ b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink @@ -11,6 +11,28 @@ else exit fi +get_site_string() { + local path="$1" + + lua < Date: Sat, 23 Mar 2024 13:16:50 +0100 Subject: [PATCH 3/6] move get wgkey_data to own function --- .../gluon-mesh-wireguard-vxlan/checkuplink | 104 ++++++++++-------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink index fe1d752f..68a082b2 100755 --- a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink +++ b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink @@ -117,6 +117,63 @@ is_loadbalancing_enabled() { return 0 } +get_wgkex_data(){ + local version="$1" + WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/$version/wg/key/exchange" + + logger -p info -t checkuplink "Contacting wgkex broker $WGKEX_BROKER" + + if ! WGKEX_DATA=$(force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"); then + logger -p err -t checkuplink "Contacting wgkex broker failed, response: $WGKEX_DATA" + exit 1 + else + logger -p info -t checkuplink "Got data from wgkex broker: $WGKEX_DATA" + echo $WGKEX_DATA + fi +} + +use_api_v1(){ + WGKEX_DATA=$(get_wgkex_data v1) + + # Get the number of configured peers and randomly select one + NUMBER_OF_PEERS=$(uci -q show wireguard | grep -E -ce "peer_[0-9]+.endpoint") + PEER="$(awk -v min=1 -v max="$NUMBER_OF_PEERS" 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')" + + logger -p info -t checkuplink "Selected peer $PEER" + PEER_HOSTPORT="$(uci get wireguard.peer_"$PEER".endpoint)" + PEER_HOST="$(clean_port "$PEER_HOSTPORT")" + PEER_ADDRESS="$(resolve_host "$PEER_HOST")" + PEER_PORT="$(extract_port "$PEER_HOSTPORT")" + PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + + PEER_PUBLICKEY="$(uci get wireguard.peer_"$PEER".publickey)" + PEER_LINKADDRESS="$(uci get wireguard.peer_"$PEER".link_address)" +} + + + +use_api_v2() { + WGKEX_DATA=$(get_wgkex_data v2) + + # Parse the returned JSON in a Lua script, returning the endpoint address, port, pubkey and first allowed IP, separated by newlines + if ! data=$(lua /lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua "$WGKEX_DATA"); then + logger -p err -t checkuplink "Parsing wgkex broker data failed" + logger -p info -t checkuplink "Falling back to API v1" + use_api_v1 + else + logger -p debug -t checkuplink "Successfully parsed wgkex broker data" + PEER_ADDRESS="$(echo "$data" | sed -n 1p)" + PEER_PORT="$(echo "$data" | sed -n 2p)" + PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" + PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) + + PEER_ADDRESS="$(resolve_host "$PEER_ADDRESS")" + PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + fi +} + + + mesh_vpn_enabled="$(uci get wireguard.mesh_vpn.enabled)" @@ -219,54 +276,11 @@ WGKEX_BROKER_BASE_PATH="$(get_site_string mesh_vpn.wireguard.broker | sed 's|/ap if is_loadbalancing_enabled; then # Use /api/v2, get gateway peer details from broker response - WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/v2/wg/key/exchange" - logger -p info -t checkuplink "Loadbalancing enabled. Contacting wgkex broker $WGKEX_BROKER" - if ! WGKEX_DATA=$(force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"); then - logger -p err -t checkuplink "Contacting wgkex broker failed, response: $WGKEX_DATA" - exit 1 - fi - - logger -p info -t checkuplink "Got data from wgkex broker: $WGKEX_DATA" - - # Parse the returned JSON in a Lua script, returning the endpoint address, port, pubkey and first allowed IP, separated by newlines - if ! data=$(lua /lib/gluon/gluon-mesh-wireguard-vxlan/parse-wgkex-response.lua "$WGKEX_DATA"); then - logger -p err -t checkuplink "Parsing wgkex broker data failed" - exit 1 - fi - - logger -p debug -t checkuplink "Successfully parsed wgkex broker data" - - PEER_ADDRESS="$(echo "$data" | sed -n 1p)" - PEER_PORT="$(echo "$data" | sed -n 2p)" - PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" - PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) - - PEER_ADDRESS="$(resolve_host "$PEER_ADDRESS")" - PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + use_api_v2 else # Use /api/v1, get gateway peer details from config - WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/v1/wg/key/exchange" - logger -p info -t checkuplink "Loadbalancing disabled. Contacting wgkex broker $WGKEX_BROKER" - if ! force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"; then - logger -p err -t checkuplink "Contacting wgkex broker failed" - exit 1 - fi - - # Get the number of configured peers and randomly select one - NUMBER_OF_PEERS=$(uci -q show wireguard | grep -E -ce "peer_[0-9]+.endpoint") - PEER="$(awk -v min=1 -v max="$NUMBER_OF_PEERS" 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')" - - logger -p info -t checkuplink "Selected peer $PEER" - - PEER_HOSTPORT="$(uci get wireguard.peer_"$PEER".endpoint)" - PEER_HOST="$(clean_port "$PEER_HOSTPORT")" - PEER_ADDRESS="$(resolve_host "$PEER_HOST")" - PEER_PORT="$(extract_port "$PEER_HOSTPORT")" - PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" - - PEER_PUBLICKEY="$(uci get wireguard.peer_"$PEER".publickey)" - PEER_LINKADDRESS="$(uci get wireguard.peer_"$PEER".link_address)" + use_api_v1 fi From b45d288906f87ad2cd6b929da1e98bd71c65d887 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sat, 23 Mar 2024 13:30:13 +0100 Subject: [PATCH 4/6] add peer hostname to log --- .../files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink index 68a082b2..482aa8a2 100755 --- a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink +++ b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink @@ -162,12 +162,12 @@ use_api_v2() { use_api_v1 else logger -p debug -t checkuplink "Successfully parsed wgkex broker data" - PEER_ADDRESS="$(echo "$data" | sed -n 1p)" + PEER_HOST="$(echo "$data" | sed -n 1p)" PEER_PORT="$(echo "$data" | sed -n 2p)" PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) - PEER_ADDRESS="$(resolve_host "$PEER_ADDRESS")" + PEER_ADDRESS="$(resolve_host "$PEER_HOST")" PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" fi } @@ -284,7 +284,7 @@ else fi -logger -p info -t checkuplink "Connecting to $PEER_ENDPOINT" +logger -p info -t checkuplink "Connecting to $PEER_HOST($PEER_ENDPOINT)" # Bring up the wireguard interface ip link add dev "$MESH_VPN_IFACE" type wireguard From 7710c18421c981fbb9e664d74065e0bdd5c02d87 Mon Sep 17 00:00:00 2001 From: Tobias Date: Tue, 26 Mar 2024 12:45:30 +0100 Subject: [PATCH 5/6] add suggestions from grische --- .../gluon-mesh-wireguard-vxlan/checkuplink | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink index 482aa8a2..7957ce42 100755 --- a/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink +++ b/ffmuc-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink @@ -120,16 +120,16 @@ is_loadbalancing_enabled() { get_wgkex_data(){ local version="$1" WGKEX_BROKER="$PROTO://$WGKEX_BROKER_BASE_PATH/api/$version/wg/key/exchange" - + logger -p info -t checkuplink "Contacting wgkex broker $WGKEX_BROKER" if ! WGKEX_DATA=$(force_wan_connection wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' "$WGKEX_BROKER"); then logger -p err -t checkuplink "Contacting wgkex broker failed, response: $WGKEX_DATA" - exit 1 - else - logger -p info -t checkuplink "Got data from wgkex broker: $WGKEX_DATA" - echo $WGKEX_DATA + return 1 fi + + logger -p info -t checkuplink "Got data from wgkex broker: $WGKEX_DATA" + echo "$WGKEX_DATA" } use_api_v1(){ @@ -160,16 +160,17 @@ use_api_v2() { logger -p err -t checkuplink "Parsing wgkex broker data failed" logger -p info -t checkuplink "Falling back to API v1" use_api_v1 - else - logger -p debug -t checkuplink "Successfully parsed wgkex broker data" - PEER_HOST="$(echo "$data" | sed -n 1p)" - PEER_PORT="$(echo "$data" | sed -n 2p)" - PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" - PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) - - PEER_ADDRESS="$(resolve_host "$PEER_HOST")" - PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" + return fi + + logger -p debug -t checkuplink "Successfully parsed wgkex broker data" + PEER_HOST="$(echo "$data" | sed -n 1p)" + PEER_PORT="$(echo "$data" | sed -n 2p)" + PEER_PUBLICKEY="$(echo "$data" | sed -n 3p)" + PEER_LINKADDRESS=$(echo "$data" | sed -n 4p) + + PEER_ADDRESS="$(resolve_host "$PEER_HOST")" + PEER_ENDPOINT="$(combine_ip_port "$PEER_ADDRESS" "$PEER_PORT")" } @@ -276,12 +277,13 @@ WGKEX_BROKER_BASE_PATH="$(get_site_string mesh_vpn.wireguard.broker | sed 's|/ap if is_loadbalancing_enabled; then # Use /api/v2, get gateway peer details from broker response + logger -p info -t checkuplink "Loadbalancing enabled." use_api_v2 else # Use /api/v1, get gateway peer details from config + logger -p info -t checkuplink "Loadbalancing disabled." use_api_v1 - fi logger -p info -t checkuplink "Connecting to $PEER_HOST($PEER_ENDPOINT)" From 70f031cf7f89a3b53607983c08d993b2e04213ed Mon Sep 17 00:00:00 2001 From: Grische <2787581+grische@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:18:45 +0100 Subject: [PATCH 6/6] add description of how to override loadbalancing --- ffmuc-mesh-vpn-wireguard-vxlan/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ffmuc-mesh-vpn-wireguard-vxlan/README.md b/ffmuc-mesh-vpn-wireguard-vxlan/README.md index 1a7c4b86..49ecafa3 100644 --- a/ffmuc-mesh-vpn-wireguard-vxlan/README.md +++ b/ffmuc-mesh-vpn-wireguard-vxlan/README.md @@ -52,6 +52,13 @@ You should use something like the following in the site.conf: }, ``` +If permitted via `on/off-by-default`, a user can override loadbalancing with `uci`: + +```sh +uci set wireguard.mesh_vpn.loadbalancing=0 # override loadbalancing to be always off +uci set wireguard.mesh_vpn.loadbalancing=1 # override loadbalancing to be always on +``` + And you should include the package in the site.mk of course! ### Interesting Links