96 lines
3.4 KiB
Text
96 lines
3.4 KiB
Text
|
|
#!/bin/sh
|
||
|
|
# wg-bounce — restart the wg1 tunnel without an interactive sudo password.
|
||
|
|
#
|
||
|
|
# macOS WireGuard (wg-quick) needs root to create the utun interface and
|
||
|
|
# install routes. This script invokes it via `sudo -n`. If the sudoers entry
|
||
|
|
# is missing, the script offers to install it from the bundled template
|
||
|
|
# (a single password prompt that won't be needed again).
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# wg-bounce # down + up the default config
|
||
|
|
# wg-bounce <conf-path> # down + up a specific .conf file
|
||
|
|
#
|
||
|
|
# Exit codes:
|
||
|
|
# 0 success
|
||
|
|
# 1 setup needed and the install prompt failed
|
||
|
|
# 2 wg-quick down or up failed
|
||
|
|
|
||
|
|
set -eu
|
||
|
|
|
||
|
|
conf=${1:-"$HOME/.wireguard/wg1.conf"}
|
||
|
|
|
||
|
|
if [ ! -r "$conf" ]; then
|
||
|
|
echo "wg-bounce: missing config $conf" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Resolve symlinks so $script_dir points at the real bin/ inside session-tools,
|
||
|
|
# letting us find ../share/. macOS /bin/sh lacks `readlink -f`, so walk it
|
||
|
|
# manually until we hit a non-symlink.
|
||
|
|
resolve_link() {
|
||
|
|
target=$1
|
||
|
|
while [ -L "$target" ]; do
|
||
|
|
link=$(readlink "$target")
|
||
|
|
case $link in
|
||
|
|
/*) target=$link ;;
|
||
|
|
*) target=$(dirname "$target")/$link ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
printf %s "$target"
|
||
|
|
}
|
||
|
|
script_path=$(resolve_link "$0")
|
||
|
|
script_dir=$(cd "$(dirname "$script_path")" && pwd)
|
||
|
|
sudoers_src="$script_dir/../share/wg-quick.sudoers"
|
||
|
|
sudoers_dst=/etc/sudoers.d/wg-quick
|
||
|
|
|
||
|
|
if ! sudo -n /opt/homebrew/bin/wg-quick --help >/dev/null 2>&1; then
|
||
|
|
# Sudoers entry missing or doesn't apply yet. Try to install it from the
|
||
|
|
# bundled template — this is the ONE prompt for a password we accept; all
|
||
|
|
# subsequent wg-bounce runs are passwordless.
|
||
|
|
if [ ! -r "$sudoers_src" ]; then
|
||
|
|
echo "wg-bounce: sudoers template missing at $sudoers_src" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo "wg-bounce: first run — installing $sudoers_dst (one-time sudo prompt)"
|
||
|
|
# Validate the template before installing so a typo never lands in
|
||
|
|
# /etc/sudoers.d (visudo -c -f checks parseability).
|
||
|
|
if ! sudo visudo -c -f "$sudoers_src" >/dev/null; then
|
||
|
|
echo "wg-bounce: $sudoers_src failed visudo syntax check" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
if ! sudo install -m 0440 -o root -g wheel "$sudoers_src" "$sudoers_dst"; then
|
||
|
|
echo "wg-bounce: failed to install $sudoers_dst" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
# Re-check; should now succeed without prompting.
|
||
|
|
if ! sudo -n /opt/homebrew/bin/wg-quick --help >/dev/null 2>&1; then
|
||
|
|
echo "wg-bounce: sudoers installed but sudo still prompts — check $sudoers_dst" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo "wg-bounce: setup complete; future runs won't prompt"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "wg-bounce: down $conf"
|
||
|
|
# down may fail if the tunnel is already down — that's fine, we proceed to up.
|
||
|
|
sudo -n /opt/homebrew/bin/wg-quick down "$conf" 2>/dev/null || true
|
||
|
|
|
||
|
|
echo "wg-bounce: up $conf"
|
||
|
|
if ! sudo -n /opt/homebrew/bin/wg-quick up "$conf"; then
|
||
|
|
echo "wg-bounce: wg-quick up failed" >&2
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Brief reach test against the hub so the caller knows immediately whether
|
||
|
|
# the new endpoint is actually carrying packets.
|
||
|
|
echo "wg-bounce: waiting for handshake..."
|
||
|
|
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||
|
|
if ping -c 1 -W 1500 10.9.0.1 >/dev/null 2>&1; then
|
||
|
|
echo "wg-bounce: hub reachable after ${i}s"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
sleep 1
|
||
|
|
done
|
||
|
|
|
||
|
|
echo "wg-bounce: hub (10.9.0.1) still unreachable after 10s — tunnel is up but no packets are flowing (check hotel wifi / NAT)." >&2
|
||
|
|
exit 2
|