change: read connection state from OpenVPN's management interface #68

Merged
mysticalsoap merged 1 commit from change/management-interface into trunk 2026-08-19 22:21:31 -04:00
Owner

Problem

ovpn() tracked connection state by string-matching OpenVPN's stdout. Three of those strings (TUN/TAP device, PUSH: Received control message, and the classic device line) print zero times under DCO, the default data path since OpenVPN 2.6 — the root cause behind the device-detection and DNS failures. Issue #22.

Fix

  • New aqomui/mgmt.py: a minimal client for OpenVPN's management interface over a unix socket (one per tunnel role: mgmt.sock, mgmt_hop.sock, mgmt_bypass.sock in ROOTDIR, --management-client-user root). It translates >STATE:/>PASSWORD:Verification Failed/>FATAL: notifications into four event kinds; everything else is noise.
  • ovpn() starts each OpenVPN with the management socket and a reader thread; CONNECTED / RECONNECTING(reason) / auth-failed / fatal replace the Initialization Sequence Completed / Restart pause / SIGTERM[soft,auth-failure] / Exiting due to fatal error greps. The reader is fully guarded — a management failure becomes conn_attempt_failed, never an escaped exception in the root service.
  • The pushed-DNS scrape is eliminated, not migrated (per the issue's 'better eliminated' half): OpenVPN's own dns-updown hook applies server-pushed DNS — including default-route true + ~. routing, verified in the shipped script — on resolved systems. aqomui passes --dns-updown disable whenever it owns DNS itself: alt servers configured, DNS off, hop/bypass tunnels, or no resolved. At CONNECTED the applied servers are read back (dns_manager.link_dns) for the GUI display; a server that pushed nothing falls back to the alt servers as before.
  • stdout is still consumed — for the log view, the raw-config device-name fallback, and the double-hop iptables retry.

Scope kept out, per the issue: credentials stay in auth-user-pass files (>PASSWORD:Verification Failed reaches the socket without --management-query-passwords); double-hop orchestration and the stunnel/SSH side channels untouched.

Behavior deltas worth knowing:

  • dns_off now actually means off under 2.7 — previously OpenVPN's default-enabled dns-updown set tun DNS regardless.
  • A hop tunnel no longer touches system DNS at CONNECTED (the old code read dns_hop attributes that never existed under DCO and would have crashed the thread).
  • RECONNECTING carries the reason into the log (ping-restart, tls-error, ...) instead of the bare 'Connection attempt failed'.

Verification

  • 83 tests pass (pytest in a venv), lint gate + compileall clean. New coverage: test_mgmt.py (protocol parsing; a scripted unix-socket server standing in for OpenVPN, including late-socket timeout and EOF-on-exit) and TestManagementEvents/TestDnsUpdownDisabled in test_tunnel.py (every event kind → dispatchable GUI status, hop skips DNS, pushed-DNS read-back vs alt fallback, broken channel → failed attempt).
  • Live verification pending before merge: connect → tray goes colored and resolvectl status tun_aqomui shows the provider-pushed DNS with ~. (not Quad9, unless alt DNS is enabled in settings); disconnect/reconnect; wrong-password auth failure notifies; bypass still comes up.

🤖 Generated with Claude Code

**Problem** `ovpn()` tracked connection state by string-matching OpenVPN's stdout. Three of those strings (`TUN/TAP device`, `PUSH: Received control message`, and the classic device line) print zero times under DCO, the default data path since OpenVPN 2.6 — the root cause behind the device-detection and DNS failures. Issue #22. **Fix** - New `aqomui/mgmt.py`: a minimal client for OpenVPN's management interface over a unix socket (one per tunnel role: `mgmt.sock`, `mgmt_hop.sock`, `mgmt_bypass.sock` in ROOTDIR, `--management-client-user root`). It translates `>STATE:`/`>PASSWORD:Verification Failed`/`>FATAL:` notifications into four event kinds; everything else is noise. - `ovpn()` starts each OpenVPN with the management socket and a reader thread; CONNECTED / RECONNECTING(reason) / auth-failed / fatal replace the `Initialization Sequence Completed` / `Restart pause` / `SIGTERM[soft,auth-failure]` / `Exiting due to fatal error` greps. The reader is fully guarded — a management failure becomes `conn_attempt_failed`, never an escaped exception in the root service. - **The pushed-DNS scrape is eliminated, not migrated** (per the issue's 'better eliminated' half): OpenVPN's own `dns-updown` hook applies server-pushed DNS — including `default-route true` + `~.` routing, verified in the shipped script — on resolved systems. aqomui passes `--dns-updown disable` whenever it owns DNS itself: alt servers configured, DNS off, hop/bypass tunnels, or no resolved. At CONNECTED the applied servers are read back (`dns_manager.link_dns`) for the GUI display; a server that pushed nothing falls back to the alt servers as before. - stdout is still consumed — for the log view, the raw-config device-name fallback, and the double-hop iptables retry. Scope kept out, per the issue: credentials stay in `auth-user-pass` files (`>PASSWORD:Verification Failed` reaches the socket without `--management-query-passwords`); double-hop orchestration and the stunnel/SSH side channels untouched. Behavior deltas worth knowing: - `dns_off` now actually means off under 2.7 — previously OpenVPN's default-enabled dns-updown set tun DNS regardless. - A hop tunnel no longer touches system DNS at CONNECTED (the old code read `dns_hop` attributes that never existed under DCO and would have crashed the thread). - RECONNECTING carries the reason into the log (`ping-restart`, `tls-error`, ...) instead of the bare 'Connection attempt failed'. **Verification** - 83 tests pass (pytest in a venv), lint gate + compileall clean. New coverage: `test_mgmt.py` (protocol parsing; a scripted unix-socket server standing in for OpenVPN, including late-socket timeout and EOF-on-exit) and `TestManagementEvents`/`TestDnsUpdownDisabled` in `test_tunnel.py` (every event kind → dispatchable GUI status, hop skips DNS, pushed-DNS read-back vs alt fallback, broken channel → failed attempt). - **Live verification pending before merge**: connect → tray goes colored and `resolvectl status tun_aqomui` shows the provider-pushed DNS with `~.` (not Quad9, unless alt DNS is enabled in settings); disconnect/reconnect; wrong-password auth failure notifies; bypass still comes up. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
change: read connection state from OpenVPN's management interface
All checks were successful
ci / test (pull_request) Successful in 35s
f1eec9ec34
The stdout state machine matched strings three of which OpenVPN 2.6+
prints zero times under DCO -- the root cause of the device-detection
and DNS failures. State, auth failures and fatal errors now arrive as
structured events over a management unix socket, one per tunnel role,
connectable by root only.

The pushed-DNS scrape is eliminated rather than migrated: OpenVPN's own
dns-updown hook applies server-pushed DNS itself, with "~." routing,
on resolved systems. It is passed "disable" wherever aqomui owns DNS
instead: alternative servers, DNS off, hop and bypass tunnels, and
systems without resolved. Credentials stay in auth-user-pass files --
verification failures reach the socket without them.

stdout is still read, for the log, the raw-config device fallback and
the double-hop iptables retry.

Closes #22

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mysticalsoap force-pushed change/management-interface from f1eec9ec34
All checks were successful
ci / test (pull_request) Successful in 35s
to 703c4c2149
All checks were successful
ci / test (pull_request) Successful in 35s
2026-08-19 15:01:07 -04:00
Compare
mysticalsoap force-pushed change/management-interface from 703c4c2149
All checks were successful
ci / test (pull_request) Successful in 35s
to 5649561855
All checks were successful
ci / test (pull_request) Successful in 28s
2026-08-19 15:06:47 -04:00
Compare
mysticalsoap force-pushed change/management-interface from 5649561855
All checks were successful
ci / test (pull_request) Successful in 28s
to 5ab32b8a8b
All checks were successful
ci / test (pull_request) Successful in 45s
2026-08-19 15:25:59 -04:00
Compare
mysticalsoap deleted branch change/management-interface 2026-08-19 22:21:31 -04:00
Sign in to join this conversation.
No description provided.