Culvert cannot detect the one WireGuard failure its own slot machinery exists to prevent: two devices on one key.
The slots in #27 are there because WireGuard keeps one endpoint per peer, so two devices sharing a key answer only whichever handshook last. The code says so plainly (scripts/lib/wireguard.py, the Device slots comment). But nothing verifies it holds at runtime.
start_wg_connection_monitor polls wg show wg0 latest-handshakes and wg show wg0 transfer, and decides a peer is connected when its handshake is under 180s old. Two devices on one key rekey constantly, so that check reports a healthy, continuously connected peer the whole time traffic is going to the wrong one. wg show wg0 endpoints is never read, by the monitor or anywhere else. Neither is the metrics path -- lib/metrics.py parses handshakes and transfer, nothing else.
So the operator's only route to the answer is to be shelled into the box running wg show wg0 endpoints in a loop at the moment someone reports a drop. That is how #41 has gone.
The data is already one subprocess call away and the monitor loop is already running. Watching a peer's endpoint across poll cycles and logging when it changes between two addresses inside a short window would turn this from a two-day investigation into a log line.
Worth pairing with a metric, since the log line only helps someone already reading logs.
Two things to get right:
- A ROAMING client legitimately changes endpoint -- wifi to mobile, a CGNAT re-map. That is one address replacing another and then staying put. A shared key FLAPS between two addresses repeatedly. Detect the flap, not the change, or every phone on the estate trips it.
- The config generator already refuses duplicate
--pubkey values with a good error. This is the runtime half of the same rule.
Related: #41, where a shared slot is the live hypothesis and this gap is why it cannot be confirmed.
Done when two devices loading the same slot config produces a warning naming the peer and both endpoints, without anyone shelling into the box.
Culvert cannot detect the one WireGuard failure its own slot machinery exists to prevent: two devices on one key.
The slots in #27 are there because WireGuard keeps one endpoint per peer, so two devices sharing a key answer only whichever handshook last. The code says so plainly (scripts/lib/wireguard.py, the Device slots comment). But nothing verifies it holds at runtime.
start_wg_connection_monitorpollswg show wg0 latest-handshakesandwg show wg0 transfer, and decides a peer is connected when its handshake is under 180s old. Two devices on one key rekey constantly, so that check reports a healthy, continuously connected peer the whole time traffic is going to the wrong one.wg show wg0 endpointsis never read, by the monitor or anywhere else. Neither is the metrics path --lib/metrics.pyparses handshakes and transfer, nothing else.So the operator's only route to the answer is to be shelled into the box running
wg show wg0 endpointsin a loop at the moment someone reports a drop. That is how #41 has gone.The data is already one subprocess call away and the monitor loop is already running. Watching a peer's endpoint across poll cycles and logging when it changes between two addresses inside a short window would turn this from a two-day investigation into a log line.
Worth pairing with a metric, since the log line only helps someone already reading logs.
Two things to get right:
--pubkeyvalues with a good error. This is the runtime half of the same rule.Related: #41, where a shared slot is the live hypothesis and this gap is why it cannot be confirmed.
Done when two devices loading the same slot config produces a warning naming the peer and both endpoints, without anyone shelling into the box.