#!/usr/bin/env bash # Takes over antigua's three WireGuard tunnels (freifunk, family, mobile-clients) # on a router installed from the generic installer. Run as root on the router. # # import-antigua-wireguard.sh KEYDIR # # KEYDIR holds the secret files copied from antigua's /opt/wireguard: # freifunk.priv family.priv mobile-clients.priv troisdorf.psk mobile-clients.psk # e.g. from antigua: # tar -C /opt/wireguard -czf - freifunk.priv family.priv mobile-clients.priv \ # troisdorf.psk mobile-clients.psk | ssh root@ROUTER 'mkdir -p /root/wg && tar -xzf - -C /root/wg' # # The keys are installed as 0600 files in /var/lib/router/wireguard; the tunnels # (public settings only), the firewall rules between them and the LAN are # merged into settings.json. Nothing is applied: look at the WireGuard page, # then `router-apply` and `router-confirm`. Never run both boxes with the same # keys at once: stop the tunnels on antigua first. # # The tunnels are those of examples/home, translated from antigua's # wireguard.nix: freifunk accepts 0.0.0.0/0 there but routes just the three # treasure-maps.com hosts (masqueraded); the road-warrior tunnel is called # `mobile` (the interface name wg-mobile-clients would be too long). set -euo pipefail state=${STATE_DIR:-/var/lib/router} settings=${SETTINGS:-$state/config/settings.json} keys=${1:-} [ -d "$keys" ] || { echo "usage: $0 KEYDIR" >&2; exit 2; } [ "$(id -u)" -eq 0 ] || [ -n "${STATE_DIR:-}" ] || { echo "must run as root" >&2; exit 1; } command -v jq >/dev/null || { echo "jq is needed" >&2; exit 1; } [ -f "$settings" ] || { echo "no $settings" >&2; exit 1; } # antigua's file -> the name it gets here. declare -A install=( [freifunk.priv]=freifunk.key [family.priv]=family.key [mobile-clients.priv]=mobile.key [troisdorf.psk]=family-troisdorf.psk [mobile-clients.psk]=mobile-laptop.psk ) for f in "${!install[@]}"; do [ -s "$keys/$f" ] || { echo "missing $keys/$f" >&2; exit 1; } # A WireGuard key is 32 bytes in base64: 44 characters. [[ $(tr -d '[:space:]' < "$keys/$f") =~ ^[A-Za-z0-9+/]{43}=$ ]] || { echo "$f is not a WireGuard key" >&2; exit 1; } done if jq -e '.router.wireguard // {} | has("freifunk") or has("family") or has("mobile")' "$settings" >/dev/null; then echo "settings.json has a freifunk, family or mobile tunnel already; not touching it" >&2 exit 1 fi lan=$(jq -r '[.router.lans[]?.ipv4.address][0] // empty' "$settings") lan_zone=$(jq -r '[.router.lans // {} | to_entries[] | (.value.zone // .key)][0] // empty' "$settings") [ -n "$lan" ] && [ -n "$lan_zone" ] || { echo "settings.json has no LAN" >&2; exit 1; } dest=$state/wireguard install -d -m 0700 "$dest" for f in "${!install[@]}"; do (umask 077; tr -d '[:space:]' < "$keys/$f" > "$dest/${install[$f]}"; echo >> "$dest/${install[$f]}") done cp -a "$settings" "$settings.before-wireguard-import" jq --arg dest "$dest" --arg lan "$lan" --arg lanzone "$lan_zone" ' .router.wireguard = ((.router.wireguard // {}) + { freifunk: { privateKeyFile: "\($dest)/freifunk.key", masquerade: true, addresses: ["10.255.1.8/32", "fd80:3ea2:e399:203a::8/128"], peers: { supernode: { publicKey: "5B/YTaDPVWVApUyHshJp899iXXlBy8rBqJUpYvKo+1s=", endpoint: "5.9.220.114:42001", persistentKeepalive: 15, allowedIPs: ["185.90.196.65/32", "185.90.196.97/32", "185.90.196.129/32"] } } }, family: { privateKeyFile: "\($dest)/family.key", listenPort: 36432, allowManagement: true, dns: true, sourceAddresses: [$lan], peers: { troisdorf: { publicKey: "BsQODhcHnEN4tXn60t72iejZvgYvVNt5BDVK+69+RgM=", endpoint: "fw-tdf.stinnesbeck.com:36432", persistentKeepalive: 25, presharedKeyFile: "\($dest)/family-troisdorf.psk", allowedIPs: ["10.1.0.0/24", "fd73:f65e:6b64::/48"] } } }, mobile: { privateKeyFile: "\($dest)/mobile.key", listenPort: 36433, allowManagement: true, dns: true, peers: { laptop: { publicKey: "Jts5u8zIs28v6RIEYCCufDDiWXf+gbElT63wUFWQxX4=", persistentKeepalive: 25, presharedKeyFile: "\($dest)/mobile-laptop.psk", allowedIPs: ["10.2.0.2/32"] } } } }) # antigua trusted these interfaces completely; spelled out: the LAN and # Troisdorf reach each other, the LAN reaches the freifunk hosts, road # warriors reach the LAN. Nothing initiates from freifunk. | .router.firewall.rules = ((.router.firewall.rules // []) + [ { from: $lanzone, to: "family" }, { from: "family", to: $lanzone }, { from: $lanzone, to: "freifunk" }, { from: "mobile", to: $lanzone } ])' "$settings.before-wireguard-import" > "$settings.new" chmod 600 "$settings.new" mv "$settings.new" "$settings" echo "Imported freifunk, family and mobile (keys in $dest)." echo "Look at the WireGuard page, then: router-apply, and router-confirm when the LAN still works." echo "The previous settings are in $settings.before-wireguard-import. Remove $keys when done."