Skip to content

Commit

Permalink
Updated: checkout action
Browse files Browse the repository at this point in the history
ffka-gluon-web-usb-wan-hotplug: i18n/de.po: fix typo (#13)

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]>

Update Makefile
  • Loading branch information
GoliathLabs authored and awlx committed Oct 18, 2021
1 parent 70dbfdc commit 97b3ab3
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: Lua
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Install Dependencies
run: sudo apt-get install lua-check
- name: Lint Lua
Expand Down
4 changes: 2 additions & 2 deletions ffka-gluon-web-usb-wan-hotplug/i18n/de.po
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ msgstr "USB-WAN-Hotplug"
msgid "ffka-gluon-web-usb-wan-hotplug:description"
msgstr ""
"Dein Knoten kann automatisch einen per USB angeschlossen Ethernet-Adapter als WAN benutzen. "
"Das ist insbesondere Praktisch wenn als WAN der mobile Hotspot eines per USB verbundenen "
"Das ist insbesondere praktisch wenn als WAN der mobile Hotspot eines per USB verbundenen "
"Mobiltelefons verwendet werden soll. Für iOS wird automatisch die Trust-Frage ausgelöst, "
"wenn diese positiv beantwortet wird stellt auch iOS ein Ethernet Gerät über USB zur verfügung."
"wenn diese positiv beantwortet wird stellt auch iOS ein Ethernet Gerät über USB zur Verfügung."
17 changes: 17 additions & 0 deletions ffmuc-gluon-mesh-vpn-wireguard-vxlan/Makefile
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,ffmuc-gluon-mesh-vpn-wireguard-vxlan))
59 changes: 59 additions & 0 deletions ffmuc-gluon-mesh-vpn-wireguard-vxlan/README.md
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.
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.
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
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')
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

0 comments on commit 97b3ab3

Please sign in to comment.