Skip to content

Instantly share code, notes, and snippets.

@sjlongland
Created May 26, 2024 08:11
Show Gist options
  • Save sjlongland/de25064530b3f8ce1ab441a51e9420f1 to your computer and use it in GitHub Desktop.
Save sjlongland/de25064530b3f8ce1ab441a51e9420f1 to your computer and use it in GitHub Desktop.
Crude mDNS / Wireguard based point-to-point VPN

What is this?

It's a very crude script that allows two machines that share a possibly untrusted network (e.g. WiFi) to connect to each-other and establish a secure tunnel.

How does it work?

The assumption is both machines can see each-other via multicast DNS, and can send UDP traffic between each-other. If you're using a WiFi network that isolates its clients, you're out of luck.

How does one set this up?

Enabling mDNS

Firstly, you must enable mDNS for resolving hosts, something like:

hosts:          files mdns4_minimal [NOTFOUND=return] dns

in your /etc/nsswitch.conf will get you started.

You'll also need something like libnss-mdns installed.

Setting link parameters

We define a configuration file; /etc/wg0.conf with the settings for our peer network:

INTERFACE=wg0
SUBNET_IP=2001:db8:aaaa:bbbb::  # pro-tip, generate a ULA for this!
SUBNET_SZ=64
LISTEN_PORT=51820
PERSISTENT_KEEPALIVE=60

Defining your peers

You need to generate keypairs for all your nodes, then create a file for each node in /etc/wg.peers:

ip=2001:db8:aaaa:bbbb::1
pubkey=q2DV2U45a5JLzEFScIZLs7CMeV0kZePOl8n15phACrki7Vha

Keep the ips unique!

Periodically polling for the peer and establishing the VPN

The vpn-up.sh script is run each minute from cron. (Yes, very hacky!)

* * * * * root /usr/local/sbin/vpn-up.sh >> /tmp/vpn.log 2>&1
#!/bin/bash
. /etc/wg0.conf
if [ -d /sys/class/net/${INTERFACE} ]; then
ip link set ${INTERFACE} down
ip link delete dev ${INTERFACE}
sed -i -e "/^${SUBNET_IP}/ d" /etc/hosts
fi
#!/bin/bash
set -e
. /etc/wg0.conf
# Pick up my IP details and private key
ME=$( uname -n ).local
MY_IP=$( . /etc/wg.peers/${ME} ; echo ${ip} )
# Gather command line arguments
declare -A static_peers
while [ $# -gt 0 ]; do
case "$1" in
--restart)
# Re-start the VPN service
vpn-down.sh
shift
;;
*=*) # Peer address
static_peers["${1%=*}"]="${1#*=}"
shift
;;
*)
echo "Unrecognised argument: $1"
exit 1
esac
done
# Abort if we already have our interface
if [ -d /sys/class/net/${INTERFACE} ]; then
exit 0
fi
# Gather the cryptography configuration settings
peers=""
reachable=0
for peerfile in /etc/wg.peers/*; do
peer=$( basename ${peerfile} )
if [ "${peer}" != ${ME} ]; then
# Derive a host name for the endpoint on the VPN
host=${peer%.local}
vpn_hostname=${host}.vpn
# Do we have an endpoint IP given on the command line?
endpoint=${static_peers[${peer}]}
if [ -n "${endpoint}" ]; then
if [ "${endpoint}" != listen ]; then
# Given an IP/name, add brackets around IPv6, add port number if needed.
endpoint=$(
echo "${endpoint}" | sed \
-e 's/^[0-9a-f]\+:[0-9a-f]\+:[0-9a-f:]\+$/[&]/' \
-e "s/^\\(\\[[0-9a-f:]\\+\\]\\|[0-9\\.]\+\\)\$/\1:${LISTEN_PORT}/"
)
fi
# If we have an endpoint, assume it's reachable
reachable=$(( ${reachable} + 1 ))
elif [ -z "${endpoint}" ]; then
# Try to resolve the IP address for the peer
# Ignore link-local and VPN tunnel!
endpoint_ip=$(
getent hosts ${peer} \
| cut -f 1 -d' ' \
| grep -v "^\(fe80:\|169\.254\.\|${SUBNET_IP}\)"
)
if ping -n -w 20 -c 1 ${endpoint_ip}; then
# Endpoint is reachable. Construct endpoint argument
endpoint=$( echo ${endpoint_ip} | sed -e '/:/ s/^.*$/[&]/' ):${LISTEN_PORT}
# We know this endpoint is reachable
reachable=$(( ${reachable} + 1 ))
else
# Not reachable, listen for it connecting
endpoint=listen
fi
fi
# Pick up peer pubkey and VPN IP
. ${peerfile}
# Add to peers
peers="${peers} peer ${pubkey}"
if [ "${endpoint}" != "listen" ]; then
peers="${peers} endpoint ${endpoint}"
fi
peers="${peers} persistent-keepalive ${PERSISTENT_KEEPALIVE}"
peers="${peers} allowed-ips ${SUBNET_IP}/${SUBNET_SZ}"
if ! grep -q "${vpn_hostname} ${host}\\$" /etc/hosts ; then
# Add to /etc/hosts
echo "${ip} ${vpn_hostname} ${host}" >> /etc/hosts
else
# Update /etc/hosts
sed -i -e "/${vpn_hostname} ${host}\\$/ s/^[^ ]\+/${ip}/" \
/etc/hosts
fi
fi
done
# Abort if no peers are reachable
if [ ${reachable} = 0 ]; then
exit 0
fi
# Create the interface
ip link add ${INTERFACE} type wireguard
# Configre the cryptographic settings
wg set ${INTERFACE} listen-port ${LISTEN_PORT} \
private-key /etc/wg.priv ${peers}
# Bring the interface up
ip -6 addr add ${MY_IP}/${SUBNET_SZ} dev ${INTERFACE}
ip link set ${INTERFACE} up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment