forked from freifunk-gluon/community-packages
-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ffmuc-gluon-mesh-vpn-wireguard-vxlan: add package (#6)
Co-authored-by: Annika Wickert <[email protected]> Co-authored-by: krombel <[email protected]> Co-authored-by: Martin Weinelt <[email protected]> Co-authored-by: lqb <[email protected]> Co-authored-by: lqb <[email protected]> Co-authored-by: Julian Labus <[email protected]> Co-authored-by: Tristan Helmich <[email protected]> Co-authored-by: goligo <[email protected]>
- Loading branch information
1 parent
1d7e81b
commit 1df9c3d
Showing
8 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
include $(TOPDIR)/rules.mk | ||
|
||
PKG_NAME:=ffmuc-gluon-mesh-vpn-wireguard-vxlan | ||
PKG_VERSION:=1 | ||
PKG_RELEASE:=1 | ||
|
||
PKG_MAINTAINER:=Annika Wickert <[email protected]> | ||
PKG_LICENSE:=GPL-2.0-or-later | ||
|
||
include $(TOPDIR)/../package/gluon.mk | ||
|
||
define Package/ffmuc-gluon-mesh-vpn-wireguard-vxlan | ||
TITLE:=Support for connecting meshes via wireguard | ||
DEPENDS:=+gluon-mesh-vpn-core +micrond +kmod-wireguard +wireguard-tools +ip-full | ||
endef | ||
|
||
$(eval $(call BuildPackageGluon,gluon-mesh-vpn-wireguard-vxlan)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# gluon-mesh-vpn-wireguard-vxlan | ||
|
||
You can use this package for connecting with wireguard to the Freifunk Munich network. | ||
|
||
You should use something like the following in the site.conf: | ||
|
||
|
||
``` | ||
mesh_vpn = { | ||
mtu = 1400, | ||
wireguard = { | ||
enabled = '1', | ||
iface = 'mesh-vpn', | ||
limit = '1', -- actually unused | ||
broker = 'broker.ffmuc.net/api/v1/wg/key/exchange', | ||
peers = { | ||
{ | ||
publickey ='N9uF5Gg1B5AqWrE9IuvDgzmQePhqhb8Em/HrRpAdnlY=', | ||
endpoint ='ffkwsn01.freifunk-koenigswinter.de:30020', | ||
link_address = 'fe80::f000:22ff:fe12:01', | ||
}, | ||
{ | ||
publickey ='liatbdT62FbPiDPHKBqXVzrEo6hc5oO5tmEKDMhMTlU=', | ||
endpoint ='ffkwsn02.freifunk-koenigswinter.de:30020', | ||
link_address = 'fe80::f000:22ff:fe12:02', | ||
}, | ||
{ | ||
publickey ='xakSGG39D1v90j3Z9eVWzojh6nDbnsVUc/RByVdcKB0=', | ||
endpoint ='ffkwsn03.freifunk-koenigswinter.de:30020', | ||
link_address = 'fe80::f000:22ff:fe12:07', | ||
}, | ||
}, | ||
}, | ||
``` | ||
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) | ||
- [FFMUC: WireGuard Firmware (German)](https://ffmuc.net/freifunkmuc/2020/12/03/wireguard-firmware/) | ||
- [FFMUC: Statistics](https://stats.ffmuc.net) | ||
|
||
### Contact | ||
|
||
Feel free to ask questions in the [FFMUC chat](https://chat.ffmuc.net). |
Empty file.
120 changes: 120 additions & 0 deletions
120
ffmuc-gluon-mesh-vpn-wireguard-vxlan/files/lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#!/bin/sh | ||
|
||
if { set -C; 2>/dev/null >/var/lock/checkuplink.lock; }; then | ||
trap "rm -f /var/lock/checkuplink.lock" EXIT | ||
else | ||
echo "Lock file exists... exiting" | ||
exit | ||
fi | ||
|
||
interface_linklocal() { | ||
# We generate a predictable v6 address | ||
local macaddr="$(echo $(uci get wireguard.mesh_vpn.privatekey | wg pubkey) |md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')" | ||
local oldIFS="$IFS"; IFS=':'; set -- $macaddr; IFS="$oldIFS" | ||
echo "fe80::$1$2:$3ff:fe$4:$5$6" | ||
} | ||
|
||
clean_port() { | ||
echo "$(echo $1 | sed -r 's/:[0-9]+$|\[|\]//g')" | ||
} | ||
|
||
check_address_family() { | ||
local peer_publickey="$1" | ||
local peer_endpoint="$2" | ||
local gateway="$(clean_port $peer_endpoint)" | ||
# Check if we have a default route for v6 if not fallback to v4 | ||
defgw=$(ip -6 route show table 1 | grep 'default via') | ||
if [ "$?" -eq "0" ]; then | ||
local ipv6="$(gluon-wan nslookup $gateway | grep 'Address [0-9]' | egrep -o '([a-f0-9:]+:+)+[a-f0-9]+')" | ||
echo [$ipv6]$(echo $peer_endpoint | egrep -oe :[0-9]+$) | ||
else | ||
local 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 | egrep -oe :[0-9]+$) | ||
fi | ||
|
||
} | ||
|
||
# Do we already have a private-key? If not generate one | ||
temp=$(uci get wireguard.mesh_vpn.privatekey) | ||
if [ "$?" -ne "0" ]; then | ||
uci set wireguard.mesh_vpn.privatekey=$(wg genkey) | ||
uci commit wireguard | ||
fi | ||
|
||
# Is wireguard enabled? | ||
if [ "$(uci get wireguard.mesh_vpn.enabled)" == "true" ] || [ "$(uci get wireguard.mesh_vpn.enabled)" == "1" ]; then | ||
|
||
#We assume we are not connected by default | ||
CONNECTED=0 | ||
|
||
MESH_VPN_IFACE=$(uci get wireguard.mesh_vpn.iface) | ||
|
||
# Check connectivity to supernode | ||
wget http://[$(wg | grep fe80 | awk '{split($3,A,"/")};{print A[1]}')%$MESH_VPN_IFACE]/ --timeout=5 -O/dev/null -q | ||
if [ "$?" -eq "0" ]; then | ||
GWMAC=$(batctl gwl | grep \* | awk '{print $2}') | ||
batctl ping -c 5 $GWMAC &> /dev/null | ||
if [ "$?" -eq "0" ]; then | ||
CONNECTED=1 | ||
fi | ||
fi | ||
|
||
# If we don't have a connection we try to connect | ||
if [ "$CONNECTED" -ne "1" ]; then | ||
logger -t checkuplink "Reconnecting ..." | ||
NTP_SERVER=$(uci get system.ntp.server) | ||
gluon-wan /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd-hotplug -p $NTP_SERVER -q | ||
|
||
# Get the number of configured peers and randomly select one | ||
NUMBER_OF_PEERS=$(uci -q show wireguard | egrep -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 | ||
ip link delete dev mesh-vpn &> /dev/null | ||
ip link del $MESH_VPN_IFACE &> /dev/null | ||
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 | ||
wget -q https://[::1] | ||
if [ $? -eq 1 ]; then | ||
PROTO=http | ||
else | ||
PROTO=https | ||
fi | ||
gluon-wan wget -q -O- --post-data='{"domain": "'"$SEGMENT"'","public_key": "'"$PUBLICKEY"'"}' $PROTO://$(uci get wireguard.broker) | ||
|
||
# Bring up the wireguard interface | ||
ip link add dev $MESH_VPN_IFACE type wireguard | ||
wg set $MESH_VPN_IFACE fwmark 1 | ||
uci get wireguard.mesh_vpn.privatekey | wg set $MESH_VPN_IFACE private-key /proc/self/fd/0 | ||
ip link set up dev $MESH_VPN_IFACE | ||
|
||
# Add link-address and Peer | ||
ip address add "$(interface_linklocal "$MESH_VPN_IFACE")"/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 | ||
|
||
# We need to allow incoming vxlan traffic on mesh iface | ||
sleep 10 | ||
ip6tables -I INPUT 1 -i $MESH_VPN_IFACE -m udp -p udp --dport 8472 -j ACCEPT | ||
|
||
# Bring up VXLAN | ||
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 $(interface_linklocal "$MESH_VPN_IFACE") remote $(uci get wireguard.peer_$PEER.link_address) dstport 8472 dev $MESH_VPN_IFACE | ||
ip link set up dev mesh-vpn | ||
|
||
sleep 5 | ||
# If we have a BATMAN_V env we need to correct the throughput value now | ||
batctl hardif mesh-vpn throughput_override 1000mbit; | ||
fi | ||
fi |
Empty file.
1 change: 1 addition & 0 deletions
1
ffmuc-gluon-mesh-vpn-wireguard-vxlan/files/usr/lib/micron.d/gluon-mesh-wireguard-vxlan
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* * * * * sleep $(awk 'BEGIN{srand();print int(rand()*40)}') && /lib/gluon/gluon-mesh-wireguard-vxlan/checkuplink |
42 changes: 42 additions & 0 deletions
42
ffmuc-gluon-mesh-vpn-wireguard-vxlan/luasrc/lib/gluon/upgrade/400-mesh-vpn-wireguard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#!/usr/bin/lua | ||
|
||
local site = require 'gluon.site' | ||
local uci = require("simple-uci").cursor() | ||
|
||
local wg_enabled = uci:get_bool('wireguard', 'mesh_vpn', 'enabled') or false | ||
local privkey = uci:get("wireguard", "mesh_vpn", "privatekey") or "" | ||
|
||
-- Clean up previous configuration | ||
uci:delete_all('wireguard', 'peer', function(peer) | ||
return peer.preserve ~= '1' | ||
end) | ||
-- Clean up previous configuration | ||
uci:delete_all('wireguard', 'wireguard', function(peer) | ||
return peer.preserve ~= '1' | ||
end) | ||
|
||
local mesh_enabled = uci:get_bool('gluon', 'mesh_vpn', 'enabled') -- default | ||
or uci:get_bool('fastd', 'mesh_vpn', 'enabled') --migration | ||
or wg_enabled -- specific config | ||
|
||
uci:set('gluon', 'mesh_vpn', 'enabled', mesh_enabled) | ||
|
||
uci:section("wireguard", "wireguard", "mesh_vpn", { | ||
iface = site.mesh_vpn.wireguard.iface(), | ||
limit = site.mesh_vpn.wireguard.limit(), | ||
broker = site.mesh_vpn.wireguard.broker(), | ||
enabled = mesh_enabled, | ||
privatekey = privkey, | ||
}) | ||
|
||
for name, peer in pairs(site.mesh_vpn.wireguard.peers()) do | ||
uci:section("wireguard", "peer", "peer_" .. name, { | ||
enabled = true, | ||
endpoint = peer.endpoint, | ||
publickey = peer.publickey, | ||
link_address = peer.link_address, | ||
}) | ||
end | ||
|
||
uci:save('wireguard') | ||
uci:save('gluon') |
36 changes: 36 additions & 0 deletions
36
...c-gluon-mesh-vpn-wireguard-vxlan/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/wireguard.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
local uci = require('simple-uci').cursor() | ||
|
||
local site = require 'gluon.site' | ||
local util = require 'gluon.util' | ||
local vpn_core = require 'gluon.mesh-vpn' | ||
|
||
local M = {} | ||
|
||
function M.public_key() | ||
return util.trim(util.exec('/usr/bin/wg show wg_mesh_vpn public-key')) | ||
end | ||
|
||
function M.enable(val) | ||
uci:set('wireguard', 'mesh_vpn', 'enabled', val) | ||
uci:save('wireguard') | ||
end | ||
|
||
function M.active() | ||
return site.mesh_vpn.wireguard() ~= nil | ||
end | ||
|
||
function M.set_limit(ingress_limit, egress_limit) | ||
uci:delete('simple-tc', 'mesh_vpn') | ||
if ingress_limit ~= nil and egress_limit ~= nil then | ||
uci:section('simple-tc', 'interface', 'mesh_vpn', { | ||
ifname = vpn_core.get_interface(), | ||
enabled = true, | ||
limit_egress = egress_limit, | ||
limit_ingress = ingress_limit, | ||
}) | ||
end | ||
|
||
uci:save('simple-tc') | ||
end | ||
|
||
return M |