2026-10-03 00:01:09 -04:00
|
|
|
#!/usr/bin/env bash
|
2026-10-03 00:34:43 -04:00
|
|
|
# netvm-node-up.sh <node> — bring up a node's Warp egress in its own netns.
|
2026-10-03 00:43:08 -04:00
|
|
|
# Run as root (operator: sudo -n via the allowlist). Idempotent.
|
2026-10-03 00:01:09 -04:00
|
|
|
set -euo pipefail
|
2026-10-03 00:43:08 -04:00
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
. "$SCRIPT_DIR/netvm-names.sh"
|
|
|
|
|
netvm_names "${1:?usage: netvm-node-up.sh <node>}"
|
2026-10-03 00:01:09 -04:00
|
|
|
CONF="/etc/netvm/${NODE}.conf"
|
2026-10-03 00:34:43 -04:00
|
|
|
[ -f "$CONF" ] || { echo "missing $CONF (human: netvm-new-identity.sh $NODE)"; exit 1; }
|
2026-10-03 00:01:09 -04:00
|
|
|
chmod 600 "$CONF"
|
2026-10-03 01:06:00 -04:00
|
|
|
# let the operator user stat (not read) identities: 711 dir, 600 files
|
|
|
|
|
chmod 711 /etc/netvm 2>/dev/null || true
|
2026-10-03 00:34:43 -04:00
|
|
|
nsexec() { ip netns exec "$NETNS" "$@"; }
|
|
|
|
|
|
2026-10-03 00:01:09 -04:00
|
|
|
ip netns add "$NETNS" 2>/dev/null || true
|
2026-10-03 00:34:43 -04:00
|
|
|
|
|
|
|
|
# veth pair host <-> netns
|
|
|
|
|
if ! nsexec ip link show "$VPEER" >/dev/null 2>&1; then
|
|
|
|
|
ip link del "$VETH" 2>/dev/null || true
|
|
|
|
|
ip link add "$VETH" type veth peer name "$VPEER"
|
|
|
|
|
ip link set "$VPEER" netns "$NETNS"
|
|
|
|
|
ip addr add "${GW}/30" dev "$VETH" 2>/dev/null || true
|
|
|
|
|
ip link set "$VETH" up
|
|
|
|
|
nsexec ip addr add "${PEER_IP}/30" dev "$VPEER" 2>/dev/null || true
|
|
|
|
|
nsexec ip link set "$VPEER" up
|
2026-10-03 00:33:43 -04:00
|
|
|
fi
|
2026-10-03 00:34:43 -04:00
|
|
|
nsexec ip link set lo up
|
|
|
|
|
|
|
|
|
|
# host NAT + forwarding for the veth subnet
|
|
|
|
|
sysctl -qw net.ipv4.ip_forward=1
|
2026-10-03 00:58:23 -04:00
|
|
|
# NB: never NAT host<->netvm traffic — SNATing the host's own veth IP breaks
|
|
|
|
|
# host->netns connections (e.g. CDP): the reply would route out the tunnel.
|
|
|
|
|
iptables -t nat -D POSTROUTING -s "$SUB" -j MASQUERADE 2>/dev/null || true # migrate old broad rule
|
|
|
|
|
iptables -t nat -C POSTROUTING -s "$SUB" ! -d 10.201.0.0/16 -j MASQUERADE 2>/dev/null || \
|
|
|
|
|
iptables -t nat -A POSTROUTING -s "$SUB" ! -d 10.201.0.0/16 -j MASQUERADE
|
2026-10-03 00:34:43 -04:00
|
|
|
|
|
|
|
|
# endpoint bypasses the tunnel (else the handshake routes into itself)
|
|
|
|
|
ENDPOINT=$(grep -oP '^\s*Endpoint\s*=\s*\K[^:;#]+' "$CONF" | head -1)
|
|
|
|
|
for eip in $(getent ahostsv4 "$ENDPOINT" | awk '{print $1}' | sort -u); do
|
|
|
|
|
nsexec ip route replace "$eip" via "$GW"
|
|
|
|
|
done
|
|
|
|
|
|
2026-10-03 00:43:08 -04:00
|
|
|
# wireguard interface (wg setconf rejects wg-quick-only keys: strip them)
|
2026-10-03 00:34:43 -04:00
|
|
|
if ! nsexec ip link show "$WG" >/dev/null 2>&1; then
|
2026-10-03 00:37:43 -04:00
|
|
|
ip link del "$WG" 2>/dev/null || true # stale host-side (reaped zombie netns)
|
2026-10-03 00:34:43 -04:00
|
|
|
ip link add "$WG" type wireguard
|
|
|
|
|
ip link set "$WG" netns "$NETNS"
|
|
|
|
|
fi
|
|
|
|
|
STRIPPED=$(mktemp)
|
2026-10-03 00:43:08 -04:00
|
|
|
grep -vE '^\s*(Address|DNS|MTU|Table|PreUp|PreDown|PostUp|PostDown|SaveConfig)\s*=' "$CONF" > "$STRIPPED"
|
2026-10-03 00:34:43 -04:00
|
|
|
nsexec wg setconf "$WG" "$STRIPPED"
|
|
|
|
|
rm -f "$STRIPPED"
|
|
|
|
|
MTU=$(grep -oP '^\s*MTU\s*=\s*\K\d+' "$CONF" | head -1); MTU=${MTU:-1280}
|
|
|
|
|
nsexec ip link set "$WG" mtu "$MTU"
|
|
|
|
|
for a in $(grep -oP '^\s*Address\s*=\s*\K\S+' "$CONF" | tr ',' ' '); do
|
|
|
|
|
if [[ "$a" == *:* ]]; then nsexec ip -6 addr add "$a" dev "$WG" 2>/dev/null || true
|
|
|
|
|
else nsexec ip addr add "$a" dev "$WG" 2>/dev/null || true; fi
|
|
|
|
|
done
|
|
|
|
|
nsexec ip link set "$WG" up
|
|
|
|
|
nsexec ip route replace default dev "$WG"
|
|
|
|
|
nsexec ip -6 route replace default dev "$WG" 2>/dev/null || true
|
|
|
|
|
|
2026-10-03 01:00:37 -04:00
|
|
|
# CDP bridge: chromium binds DevTools to loopback only. A tiny TCP relay
|
|
|
|
|
# listens on the veth IP and forwards to loopback (empirically reliable;
|
|
|
|
|
# iptables REDIRECT to 127.0.0.1 did not establish). Supervised via pidfile.
|
|
|
|
|
PIDFILE="/run/netvm-${NODE}-cdp-relay.pid"
|
|
|
|
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
|
|
|
|
echo "cdp relay already running"
|
|
|
|
|
else
|
|
|
|
|
nsexec python3 "$SCRIPT_DIR/netvm-cdp-relay.py" "$PEER_IP" "$CDP_PORT" 127.0.0.1 "$CDP_PORT" \
|
|
|
|
|
>/dev/null 2>&1 &
|
|
|
|
|
echo $! > "$PIDFILE"
|
|
|
|
|
echo "cdp relay started ($PEER_IP:$CDP_PORT -> 127.0.0.1:$CDP_PORT)"
|
|
|
|
|
fi
|
2026-10-03 00:55:39 -04:00
|
|
|
|
2026-10-03 00:43:08 -04:00
|
|
|
# wait for handshake (first one can take ~10s)
|
|
|
|
|
HS=""
|
|
|
|
|
for i in $(seq 1 10); do
|
|
|
|
|
HS=$(nsexec wg show "$WG" latest-handshakes 2>/dev/null | awk '{print $2}')
|
|
|
|
|
if [ -n "$HS" ] && [ "$HS" != "0" ]; then break; fi
|
|
|
|
|
sleep 2
|
|
|
|
|
done
|
2026-10-03 00:34:43 -04:00
|
|
|
if [ -z "$HS" ] || [ "$HS" = "0" ]; then
|
2026-10-03 00:43:08 -04:00
|
|
|
echo "no handshake yet (endpoint=$ENDPOINT)"
|
2026-10-03 00:34:43 -04:00
|
|
|
else
|
|
|
|
|
echo "handshake ok"
|
|
|
|
|
fi
|
2026-10-03 00:43:08 -04:00
|
|
|
EGRESS=$(nsexec curl -sk --max-time 15 'https://1.1.1.1/cdn-cgi/trace' 2>/dev/null | grep -oP '^ip=\K.*' || true)
|
|
|
|
|
echo "node=$NODE netns=$NETNS ifaces=$WG/$VETH egress=${EGRESS:-unknown}"
|