net-tools: fix wg-render apply (set -e abort + dash syncconf), nyc3 endpoint
Two bugs found bringing the nyc3 segment live (citron hub + lime spoke): - Hub render ended in `[ -n "$miss" ] && echo`, which returns 1 when no spokes are unkeyed; under `set -e` that silently aborted `render_conf > tmp` on the apply path (spokes were fine — they end in printf). Use an if-block. - `wg syncconf <(wg-quick strip)` used bash process substitution but the script runs under /bin/sh (dash) — replaced with a POSIX temp file. Also: nyc3 endpoint -> citron's bound public IP (104.248.9.88), not the reserved IP (143.244.223.5) — DO routes the reserved IP in but WG replies from the primary, so the reserved IP can't be a WG endpoint without anchor source-routing. Verified live: lime<->citron handshake, ping 10.9.0.7 0% loss, citron dnsmasq resolving *.wg on 10.9.0.7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
de1f7f2dec
commit
53a79d3494
2 changed files with 14 additions and 6 deletions
|
|
@ -166,10 +166,14 @@ render_conf() {
|
||||||
| select(.wg_pubkey != null and .wg_pubkey != \"\")
|
| select(.wg_pubkey != null and .wg_pubkey != \"\")
|
||||||
| \"# \(.name)\n[Peer]\nPublicKey = \(.wg_pubkey)\nAllowedIPs = \(.wg)/32\n\"
|
| \"# \(.name)\n[Peer]\nPublicKey = \(.wg_pubkey)\nAllowedIPs = \(.wg)/32\n\"
|
||||||
" "$data_file"
|
" "$data_file"
|
||||||
# Warn (to stderr) about spokes still missing a key.
|
# Warn (to stderr) about spokes still missing a key. Use an if-block, not
|
||||||
|
# `[ ... ] && echo`: the latter returns 1 when the test is false, which
|
||||||
|
# under `set -e` (apply path: render_conf > tmp) aborts the whole render.
|
||||||
miss=$(jq -r --arg SEG "${self_seg:-}" --arg SELF "$self" "
|
miss=$(jq -r --arg SEG "${self_seg:-}" --arg SELF "$self" "
|
||||||
${seg_members_filter} | select(.name!=\$SELF) | select((.wg_pubkey//\"\")==\"\") | .name" "$data_file" | tr '\n' ' ')
|
${seg_members_filter} | select(.name!=\$SELF) | select((.wg_pubkey//\"\")==\"\") | .name" "$data_file" | tr '\n' ' ')
|
||||||
[ -n "$(echo "$miss" | tr -d ' ')" ] && echo "wg-render: NOTE spokes without wg_pubkey (not peered): $miss" >&2
|
if [ -n "$(echo "$miss" | tr -d ' ')" ]; then
|
||||||
|
echo "wg-render: NOTE spokes without wg_pubkey (not peered): $miss" >&2
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
# Single [Peer] = the segment hub.
|
# Single [Peer] = the segment hub.
|
||||||
hub_pub=$(jq -r --arg H "$seg_hub" '.hosts[] | select(.name==$H) | .wg_pubkey // empty' "$data_file")
|
hub_pub=$(jq -r --arg H "$seg_hub" '.hosts[] | select(.name==$H) | .wg_pubkey // empty' "$data_file")
|
||||||
|
|
@ -203,10 +207,14 @@ echo "wg-render: wrote $CONF_FILE for $self ($role/${self_seg:-legacy})"
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
$SUDO systemctl enable "wg-quick@${iface}" >/dev/null 2>&1 || true
|
$SUDO systemctl enable "wg-quick@${iface}" >/dev/null 2>&1 || true
|
||||||
if $SUDO systemctl is-active "wg-quick@${iface}" >/dev/null 2>&1; then
|
if $SUDO systemctl is-active "wg-quick@${iface}" >/dev/null 2>&1; then
|
||||||
# Live update without dropping the tunnel.
|
# Live update without dropping the tunnel. `wg syncconf` needs a stripped
|
||||||
if $SUDO sh -c "wg syncconf $iface <(wg-quick strip $iface)" 2>/dev/null; then
|
# conf file; build it with a temp file (POSIX) — NOT bash <() process
|
||||||
echo "wg-render: $iface syncconf applied"
|
# substitution, since this script runs under /bin/sh (dash on Ubuntu).
|
||||||
|
strip_tmp=$(mktemp "${TMPDIR:-/tmp}/wg1.strip.XXXXXX")
|
||||||
|
if $SUDO wg-quick strip "$iface" > "$strip_tmp" 2>/dev/null && $SUDO wg syncconf "$iface" "$strip_tmp" 2>/dev/null; then
|
||||||
|
echo "wg-render: $iface syncconf applied"; rm -f "$strip_tmp"
|
||||||
else
|
else
|
||||||
|
rm -f "$strip_tmp"
|
||||||
$SUDO systemctl restart "wg-quick@${iface}" || { echo "wg-render: $iface restart failed — rolling back" >&2; [ -f "$CONF_FILE.netbak" ] && $SUDO cp "$CONF_FILE.netbak" "$CONF_FILE"; $SUDO systemctl restart "wg-quick@${iface}" || true; exit 3; }
|
$SUDO systemctl restart "wg-quick@${iface}" || { echo "wg-render: $iface restart failed — rolling back" >&2; [ -f "$CONF_FILE.netbak" ] && $SUDO cp "$CONF_FILE.netbak" "$CONF_FILE"; $SUDO systemctl restart "wg-quick@${iface}" || true; exit 3; }
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
"segments": {
|
"segments": {
|
||||||
"_note": "A segment = a WireGuard hub + its spokes (bin/wg-render). hosts[].segment names the segment a host belongs to; hosts[].wg_pubkey is its public key (never private). yuzu (iceland) and citron (nyc3) are independent stars. Hosts without a `segment` fall back to the legacy single hub (mesh.hub) in wg-render.",
|
"_note": "A segment = a WireGuard hub + its spokes (bin/wg-render). hosts[].segment names the segment a host belongs to; hosts[].wg_pubkey is its public key (never private). yuzu (iceland) and citron (nyc3) are independent stars. Hosts without a `segment` fall back to the legacy single hub (mesh.hub) in wg-render.",
|
||||||
"iceland": { "hub": "yuzu", "endpoint": "89.127.233.145:51820", "dns_host": "apricot", "dns_listen": "127.0.0.1,10.9.0.2" },
|
"iceland": { "hub": "yuzu", "endpoint": "89.127.233.145:51820", "dns_host": "apricot", "dns_listen": "127.0.0.1,10.9.0.2" },
|
||||||
"nyc3": { "hub": "citron", "endpoint": "143.244.223.5:51820", "dns_host": "citron", "dns_listen": "127.0.0.1,10.9.0.7" }
|
"nyc3": { "hub": "citron", "endpoint": "104.248.9.88:51820", "dns_host": "citron", "dns_listen": "127.0.0.1,10.9.0.7" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lan": {
|
"lan": {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue