From bf36c94464cd9a4c28df59aa016cd67da4106aaa Mon Sep 17 00:00:00 2001 From: Natalie Date: Mon, 11 May 2026 19:21:04 -0700 Subject: [PATCH] =?UTF-8?q?feat(@scripts):=20=E2=9C=A8=20add=20sudo-less?= =?UTF-8?q?=20wg-bounce=20restart=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- bin/wg-bounce | 95 ++++++++++++++++++++++++++++++++++++++++++ share/wg-quick.sudoers | 12 ++++++ 2 files changed, 107 insertions(+) create mode 100755 bin/wg-bounce create mode 100644 share/wg-quick.sudoers diff --git a/bin/wg-bounce b/bin/wg-bounce new file mode 100755 index 0000000..a5f7764 --- /dev/null +++ b/bin/wg-bounce @@ -0,0 +1,95 @@ +#!/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 # 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 diff --git a/share/wg-quick.sudoers b/share/wg-quick.sudoers new file mode 100644 index 0000000..5efb92a --- /dev/null +++ b/share/wg-quick.sudoers @@ -0,0 +1,12 @@ +# Allow Natalie to run wg-quick without a password. Required by +# session-tools/bin/wg-bounce so the WG tunnel can be re-established +# from CLI without prompting (useful on hotel wifi where NAT mappings +# shift and the existing tunnel goes dead). +# +# Install with: +# sudo install -m 0440 -o root -g wheel \ +# ~/Code/@scripts/session-tools/share/wg-quick.sudoers \ +# /etc/sudoers.d/wg-quick +# +# Validate before activating: sudo visudo -c -f .../wg-quick.sudoers +natalie ALL=(root) NOPASSWD: /opt/homebrew/bin/wg-quick