Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/rework wg broker server #12

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/config
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
BROKERPORT=40000 # tcp port where the broker listens for connections
STARTPORT=40000 # start of udp portrange accepting wireguard connections
ENDPORT=41000 # end of udp portrange accepting wireguard connections
WIREGUARDPORT=40001 # udp port accepting wireguard connections
BABELPORT=33123 # control port of local babeld
MTU=1374 # MTU of VPN in this network
WG="wg" # wireguard command
Expand All @@ -9,5 +8,3 @@ MAXCONNECTIONS=150 # allow MAXCONNECTIONS concurrent vpn connections
PRIVATEKEY=/etc/wg-broker/secret # this file contains the secret key
L3ROAMDSOCK=/var/run/l3roamd.sock # this is the l3roamd socket
MMFDSOCK=/var/run/mmfd.sock # this is the mmfd socket


228 changes: 87 additions & 141 deletions src/wg-broker-server
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#
# Copyright (C) Christof Schulze <[email protected]>. All Rights Reserved.
#
nic_basename="babel-wg-"
nic_name="babel-wg"

declare -A INACTIVE

if [[ -z $WAN ]] || [[ -z $MAXCONNECTIONS ]] || [[ -z $BROKERPORT ]] ||
[[ -z $STARTPORT ]] || [[ -z $ENDPORT ]] || [[ -z $BABELPORT ]] || [[ -z $WG ]]
[[ -z $WIREGUARDPORT ]] || [[ -z $BABELPORT ]] || [[ -z $WG ]]
[[ -z $MTU ]] || [[ -z $PRIVATEKEY ]] || [[ -z $L3ROAMDSOCK ]] || [[ -z $MMFDSOCK ]]
then
if [[ -f /etc/wg-broker/config ]]; then
Expand All @@ -30,70 +30,38 @@ set -x
cleanup() {
EXITING=true
rm -f /tmp/wg-broker
for i in $(get_wg_interfaces)
do
del_interface ${i}
done
del_interface ${nic_name}
exit 0
}

trap cleanup exit

