VPN: a WireGuard tunnel (#8)
CI / build (pull_request) Successful in 2m58s
Site / build (pull_request) Successful in 13s

The device joins a WireGuard network over whatever Wi-Fi it is on: one
peer, IPv4. A client's .conf is imported from the card (/vpn/wg0.conf) and
kept in the device's settings, private key included, never shown; Settings
offers to delete the file. A switch brings the tunnel up until the next
restart, "Start with Wi-Fi" every time; it waits for the clock, which a
handshake needs. VPN shows in the Status Bar.

The protocol is esphome/wireguard 0.4.8. It calls lwIP without lwIP's lock,
which this framework checks: every call into it is made with the lock held.

What goes through the tunnel is everything (AllowedIPs 0.0.0.0/0) or the
one subnet the device's tunnel address is in: lwIP routes by an
interface's subnet or by default, nothing finer. The import says how many
ranges it can't reach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-07 23:13:28 +02:00
co-authored by Claude Opus 5.5
parent c35bc47693
commit b041c7b67c
30 changed files with 1298 additions and 11 deletions
+68
View File
@@ -0,0 +1,68 @@
+++
title = "VPN"
description = "A WireGuard tunnel: reach your own network from any Wi-Fi, or send everything through it on a network you don't trust."
weight = 12
[extra]
tag = "WireGuard"
+++
The Cardputer can join a **WireGuard** network over whatever Wi-Fi it is on. Two uses: reaching your own machines from anywhere (an IRC bouncer, the device's own [Debug Console](/dev/debug/)), and keeping its traffic private on a hotel or café network.
You need a WireGuard server, yours or a provider's, and the configuration file it gives a client: a `.conf`.
## Setting it up
1. On the server, make a configuration for a new client, as you would for a phone.
2. Copy the file to the SD card as **`/vpn/wg0.conf`**.
3. On the device: **Settings → VPN → Import /vpn/wg0.conf**.
4. Say yes when it offers to **delete the file**: the configuration is now stored in the device, and the file on the card still holds the private key in clear.
If the file can't be used, the page says which line and why. The key itself is never shown, anywhere, once imported.
## Using it
**Settings → VPN** has a switch. `VPN` appears in the [Status Bar](/guide/basics/#the-status-bar) while the tunnel is wanted, and turns bright once the server has answered. The page shows the state, the server, this device's address in the tunnel, what goes through it, and how long ago the server was last heard.
- **The switch is for now.** It doesn't survive a restart.
- **Start with Wi-Fi**, off by default, starts the tunnel whenever Wi-Fi connects.
- The tunnel waits for the clock: WireGuard needs the time. The clock is set over plain Wi-Fi first, or from GNSS.
- A [Toast](/guide/basics/#toasts) says when the tunnel comes up, and when the server stops answering.
## What goes through it
That depends on the `AllowedIPs` line of the file, and there are only two cases:
| The file says | What happens |
|---|---|
| `AllowedIPs = 0.0.0.0/0` | **Everything** goes through the tunnel. While the server is silent, nothing leaves the device at all. |
| Anything else | **One subnet** goes through it: the one the device's own tunnel address is in (for `10.9.0.2` with `AllowedIPs = 10.9.0.0/24`, that is `10.9.0.x`). The rest goes out on Wi-Fi as before. |
**A home network behind the server can only be reached with the first kind.** If the file lists `10.9.0.0/24, 192.168.1.0/24`, the second range isn't routed, and the import says so: "through it 10.9.0.0/24, not 1 other range". This is a limit of the device's network software, which can route by one subnet or by default and nothing finer.
The DNS servers in the file are used while the tunnel is up, if they can be reached through it.
## Being reached through it
With the tunnel up, the device answers on its tunnel address as it does on Wi-Fi: the [Debug Console](/dev/debug/) if you switched it on (it still wants its token), and the port that receives firmware updates (they still have to be signed).
## From the Shell
```
vpn status what it is doing
vpn up on, until the next restart
vpn up 120 on for two minutes, then off by itself
vpn down
vpn import reads /vpn/wg0.conf (or the path you give)
vpn forget stops it and erases its keys from the device
vpn auto on|off start with Wi-Fi
```
`vpn up` with a number of seconds is for trying a new configuration from a distance: if it cuts you off, it comes back by itself.
## Limits
One tunnel, to one server. IPv4 only: IPv6 addresses in the file are left out. The server can be an address or a name.
## The keys, as the device lists them
{{ keys(scopes=["vpn"]) }}