feat(cli): OS-level split-tunnel routes (removes send_direct stub)

DIRECT-destination traffic now bypasses the TUN entirely via OS routing
table edits, instead of going through user-space and hitting the v1
send_direct stub. The user-space router only sees VPN-bound packets,
making the split-tunnel real.

- aura_cli::os_routes::OsRouteGuard: RAII install + rollback of OS routes.
  Linux: `ip route show default` parser -> DIRECT CIDRs via original gw,
  VPN default via TUN with metric 50. macOS: `route -n get default`
  parser -> `route add -net/-host ... <gw>` for DIRECT, `route add -net
  ... -interface <tun>` for VPN. Windows: stub + warning (v3).
- dry_run works on every platform (logs `would run: ...`); useful for
  tests and operator confidence-checks.
- SplitRoutes::from_config folds [[tunnel.split.direct]]/[[...vpn]] +
  resolved domains (via AuraDns) into one declarative plan.
- New [tunnel.os_routes] {enabled (default true), dry_run, gateway,
  egress_iface}; absent section = old user-space behavior (back-compat).
- client::run installs routes after AuraTun::create, before privdrop;
  guard's Drop reverts everything on shutdown.
- aura-tunnel::router unchanged; AuraRouter::send_direct kept as a
  defensive fallback (in v2 it should never fire — OS routes prevent
  DIRECT packets from reaching the TUN at all).

20 new tests (linux/macos parser unit tests, install dry-run, config
back-compat). Workspace: 174 tests passed (+19), clippy -D warnings
clean, fmt clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xah30
2026-05-27 02:20:30 +03:00
parent c6f0d7af9b
commit 65b26b555d
6 changed files with 1267 additions and 8 deletions
+25
View File
@@ -54,6 +54,31 @@ domain = "intranet.example.com"
[[tunnel.split.vpn]]
cidr = "10.7.0.0/24"
# v2: OS-level split-tunnel routing. With `enabled = true` (the default) the client programs the
# system routing table at startup so DIRECT destinations bypass the TUN entirely — they continue
# to use the host's original default gateway, while only VPN-classified traffic reaches the
# tunnel. This eliminates the v1 user-space `send_direct` stub, where DIRECT packets were merely
# logged and dropped. The guard is RAII: the routes are rolled back when `aura client` exits.
#
# Linux: uses `ip route add ... via <gw> dev <egress>` for bypasses and `ip route add ... dev <tun>`
# for the VPN default; the VPN default carries metric 50 so it wins over DHCP-installed defaults.
# macOS: uses `route add -net <cidr> <gw>` for bypasses and `route add -net <cidr> -interface <tun>`
# for VPN routes.
# Windows: not implemented in v1 (the section is parsed but the install is a logged no-op).
[tunnel.os_routes]
# Master switch. `false` falls back to the v1 user-space router (the `send_direct` path drops
# DIRECT packets; kept intentionally as a fallback). Default: true.
enabled = true
# When `true`, every routing command is only logged (`would run: ...`) and not executed —
# useful for testing the plan without root. Default: false.
dry_run = false
# Optional explicit IPv4 default gateway. When omitted, auto-detected (Linux:
# `ip route show default`; macOS: `route -n get default`).
# gateway = "192.168.1.1"
# Optional explicit egress interface name (e.g. "eth0" on Linux, "en0" on macOS). When omitted,
# auto-detected alongside the gateway.
# egress_iface = "en0"
[mimicry]
# Enable traffic padding to blend packet sizes into HTTPS buckets.
padding = false