Give the VPN a toggle, an indicator, and a way back
Turning on a WireGuard profile whose server was unreachable used to cost the whole network stack, and the only way out was nmcli typed into a terminal. Quickshell's Networking module has no VPN surface, so this arrives as the one sanctioned nmcli exception: a helper that lists, raises and lowers profiles, a service that watches NetworkManager for changes made anywhere, a quick settings tile (left-click toggles the most recently used profile, right-click picks among them), and a bar glyph while a tunnel is up. The safety property is in the helper, where it cannot be skipped: activation waits a bounded 25 seconds, and a failure is rolled back down and reported instead of leaving a black-hole default route. The contract pins exactly that, against a stateful stub NetworkManager. Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
This commit is contained in:
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# VPN connections, via NetworkManager.
|
||||
#
|
||||
# Quickshell.Networking covers Wi-Fi and wired devices but has no surface for
|
||||
# VPN or WireGuard connections at all, so this is the one sanctioned exception
|
||||
# to the no-nmcli rule stated in services/Connectivity.qml: there is nothing
|
||||
# else to talk to. The moment Quickshell grows VPN support, this helper is what
|
||||
# gets deleted.
|
||||
#
|
||||
# Usage:
|
||||
# panama-vpn list -> {"connections":[{"name","uuid","kind","active","timestamp"}],"error":""}
|
||||
# panama-vpn up <uuid>
|
||||
# panama-vpn down <uuid>
|
||||
#
|
||||
# `up` is bounded and self-cleaning: a VPN whose server is unreachable is
|
||||
# exactly the case this exists for, and NetworkManager's default is to keep
|
||||
# trying for 90 seconds while every packet on a full-tunnel profile goes into
|
||||
# the void. Waiting a bounded time and deactivating on failure is what makes
|
||||
# the quick-settings toggle safe to press on a broken profile.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
emit_error() {
|
||||
printf '{"connections":[],"error":%s}\n' "$(jq -Rn --arg e "$1" '$e')"
|
||||
exit 0
|
||||
}
|
||||
|
||||
command -v nmcli >/dev/null 2>&1 || emit_error 'nmcli is not available'
|
||||
|
||||
cmd_list() {
|
||||
local rows active
|
||||
# TIMESTAMP is when the connection last activated successfully; the toggle
|
||||
# uses it to pick the profile the person most recently used.
|
||||
rows="$(nmcli -t -f NAME,UUID,TYPE,TIMESTAMP connection show 2>/dev/null)" \
|
||||
|| emit_error 'NetworkManager did not answer'
|
||||
active="$(nmcli -t -f UUID connection show --active 2>/dev/null)" || active=""
|
||||
|
||||
# Fields are parsed from the end because NAME may contain escaped colons;
|
||||
# UUID, TYPE and TIMESTAMP never do.
|
||||
awk -F: -v active="$active" '
|
||||
$(NF-1) == "wireguard" || $(NF-1) == "vpn" {
|
||||
timestamp = $NF; kind = $(NF-1); uuid = $(NF-2);
|
||||
name = $1;
|
||||
for (i = 2; i <= NF-3; i++) name = name FS $i;
|
||||
gsub(/\\:/, ":", name);
|
||||
is_active = index(active, uuid) > 0 ? "true" : "false";
|
||||
printf "%s\t%s\t%s\t%s\t%s\n", name, uuid, kind, is_active, timestamp;
|
||||
}
|
||||
' <<<"$rows" | jq -Rn '
|
||||
{"connections": [inputs | split("\t")
|
||||
| {name: .[0], uuid: .[1], kind: .[2],
|
||||
active: (.[3] == "true"), timestamp: (.[4] | tonumber? // 0)}],
|
||||
"error": ""}'
|
||||
}
|
||||
|
||||
cmd_up() {
|
||||
local uuid="$1" output
|
||||
if ! output="$(nmcli -w 25 connection up uuid "$uuid" 2>&1)"; then
|
||||
# Roll the half-activated connection back down so a dead server does
|
||||
# not leave the machine with a black-hole default route.
|
||||
nmcli connection down uuid "$uuid" >/dev/null 2>&1 || true
|
||||
printf '%s\n' "$output" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_down() {
|
||||
local uuid="$1" output
|
||||
if ! output="$(nmcli connection down uuid "$uuid" 2>&1)"; then
|
||||
printf '%s\n' "$output" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
list) cmd_list ;;
|
||||
up) [[ -n "${2:-}" ]] || { echo 'panama-vpn up needs a connection uuid' >&2; exit 1; }
|
||||
cmd_up "$2" ;;
|
||||
down) [[ -n "${2:-}" ]] || { echo 'panama-vpn down needs a connection uuid' >&2; exit 1; }
|
||||
cmd_down "$2" ;;
|
||||
*) echo 'usage: panama-vpn list | up <uuid> | down <uuid>' >&2; exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user