feat(@scripts): ✨ add phone bootstrap and peer setup scripts
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
8a222d547d
commit
33094b9c05
2 changed files with 204 additions and 0 deletions
60
bin/quinn-phone-bootstrap
Executable file
60
bin/quinn-phone-bootstrap
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/sh
|
||||
# quinn-phone-bootstrap — one-shot end-to-end setup for a phone (or tablet) to
|
||||
# reach the home LAN via the wg1 mesh, with .local resolution.
|
||||
#
|
||||
# What it runs (in order):
|
||||
# 1. wg-dns-sync on apricot — installs/updates dnsmasq wg-mesh.conf,
|
||||
# so the phone resolves *.apricot.local etc.
|
||||
# Requires interactive sudo on apricot
|
||||
# (uses ssh -t to forward your tty).
|
||||
# 2. wg-phone-add (locally) — generates or reuses the device's keypair,
|
||||
# adds peer to quinn-vps wg1 hub, prints QR.
|
||||
#
|
||||
# Idempotent: re-runs are no-ops where possible. Use --device to onboard a new
|
||||
# device (default: phone-quinn).
|
||||
#
|
||||
# Usage:
|
||||
# quinn-phone-bootstrap # full setup, default device
|
||||
# quinn-phone-bootstrap -d ipad-quinn # onboard a new device
|
||||
# quinn-phone-bootstrap --skip-dns # skip the apricot dnsmasq step
|
||||
# quinn-phone-bootstrap --show -d phone-quinn # just re-render the QR
|
||||
#
|
||||
# Run interactively (so apricot's sudo can prompt):
|
||||
# ! quinn-phone-bootstrap
|
||||
|
||||
set -eu
|
||||
|
||||
device="phone-quinn"
|
||||
skip_dns=0
|
||||
show_only=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-d) device=$2; shift 2 ;;
|
||||
--skip-dns) skip_dns=1; shift ;;
|
||||
--show) show_only=1; shift ;;
|
||||
-h|--help) sed -n '2,21p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
script_dir=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
if [ "$show_only" -eq 0 ] && [ "$skip_dns" -eq 0 ]; then
|
||||
echo "===== step 1/2: sync dnsmasq on apricot ====="
|
||||
# ssh -t so apricot's sudo can prompt against the user's tty.
|
||||
ssh -t apricot 'cd /var/home/lilith/Code/@scripts/session-tools && sudo bin/wg-dns-sync'
|
||||
echo
|
||||
fi
|
||||
|
||||
echo "===== step $([ "$show_only" -eq 1 ] && echo "1/1" || echo "2/2"): phone WireGuard peer ====="
|
||||
if [ "$show_only" -eq 1 ]; then
|
||||
"$script_dir/wg-phone-add" -d "$device" --show
|
||||
else
|
||||
"$script_dir/wg-phone-add" -d "$device"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Bootstrap complete."
|
||||
echo "If the QR was already imported on the phone before, scanning again is harmless"
|
||||
echo "(WireGuard iOS will refuse to import a duplicate)."
|
||||
144
bin/wg-phone-add
Executable file
144
bin/wg-phone-add
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
#!/bin/sh
|
||||
# wg-phone-add — generate (or reuse) a WireGuard peer for a phone/tablet,
|
||||
# add it to the quinn-vps wg1 hub, render the client config + QR.
|
||||
#
|
||||
# Idempotent: re-runs reuse the stored keypair and just re-display the QR.
|
||||
#
|
||||
# Keys live OUTSIDE this repo (private keys must not be committed):
|
||||
# ~/.config/wg-mesh/clients/<device>/{private.key,public.key,address}
|
||||
#
|
||||
# Usage:
|
||||
# wg-phone-add # default device "phone-quinn", auto-pick IP
|
||||
# wg-phone-add -d phone-rachel # named device, auto-pick IP
|
||||
# wg-phone-add -d ipad-quinn -i 10.9.0.6
|
||||
# wg-phone-add -d phone-quinn --show # just re-render QR for an existing client
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 success
|
||||
# 1 missing dep / invalid input
|
||||
# 2 ssh to hub failed
|
||||
# 3 hub config edit / wg syncconf failed
|
||||
|
||||
set -eu
|
||||
|
||||
device="phone-quinn"
|
||||
forced_ip=""
|
||||
show_only=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-d) device=$2; shift 2 ;;
|
||||
-i) forced_ip=$2; shift 2 ;;
|
||||
--show) show_only=1; shift ;;
|
||||
-h|--help) sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate device name (used in filenames + comment in hub conf).
|
||||
case $device in
|
||||
*[!A-Za-z0-9_-]*|"") echo "invalid device name: $device" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
for cmd in ssh wg qrencode jq; do
|
||||
command -v $cmd >/dev/null || { echo "$cmd not installed" >&2; exit 1; }
|
||||
done
|
||||
|
||||
hub=quinn-vps
|
||||
hub_endpoint=89.127.233.145:51820
|
||||
hub_pubkey=t6LT6Ff4AxcGCd9Zug7dxkT7tXhkMV+fd28UCY/h8Xw=
|
||||
mesh_subnet=10.9.0.0/24
|
||||
lan_subnet=10.0.0.0/24
|
||||
mesh_dns=10.9.0.2 # apricot, where wg-dns-sync runs
|
||||
|
||||
client_dir="$HOME/.config/wg-mesh/clients/$device"
|
||||
mkdir -p "$client_dir"
|
||||
chmod 700 "$client_dir"
|
||||
|
||||
# 1. Reuse or generate keypair.
|
||||
if [ -s "$client_dir/private.key" ] && [ -s "$client_dir/public.key" ]; then
|
||||
echo "wg-phone-add: reusing existing keypair at $client_dir"
|
||||
privkey=$(cat "$client_dir/private.key")
|
||||
pubkey=$(cat "$client_dir/public.key")
|
||||
else
|
||||
[ "$show_only" -eq 1 ] && { echo "wg-phone-add: --show given but no keypair at $client_dir" >&2; exit 1; }
|
||||
echo "wg-phone-add: generating new keypair"
|
||||
privkey=$(wg genkey)
|
||||
pubkey=$(echo "$privkey" | wg pubkey)
|
||||
umask 077
|
||||
printf %s "$privkey" > "$client_dir/private.key"
|
||||
printf %s "$pubkey" > "$client_dir/public.key"
|
||||
fi
|
||||
|
||||
# 2. Pick or validate the WG IP slot.
|
||||
if [ -n "$forced_ip" ]; then
|
||||
address=$forced_ip
|
||||
elif [ -s "$client_dir/address" ]; then
|
||||
address=$(cat "$client_dir/address")
|
||||
else
|
||||
# Find lowest free .5..254 in 10.9.0.0/24 by reading hub config over ssh.
|
||||
used=$(ssh -o BatchMode=yes "$hub" "grep -hoE '10\\.9\\.0\\.[0-9]+' /etc/wireguard/wg1.conf | sort -u")
|
||||
address=""
|
||||
for n in $(seq 5 254); do
|
||||
candidate="10.9.0.$n"
|
||||
echo "$used" | grep -qx "$candidate" || { address=$candidate; break; }
|
||||
done
|
||||
[ -n "$address" ] || { echo "no free 10.9.0.x slot in /24" >&2; exit 1; }
|
||||
fi
|
||||
printf %s "$address" > "$client_dir/address"
|
||||
|
||||
# 3. (skipped if --show) ensure peer block is on hub.
|
||||
if [ "$show_only" -eq 0 ]; then
|
||||
# Probe whether hub already has this pubkey configured.
|
||||
if ssh -o BatchMode=yes "$hub" "grep -qxF 'PublicKey = $pubkey' /etc/wireguard/wg1.conf"; then
|
||||
echo "wg-phone-add: hub already has peer with this pubkey, skipping config edit"
|
||||
else
|
||||
echo "wg-phone-add: appending [Peer] block to $hub:/etc/wireguard/wg1.conf"
|
||||
# Use printf to avoid heredoc quoting headaches over ssh.
|
||||
# NOTE: assumes ssh user on quinn-vps can write /etc/wireguard/wg1.conf.
|
||||
# On quinn-vps the user is root (no sudo), per existing topology.
|
||||
if ! ssh -o BatchMode=yes "$hub" "printf '\n[Peer]\n# %s\nPublicKey = %s\nAllowedIPs = %s/32\nPersistentKeepalive = 25\n' '$device' '$pubkey' '$address' >> /etc/wireguard/wg1.conf"; then
|
||||
echo "ssh write to hub failed" >&2; exit 3
|
||||
fi
|
||||
# Live-reload without disturbing existing tunnels.
|
||||
if ! ssh -o BatchMode=yes "$hub" "wg syncconf wg1 <(wg-quick strip wg1)"; then
|
||||
echo "wg syncconf on hub failed" >&2; exit 3
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. Render the client config.
|
||||
client_conf="$client_dir/$device.conf"
|
||||
umask 077
|
||||
cat > "$client_conf" <<EOF
|
||||
# WireGuard config for $device
|
||||
# Generated by session-tools/bin/wg-phone-add at $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
# Hub: $hub ($hub_endpoint)
|
||||
|
||||
[Interface]
|
||||
PrivateKey = $privkey
|
||||
Address = $address/32
|
||||
DNS = $mesh_dns
|
||||
|
||||
[Peer]
|
||||
PublicKey = $hub_pubkey
|
||||
Endpoint = $hub_endpoint
|
||||
AllowedIPs = $mesh_subnet, $lan_subnet
|
||||
PersistentKeepalive = 25
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo "Client config written: $client_conf"
|
||||
echo
|
||||
echo "===== QR (scan with WireGuard iOS) ====="
|
||||
qrencode -t ansiutf8 < "$client_conf"
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "Device: $device"
|
||||
echo "Address: $address"
|
||||
echo "Pubkey: $pubkey"
|
||||
echo "Hub: $hub_endpoint"
|
||||
echo
|
||||
echo "After scanning: enable the tunnel in WireGuard iOS, then test from phone:"
|
||||
echo " ping 10.0.0.116 (apricot LAN)"
|
||||
echo " open https://quinn.apricot.local/"
|
||||
Loading…
Add table
Reference in a new issue