VPN: a WireGuard tunnel #8

Open
opened 2026-10-05 09:46:05 +00:00 by twisla · 0 comments
Owner

Idea

A WireGuard client: the Cardputer joins a WireGuard network over whatever Wi-Fi it's on, so it can reach home services and keep its traffic private on public networks. You'd configure it in Settings, turn it on and off, and see its state in the status bar.

Why

  • To reach home services from anywhere: an IRC bouncer, the Debug Console and UpdateService (TCP 2323 and 3232) for debugging and updates away from home, hosts for SSH (#2), and a private Gitea (#4).
  • To keep traffic private on hotel or café Wi-Fi.

What's known

  • Libraries. There are two candidates, both built on wireguard-lwip:

    • WireGuard-ESP32-Arduino.
    • The ESP-IDF component esp_wireguard.

    Both add a lwIP network interface. The Arduino one is limited: one peer, IPv4, and few options for routing and AllowedIPs. Both need checking against our pioarduino and hybrid sdkconfig build.

  • Memory. The crypto (Curve25519, ChaCha20-Poly1305, BLAKE2s) is light next to TLS, and the state per peer is small. Packet buffers still pass through lwIP. Measure the heap with IRC connected, as always.

  • It needs the clock.

    • A WireGuard handshake carries a timestamp, and the server rejects one that isn't newer than the last it saw.
    • After a reboot with the clock at 1970, handshakes fail until NTP or GNSS has set the time. So the tunnel starts only once the Clock is trusted. NTP has to reach its server outside the tunnel, or come from GNSS.
  • Keys.

    • The private key is generated on the device (Curve25519), stored in NVS, and never shown or logged.
    • The public key is shown for the server's config, possibly as a QR code on screen.
    • Importing a standard wg0.conf from the SD card would be the easy way to configure it: Interface address, PrivateKey, DNS, Peer PublicKey, Endpoint, AllowedIPs, PersistentKeepalive. A config parser can be host-tested.
  • Routing.

    • Full tunnel (0.0.0.0/0) or split (only the home subnets through the tunnel)?
    • The endpoint itself must stay outside the tunnel.
    • DNS from the config overrides DHCP's or the fixed one (#7) while the tunnel is up.
  • Exposure.

    • With the tunnel up, a Debug Build's console becomes reachable from the whole VPN. It's still protected by its token, but it should be a deliberate choice whether it listens on the tunnel, the Wi-Fi network, or both.
    • Release builds have no console, but UpdateService still listens.
  • MTU is 1420 by default. Gemini, IRC and the update stream must all work through it.

Questions for the design round

  1. Which library: the Arduino one, or esp_wireguard? Measure both first.
  2. How is it configured: import a wg0.conf from the card, type it in Settings, or both? Should the private key be generated on the device, or come from the config?
  3. Should it support one tunnel only, or several profiles, for example one per Saved Network?
  4. When should it start: by hand, on boot, or automatically on untrusted networks (every network except the ones marked "home")?
  5. Full or split tunnel? If full, what if the tunnel is down: block traffic (a kill switch) or fall back to plain Wi-Fi?
  6. Which services listen on the tunnel: the Debug Console, UpdateService, or neither by default?
  7. What does the status bar and status page show: up or down, the last handshake, bytes in and out?

Related

#7 (DNS and NTP settings), #2 (SSH), #4 (Gitea), src/services/wifi_service.cpp, src/services/clock_service.h, ADR 0006 (custom_sdkconfig).

## Idea A WireGuard client: the Cardputer joins a WireGuard network over whatever Wi-Fi it's on, so it can reach home services and keep its traffic private on public networks. You'd configure it in Settings, turn it on and off, and see its state in the status bar. ## Why - To reach home services from anywhere: an IRC bouncer, the Debug Console and UpdateService (TCP 2323 and 3232) for debugging and updates away from home, hosts for SSH (#2), and a private Gitea (#4). - To keep traffic private on hotel or café Wi-Fi. ## What's known - **Libraries.** There are two candidates, both built on wireguard-lwip: - WireGuard-ESP32-Arduino. - The ESP-IDF component `esp_wireguard`. Both add a lwIP network interface. The Arduino one is limited: one peer, IPv4, and few options for routing and AllowedIPs. Both need checking against our pioarduino and hybrid sdkconfig build. - **Memory.** The crypto (Curve25519, ChaCha20-Poly1305, BLAKE2s) is light next to TLS, and the state per peer is small. Packet buffers still pass through lwIP. Measure the heap with IRC connected, as always. - **It needs the clock.** - A WireGuard handshake carries a timestamp, and the server rejects one that isn't newer than the last it saw. - After a reboot with the clock at 1970, handshakes fail until NTP or GNSS has set the time. So the tunnel starts only once the Clock is trusted. NTP has to reach its server outside the tunnel, or come from GNSS. - **Keys.** - The private key is generated on the device (Curve25519), stored in NVS, and never shown or logged. - The public key is shown for the server's config, possibly as a QR code on screen. - Importing a standard `wg0.conf` from the SD card would be the easy way to configure it: Interface address, PrivateKey, DNS, Peer PublicKey, Endpoint, AllowedIPs, PersistentKeepalive. A config parser can be host-tested. - **Routing.** - Full tunnel (0.0.0.0/0) or split (only the home subnets through the tunnel)? - The endpoint itself must stay outside the tunnel. - DNS from the config overrides DHCP's or the fixed one (#7) while the tunnel is up. - **Exposure.** - With the tunnel up, a Debug Build's console becomes reachable from the whole VPN. It's still protected by its token, but it should be a deliberate choice whether it listens on the tunnel, the Wi-Fi network, or both. - Release builds have no console, but UpdateService still listens. - **MTU** is 1420 by default. Gemini, IRC and the update stream must all work through it. ## Questions for the design round 1. Which library: the Arduino one, or `esp_wireguard`? Measure both first. 2. How is it configured: import a `wg0.conf` from the card, type it in Settings, or both? Should the private key be generated on the device, or come from the config? 3. Should it support one tunnel only, or several profiles, for example one per Saved Network? 4. When should it start: by hand, on boot, or automatically on untrusted networks (every network except the ones marked "home")? 5. Full or split tunnel? If full, what if the tunnel is down: block traffic (a kill switch) or fall back to plain Wi-Fi? 6. Which services listen on the tunnel: the Debug Console, UpdateService, or neither by default? 7. What does the status bar and status page show: up or down, the last handshake, bytes in and out? ## Related #7 (DNS and NTP settings), #2 (SSH), #4 (Gitea), `src/services/wifi_service.cpp`, `src/services/clock_service.h`, ADR 0006 (`custom_sdkconfig`).
twisla added this to the N1 Network tools milestone 2026-10-05 20:14:10 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: twisla/roro9stack#8