get_wg_interfaces() {
$WG show interfaces |sed 's/ /\n/g'|grep "${nic_basename}"
get_wg_peers() {
$WG show ${nic_name} | grep peer |sed 's/peer://g' | tr '\n' ' '
}

get_connection_count() {
interfaces=( $(get_wg_interfaces) )
echo ${#interface[@]}
}

get_port() {
$WG show ${1:-all} listen-port
}

port_is_assigned() {
get_port | grep -q "$1"
}

find_free_port() {
local port=$((STARTPORT + $RANDOM%1000)) # adding RANDOM to not assign ports sequentially and save some time in below loop
local sport=$port #

while port_is_assigned $port
do
port=$((port+1))
[[ $port -eq $ENDPORT ]] && port=$STARTPORT
[[ $port -eq $sport ]] &&
{
echo 0
return
}
done

echo $port
peers=( $(get_wg_peers) )
echo ${#peers[@]}
}

add_interface() {
local newdevice="$1"
local freeport="$2"
local public_key="$3"

unset INACTIVE[$newdevice]

ip link add dev $newdevice type wireguard
ip link set multicast on mtu $MTU dev $newdevice
devicenumber=${ifname##*-}
ip a a fe80::$devicenumber/64 dev $newdevice

$WG set $newdevice private-key $PRIVATEKEY listen-port $freeport peer $public_key allowed-ips ::/0
# ip6tables -A INPUT -i $newdevice -p udp --dport 6696 -j ACCEPT
# ip6tables -A INPUT -i $WAN -p udp --dport $freeport -j ACCEPT
# iptables -A INPUT -i $WAN -p udp --dport $freeport -j ACCEPT
ip link set up dev $newdevice
echo add_meshif "$1" | socat - unix:$L3ROAMDSOCK
echo add_meshif "$1" | socat - unix:$MMFDSOCK
echo interface $newdevice | timeout 0.2 nc ::1 $BABELPORT >/dev/null
local newdevice="${nic_name}"

unset INACTIVE[$newdevice]

ip link add dev $newdevice type wireguard
ip link set multicast on mtu $MTU dev $newdevice
devicenumber=${ifname##*-}
ip a a fe80::$devicenumber/64 dev $newdevice

# ip6tables -A INPUT -i $newdevice -p udp --dport 6696 -j ACCEPT
# ip6tables -A INPUT -i $WAN -p udp --dport $freeport -j ACCEPT
# iptables -A INPUT -i $WAN -p udp --dport $freeport -j ACCEPT
ip link set up dev $newdevice
echo add_meshif "$1" | socat - unix:$L3ROAMDSOCK
echo add_meshif "$1" | socat - unix:$MMFDSOCK
echo interface $newdevice | timeout 0.2 nc ::1 $BABELPORT >/dev/null
}

del_interface() {
Expand All @@ -106,105 +74,86 @@ del_interface() {
} >&2
}

nic_namegen() {
local number=1
add_peer() {
local public_key="$1"
unset INACTIVE[$public_key]

while $(get_wg_interfaces|grep -q "${nic_basename}$number")
do
((number+=1))
done
echo "${nic_basename}$number"
$WG set ${nic_name} private-key $PRIVATEKEY listen-port $WIREGUARDPORT peer $public_key allowed-ips ::/0
}

prune_ifs() {
local interface="$1"
del_peer () {
local peer="$1"
$WG ${nic_name} set peer $peer remove
}

# delete interface if its peers last_handshake is > 10 Minutes ago
lhandshake=$($WG show $interface latest-handshakes|awk '{print $2}')
prune_peers() {
local lines=$WG show ${nic_name} latest-handshakes
for line in "${lines[@]}"; do
read -a strarr <<< "$line"
peer="${strarr[0]}"
lhandshake="${strarr[1]}"
age=$(($(date +%s)-lhandshake))

if (( (lhandshake > 0 && age > 600) || (lhandshake == 0 && "${INACTIVE[$interface]:-0}" == 1) ))
if (( (lhandshake > 0 && age > 600) || (lhandshake == 0 && "${INACTIVE[$peer]:-0}" == 1) ))
then
del_interface $interface
return
elif [[ $((lhandshake)) -eq 0 ]] && [[ -z ${INACTIVE[$interface]} ]]
del_peer $peer
return
elif [[ $((lhandshake)) -eq 0 ]] && [[ -z ${INACTIVE[$peer]} ]]
then
INACTIVE[$interface]=1
INACTIVE[$peer]=1
fi
}

find_if() {
local peer="$1"

for i in $(get_wg_interfaces)
do
if wg show $i peers |grep -q $peer
then
echo $i
return
fi
done
done
}

handle_connection() {
family=$1
read -r REPLY
family=$1
read -r REPLY

local pkey=$(jq -r ".pubkey" <<<"$REPLY")

# TODO this block looks wonky
if [[ -n $pkey ]]
then

if [[ ! $(base64 -d <<<"$pkey" 2>/dev/null |wc -c) -eq 32 ]]
then
echo "invalid public key" >&2
return
fi

local pkey=$(jq -r ".pubkey" <<<"$REPLY")
ifname=${nic_name}
port=${WIREGUARDPORT}

if [[ -n $pkey ]]
then
if [[ ! $(base64 -d <<<"$pkey" 2>/dev/null |wc -c) -eq 32 ]]
then
echo "invalid public key" >&2
return
fi

ifname=$(find_if $pkey)

if [[ -z $ifname ]]
then
port=$(find_free_port)
if [[ $port -eq 0 ]]
then
echo "NOT handling the current connection - Unable to find unused port within limits." >&2
fi
ifname=$(nic_namegen)
else
port=$(wg show "$ifname" listen-port)
fi

success=0
if [[ $port -eq 0 ]] || [[ $(get_connection_count) -gt $MAXCONNECTIONS ]]
then
response="{\"version\":1, \"error\": { \"code\":1, \"reason\": \"Remote peer is not accepting additional connections\"} }"
else
response="{\"version\":1, \"port\": $port, \"time\": $(date +%s) }"
success=1
fi

echo "working on $ifname, write to peer: $response" >&2
del_interface "$ifname"

[[ "$success" == "1" ]] && add_interface "$ifname" "$port" "$pkey"
echo "$response"
fi
success=0
[[ $(get_connection_count) -gt $MAXCONNECTIONS ]]
then
response="{\"version\":1, \"error\": { \"code\":1, \"reason\": \"Remote peer is not accepting additional connections\"} }"
else
response="{\"version\":1, \"port\": $port, \"time\": $(date +%s) }"
success=1
fi

echo "working on $ifname, write to peer: $response" >&2
# del_interface "$ifname"

[[ "$success" == "1" ]] && add_peer "$pkey"
echo "$response"
fi
}

allow_udp_on_wg_interfaces() {
local port=$1
nics=$(nic_namegen)
nicbase=${nics%%-[0-9]*}
ip6tables -nL -v| grep udp | grep $nicbase | grep $port | grep -q ACCEPT || \
ip6tables -I INPUT 1 -i ${nicbase}+ -p udp --dport $port -j ACCEPT
ip6tables -nL -v| grep udp | grep ${nic_name} | grep $port | grep -q ACCEPT || \
ip6tables -I INPUT 1 -i ${nic_name} -p udp --dport $port -j ACCEPT
}

# TODO is this the right place to initialize the wg interface?
add_interface

# TODO prettify this
iptables -nL -v|grep dpt:$BROKERPORT|grep tcp|grep $WAN |grep -q ACCEPT || iptables -A INPUT -i $WAN -p tcp --dport $BROKERPORT -j ACCEPT
ip6tables -nL -v|grep dpt:$BROKERPORT|grep tcp|grep $WAN |grep -q ACCEPT || ip6tables -A INPUT -i $WAN -p tcp --dport $BROKERPORT -j ACCEPT
iptables -nL -v|grep udp |grep $WAN|grep $STARTPORT:$ENDPORT -q || iptables -A INPUT -i $WAN -p udp -m multiport --dports $STARTPORT:$ENDPORT -j ACCEPT
ip6tables -nL -v|grep udp |grep ${WAN}|grep $STARTPORT:$ENDPORT -q || ip6tables -A INPUT -i $WAN -p udp -m multiport --dports $STARTPORT:$ENDPORT -j ACCEPT
iptables -nL -v|grep udp |grep $WAN|grep $WIREGUARDPORT -q || iptables -A INPUT -i $WAN -p udp -m multiport --dports $WIREGUARDPORT -j ACCEPT
ip6tables -nL -v|grep udp |grep $WAN|grep $WIREGUARDPORT -q || ip6tables -A INPUT -i $WAN -p udp -m multiport --dports $WIREGUARDPORT -j ACCEPT

# allow babeld, l3roamd and mmfd on wireguard interfaces
allow_udp_on_wg_interfaces 5523
Expand All @@ -218,27 +167,24 @@ mkfifo /tmp/wg-broker6
rm -f "/var/lock/${0##*/}"

{
while [[ -z $EXITING ]]
do
for i in $(get_wg_interfaces)
do
prune_ifs "$i"
done
sleep 300
done
while [[ -z $EXITING ]]
do
prune_peers
sleep 300
done
} &

{
while [[ -z $EXITING ]]
do
cat /tmp/wg-broker6 | handle_connection 6 | nc -6 -w 1 -l $BROKERPORT > /tmp/wg-broker6
cat /tmp/wg-broker6 | handle_connection 6 | nc -6 -w 1 -l $BROKERPORT > /tmp/wg-broker6
done
} &

{
while [[ -z $EXITING ]]
do
cat /tmp/wg-broker4 | handle_connection 4 | nc -4 -w 1 -l $BROKERPORT > /tmp/wg-broker4
cat /tmp/wg-broker4 | handle_connection 4 | nc -4 -w 1 -l $BROKERPORT > /tmp/wg-broker4
done
} &

Expand Down