commit 804008933d66a6e7d177fcbc59ed24ef33903313 Author: redxef Date: Thu Jun 15 01:05:12 2023 +0200 Initial commit. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af87c9e --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +SCRIPT := gre-on-wg.sh +SCRIPT_NAME := $(SCRIPT:.sh=) +PREFIX ?= /usr/local + +install: install-bin + +install-bin: $(SCRIPT) + install -D -m 0755 -o root -g root -T $(SCRIPT) $(PREFIX)/bin/$(SCRIPT_NAME) + +.PHONY: install install-bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..1cc9f0c --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# gre-on-wg + +Create a GRETAP mesh network on top of wireguard. + +## Usage + +`gre-on-wg WG_QUICK_CONFIG_FILE ADDRESS_TRANSLATIONS` + +The first argument is the path to a wg-quick config file, while the +second argument provides address translations for wireguard internal ip +addresses to the new addresses. + +The wireguard configuration must have the `Address` config in the +`[Interface]` section set to exactly **one** address, similarly every +`[Peer]` section may only contain **one** AllowedIPs entry with **one** +addresss. + +The translation file should contain lines of the following format: +` ` + +### Example + +Consider the following wg0.conf file + +``` +[Interface] +Address = 192.168.3.100/24 + +[Peer] +AllowedIPs = 192.168.3.101/32 + +[Peer] +AllowedIPs = 192.168.3.102/32 + +[Peer] +AllowedIPs = 192.168.3.103/32 +``` + +And the following translation file + +``` +192.168.3.100/24 192.168.4.100/24 +192.168.3.101/24 192.168.4.101/24 +192.168.3.102/24 192.168.4.102/24 +192.168.3.103/24 192.168.4.103/24 +``` + +This would result in the creation of the bridge network `wg0br1` on host +`192.168.3.100` where the different peers have the addresses +`192.168.4.10[0123]`. Additionally the point-to-point networks +`wg0gre[1234]` would be created. diff --git a/gre-on-wg.sh b/gre-on-wg.sh new file mode 100755 index 0000000..824458a --- /dev/null +++ b/gre-on-wg.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +set -euo pipefail + +strstrip() { + sed -E -e 's/^\s*//' -e 's/\s*$//' +} + +create_br() { + local name + name="$1" + + $DEBUG ip link add "$name" type bridge +} + +create_gre() { + local name local_ip remote_ip + name="$1" + local_ip="$(sed 's_/.*$__' <<< "$2")" + remote_ip="$(sed 's_/.*$__' <<< "$3")" + + $DEBUG ip link add "$name" type gretap local "$local_ip" remote "$remote_ip" + $DEBUG ip link set up "$name" +} + +add_gre_to_br() { + local br_name gre_name + br_name="$1" + gre_name="$2" + + $DEBUG ip link set "$gre_name" master "$br_name" +} + +finish_br() { + local name local_ip + name="$1" + local_ip="$2" + + $DEBUG ip addr add "$local_ip" dev "$name" + $DEBUG ip link set up "$name" +} + +read_wg_conf() { + local filepath + local local_ip remote_ips mode line + filepath="$1" + + local_ip= + remote_ips= + mode= + while read -r line; do + line="$(strstrip <<< "$line")" + if [[ "$line" = '[Interface]' ]]; then + mode=interface + continue + elif [[ "$line" = '[Peer]' ]]; then + mode=peer + continue + fi + if [[ "$mode" = 'interface' ]] && [[ "$line" =~ ^Address\ *=\ * ]]; then + local_ip="$(awk -F= '{print $2}' <<< "$line" | strstrip)" + elif [[ "$mode" = 'peer' ]] && [[ "$line" =~ ^AllowedIPs\ *=\ * ]]; then + remote_ips="$remote_ips $(awk -F= '{print $2}' <<< "$line" | strstrip)" + fi + done < "$filepath" + remote_ips="$(strstrip <<< "$remote_ips")" + + echo "$local_ip" + echo "$remote_ips" +} + +create_networks() { + local wg_name local_ip local_ip_trans remote_ips + local br_name n + wg_name="$1" + local_ip="$2" + local_ip_trans="$3" + remote_ips="$4" + + br_name="${wg_name}br1" + n=1 + + create_br "$br_name" + for remote_ip in $remote_ips ; do + create_gre "${wg_name}gre$n" "$local_ip" "$remote_ip" + add_gre_to_br "$br_name" "${wg_name}gre$n" + n=$((n+1)) + done + + finish_br "$br_name" "$local_ip_trans" +} + +main() { + local filepath translation_filepath + local r local_ip remote_ips local_ip_trans + filepath="$1" + if [[ "$filepath" =~ .*/.* ]]; then + # path, not a name, leave as is + : + else + filepath="/etc/wireguard/$filepath" + fi + translation_filepath="$(sort <<< "$2" | uniq)" + + r="$(read_wg_conf "$filepath")" + local_ip="$(head -n1 <<< "$r")" + remote_ips="$(tail -n1 <<< "$r")" + unset r + + local_ip_trans="$(grep -E "$(sed 's_\._\\._g' <<< "$local_ip")" "$translation_filepath" | awk '{print $2}')" + + create_networks "$(basename --suffix='.conf' "$filepath")" "$local_ip" "$local_ip_trans" "$remote_ips" +} + +main "$@"