Skip to content

Instantly share code, notes, and snippets.

@rlcamp
Last active April 14, 2023 17:38
Show Gist options
  • Save rlcamp/e3f1e99a4d9dee51c88bc2a5f4b68b13 to your computer and use it in GitHub Desktop.
Save rlcamp/e3f1e99a4d9dee51c88bc2a5f4b68b13 to your computer and use it in GitHub Desktop.
#!/bin/bash
# campbell, 2022-2023
# Given a mesh of already-mutually-known peers, initially not fully connected with non-stale
# handshakes, this script will propagate knowledge of current endpoints to peers which need
# them, such that in the steady state, the network is fully-connected and will self-heal if
# an endpoint moves around.
# This conservative implementation requires that a peer already be known to wireguard in
# order to have its endpoint updated. No provision is made for having peers forward traffic
# indirectly, if a direct handshake cannot be made. The scheme is meant to stay out of the
# way of other static configuration of the same wireguard interface. To configure a peer for
# eligibility to have its endpoint updated via this scheme, simply ensure it has a leaf CIDR
# and a defined PersistentKeepalive.
# Details: each node implements a transmit and receive thread. The transmit thread
# periodically examines "wg show $IFACE dump" and finds peers with non-stale handshakes, leaf
# CIDRs, and defined PersistentKeepalive, and sends one UDP packet (within the wg network) to
# each of these peers, containing the public key and endpoint of each other such peer. The
# receive thread listens for such packets on $IFACE, and if the given public key matches a
# known peer with a stale handshake meeting the CIDR and PersistentKeepalive criteria, the
# endpoint is updated.
IFACE=$1
# thorough cleanup of child processes on exit
trap "trap - SIGTERM && kill -- -$$" SIGTERM SIGINT EXIT
while true; do
# for each pair of peers with a nonzero persistent-keepalive, non-LAN endpoint, NON-stale handshake, and leaf cidr ...
wg show "$IFACE" dump | awk '
$8 + 0 && $3 !~ /^10\./ && $5 + 185 > systime() && match($4, /[a-f0-9.:]+\/(32|128)/) {
ip = substr($4, RSTART, RLENGTH);
sub(/\/.*$/, "", ip);
a[$1] = (ip ~/:/ ? "[" ip "]" : ip);
e[$1] = $3
}
END {
for (p in a)
for (q in a)
if (p != q)
system("printf '\''" p " " e[p] "\n'\'' | socat -u STDIN UDP-SENDTO:" a[q] ":45153")
}'
sleep 60
done &
while read -r KEY EP; do
# if the pubkey is a known peer with nonzero persistent-keepalive, non-local IP, stale handshake, and leaf CIDR ...
wg show "$IFACE" dump | awk '$8 + 0 && $3 !~ /^10\./ && $5 + 185 < systime() && $4 ~/\/(32|128)/' | grep "$KEY" >/dev/null &&
wg set "$IFACE" peer "$KEY" endpoint "$EP" && printf 'set %s to %s\n' "$KEY" "$EP" >&2
done < <(exec socat -u UDP-RECV:45153,reuseaddr,so-bindtodevice="$IFACE" STDOUT)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment