51822 not 51820; the phone has its own peer (10.8.0.6) rather than sharing the laptop's key; keepalive 25 not 0. Records that the autonomous gate is dead and why a full tunnel that cannot handshake takes the machine with it.
123 lines
5.2 KiB
Markdown
123 lines
5.2 KiB
Markdown
# Wiuf VPN — phone split-tunnel
|
|
|
|
> **Corrected 2026-07-30 evening, proven working off-LAN.** Three things below
|
|
> were wrong and cost an hour:
|
|
>
|
|
> - **The port is 51822 (or 51821), NOT 51820.** A generated
|
|
> `SouveraineOS-Phone.conf` said 51820; it sends and never receives.
|
|
> - **The phone has its OWN peer now: `wiufph`, address 10.8.0.6.** The old
|
|
> `wiuf` profile shared its address *and private key* with the laptop, so
|
|
> WireGuard could only ever hold one of them, and that key is no longer
|
|
> listed server-side.
|
|
> - **`persistent-keepalive` must be 25**, not the `0` the generated conf
|
|
> carries, or the tunnel dies behind carrier NAT.
|
|
>
|
|
> Verified from a foreign network (`10.10.30.x`): handshake in 8 s, gitea
|
|
> `HTTP 200` in 0.126 s, Pi-hole reachable, internet unaffected.
|
|
>
|
|
> **The autonomous gate described below is DEAD** — it recycled the tunnel 652
|
|
> times in 90 minutes and could not be switched off. Replaced by a deliberate
|
|
> quick toggle in the shell's right panel. Never reinstate a timer or an NM
|
|
> dispatcher hook here; if posture must be automatic it belongs in sessiond as
|
|
> an Action (DEVICE-STATE-MACHINE §12).
|
|
>
|
|
> **Also note:** `allowed-ips=0.0.0.0/0` is a full tunnel — bringing that up
|
|
> when it cannot handshake black-holes the whole machine. Split-tunnel
|
|
> (`10.10.0.0/16` + `never-default=true`) fails safe instead.
|
|
|
|
Status: deployed on the Pixel 3 (blueline) 2026-07-30. Laptop profile removed
|
|
(same keys — see Caveats); it gets a fresh Wiuf peer of its own.
|
|
|
|
This is the practical client config; the aspirational per-consumer policy engine
|
|
lives in `NET_LAYER_PLAN.md`. That engine is not yet built, so this sits on
|
|
NetworkManager directly.
|
|
|
|
## What it does
|
|
|
|
- A NetworkManager WireGuard connection **`wiuf`** that routes **only
|
|
`10.10.0.0/16`** (covers the `.10` / `.20` / `.30` segments) over the tunnel
|
|
and resolves **`~wiuf.net`** via the LAN Pi-hole (`10.10.20.5`). Everything
|
|
else (LTE, general internet) stays on the default link — so this does **not**
|
|
funnel the phone's whole connection through home, and it does **not** grab the
|
|
default route at boot (`autoconnect=false`).
|
|
- A gate (`wiuf-vpn-gate`) that brings the tunnel **up only off the home LAN and
|
|
only when there's an internet path**, and **down** on the home LAN (already
|
|
direct) or with no service. Triggered by the NM dispatcher on link changes,
|
|
with a 90 s backstop timer.
|
|
|
|
## Endpoint facts (why it's pinned to IPv4)
|
|
|
|
- `sub.wiuf.net` → **`173.246.142.24`**, which is the **home WAN**. UDP `51822`
|
|
is port-forwarded to the WG server inside.
|
|
- It also has an AAAA (`2604:5580:22::adf6:8e18`), but the phone has **no global
|
|
IPv6 on wifi**, and the v6 path is dead. The endpoint is therefore pinned to
|
|
`173.246.142.24:51822`. This works on both wifi (direct v4) and cell
|
|
(clat gives v4-over-v6).
|
|
- **NAT loopback**: when the phone is *on* the home wifi it cannot handshake to
|
|
the home WAN — the router doesn't reflect the UDP forward back inside
|
|
(`transfer: 0 B received`). This is expected and irrelevant: the gate never
|
|
brings the tunnel up at home.
|
|
|
|
## The connection (keyfile)
|
|
|
|
`/etc/NetworkManager/system-connections/wiuf.nmconnection`:
|
|
|
|
```ini
|
|
[connection]
|
|
id=wiuf
|
|
type=wireguard
|
|
interface-name=wgpi
|
|
autoconnect=false
|
|
permissions=
|
|
|
|
[wireguard]
|
|
private-key=kFH0g2VTkzV5F6KBBCtZ8A9KH1c4uwtvoIOtye9lomo=
|
|
peer-routes=true
|
|
|
|
[wireguard-peer.P3Fbw93BAJyRs14aRZcEULAHD/8NcR3HU2T5lA/mADE=]
|
|
preshared-key=H5GpVr7xszqvFDj6mtQm3+Ii93TTHc2KPJmJ6LFNwUc=
|
|
allowed-ips=10.10.0.0/16;
|
|
endpoint=173.246.142.24:51822
|
|
persistent-keepalive=25
|
|
|
|
[ipv4]
|
|
method=manual
|
|
addresses=10.8.0.5/24
|
|
dns=10.10.20.5
|
|
dns-search=~wiuf.net
|
|
never-default=true
|
|
|
|
[ipv6]
|
|
method=disabled
|
|
```
|
|
|
|
> Rotate the private key / PSK when the laptop is re-keyed (see Caveats). These
|
|
> currently match the old laptop peer.
|
|
|
|
## The gate
|
|
|
|
- `/usr/local/bin/wiuf-vpn-gate` — idempotent up/down controller. Logs to
|
|
`journalctl -t wiuf-vpn-gate`.
|
|
- `/etc/NetworkManager/dispatcher.d/99-wiuf-vpn` — runs the gate on any link
|
|
`up`/`down`.
|
|
- `wiuf-vpn-gate.timer` — fires the gate every 90 s as a backstop.
|
|
|
|
Logic: home wifi (wlan0 has a `10.10.x` addr) → down; no default route → down;
|
|
otherwise up, and verifies a handshake (received bytes > 0), recycling if dead.
|
|
|
|
## Caveats
|
|
|
|
- **Shared keys.** The phone inherited the laptop's peer (`10.8.0.5`, same
|
|
private key + PSK). WireGuard maps one public key to one endpoint, so the two
|
|
devices cannot be connected at the same time until the laptop is given its own
|
|
peer. That's the "replace shortly" item.
|
|
- **Validation pending on cell.** Handshake can't be confirmed from inside the
|
|
LAN (NAT loopback). The gate self-confirms off-LAN; check
|
|
`journalctl -t wiuf-vpn-gate | grep "handshake ok"` after first cell use.
|
|
- **Pangolin alternative.** `10.10.20.120` (WIUF-Docker, `gitea.wiuf.net`) runs
|
|
fosrl **Pangolin** (reverse proxy atop traefik, manages crowdsec) with a REST
|
|
API. Exposing specific services through Pangolin (public HTTPS + auth) is an
|
|
alternative to VPN for those services — but raw `10.10.x` + Pi-hole domains
|
|
still need the tunnel, so VPN stays. A public `dns.wiuf.net` was considered
|
|
and rejected: it only resolves names, doesn't route to them, and is redundant
|
|
once the tunnel is up.
|