Skip to content

feat: UDP through method=tproxy - #11

Merged
dennisklein merged 9 commits into
mainfrom
claude/project-thread-kmxtqs-tproxy
Oct 11, 2026
Merged

dennisklein merged 9 commits into
mainfrom
claude/project-thread-kmxtqs-tproxy

Conversation

@dennisklein

@dennisklein dennisklein commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Requested by Dennis · project thread

Before: only TCP and DNS went through the tunnel. Other UDP to a tunnelled network (QUIC, NTP, a DNS server queried directly) was refused by the guard.

After: a profile with method=tproxy sends TCP and UDP through the tunnel, keeps failing closed while it is down, and leaves nothing behind. nft stays the default. The GNOME editor offers tproxy in its method drop-down and accepts it.

It implements docs/later/tproxy.md. Merge it before #12, which is stacked on it.

How: sshuttle gets --tmark 0x4e530000. The plugin installs its own policy routing (ip rule … fwmark 0x4e530000 lookup 20051 at priority 100, plus local default dev lo table 20051) together with the guard, for the families the tunnel carries. It removes the rule and route by their exact values. The guard accepts packets with that mark ahead of its dnat rules. The sweep removes stale sshuttle-[mtd]-PORT chains through iptables. For tproxy, the health check wants a live mark chain per family, and it puts back a missing rule or route. If iptables or ip6tables is missing, Connect and nm-sshuttle check fail. The packages recommend iptables. The shared profile cases, the editor's C validator and its drop-down now allow tproxy.

Also in this PR:

  • VM preparation on main fails: it asks dnf for libnm-devel, which Fedora does not have. The first commit installs the editor's build dependencies by their pkgconfig(...) names, as the RPM spec does.
  • Lab: it waits until sshuttle's rules are in place, not only until sshuttle logs "Connected to server".
  • VM journal reads: the tests read the journal through one journalctl --follow for the whole run, M2-29 included. Fresh reads after a cursor or by unit at times missed the plugin's newest lines.

Results:

  • VM (Fedora 44): 521 passed, 0 failed, including TP-01 to TP-03 and main's M2-29. The VM's iptables uses the legacy backend, which Fedora's sshuttle package pulls in. There nft flush ruleset leaves sshuttle's mark chain, so only the guard comes back and the tunnel stays up. TP-02 checks that, and the design records it.
  • Lab T15–T19: 49 passed, 0 failed, 1 skipped. T15 shows that DNS pinned to the tunnel link with IP_UNICAST_IF is captured, so dns=split stays allowed.
  • Unit tests: 283 pass. The editor's C tests pass. CI is green.

Profile keys: none change. method takes tproxy, as the design says.

🤖 Generated with Claude Code

@dennisklein dennisklein self-assigned this Oct 11, 2026
@dennisklein
dennisklein force-pushed the claude/project-thread-kmxtqs-tproxy branch from 13f415e to 93f0896 Compare October 11, 2026 12:18
@dennisklein
dennisklein force-pushed the claude/project-thread-kmxtqs-tproxy branch 8 times, most recently from e17aa47 to 59520ce Compare October 11, 2026 13:39
Base automatically changed from claude/project-thread-kmxtqs to main October 11, 2026 13:42
@dennisklein
dennisklein force-pushed the claude/project-thread-kmxtqs-tproxy branch 5 times, most recently from 2a1d8c1 to d120c8d Compare October 11, 2026 14:30
@dennisklein
dennisklein marked this pull request as ready for review October 11, 2026 14:45
@dennisklein
dennisklein force-pushed the claude/project-thread-kmxtqs-tproxy branch from d120c8d to afcadfd Compare October 11, 2026 19:52
- Fedora has no package named libnm-devel (it is
  NetworkManager-libnm-devel), so preparing the VM failed
- Ask dnf for pkgconfig(gtk4), pkgconfig(libnm) and
  pkgconfig(json-glib-1.0), as the RPM spec does
- sshuttle logs "Connected to server" before it installs its rules, so a
  request right after could miss the redirect and hang until its timeout
- wait_connected now also waits for sshuttle's systemd READY, which comes
  after the rules; with nothing on NOTIFY_SOCKET it shows in the log
- Accept method=tproxy; pass sshuttle the mark 0x4e530000 with --tmark
- Route marked packets to lo with a rule and a local route of the
  plugin's own, installed with the guard for the families the tunnel
  carries and removed by their exact values
- Let the guard accept packets with that mark ahead of the dnat rules
- Sweep stale sshuttle-[mtd]-PORT chains from iptables' mangle table
- Check health per method: a live mark chain per family for tproxy, and
  put back a missing rule or route
- Fail at Connect, and in check, when iptables or ip6tables is missing;
  remove the routing in cleanup; offer tproxy in add --method
- Recommends iptables in the deb and iptables-nft in the RPM; sshuttle's
  tproxy method runs iptables and ip6tables
- Add a UDP echo server and client for the lab
- Run sshuttle's tproxy method behind the plugin's own guard, policy
  routing and sweep, with a route to the internal network and a counter
  on the uplink so that a leak would show
- T15 DNS pinned with IP_UNICAST_IF, T16 UDP and TCP, T17 tunnel down,
  T18 stale chains after SIGKILL, T19 a UDP flow silent for 40 s
- Install iptables in CI's lab job
- Run a UDP echo server on the internal network
- TP-01: UDP, TCP and split DNS through the tunnel, fail closed in a gap,
  nothing left after the disconnect
- TP-02: a killed sshuttle and nft flush ruleset while up
- TP-03: NM's reload and a roam leave the rule and route alone
- Install iptables-nft in the VM and count mangle chains and the routing
  as leftovers
- Add the method to the profile table, the guard and the health check,
  and list lab tests T15 to T19 in the design
- Mark the tproxy design as implemented and record the T15 result and
  what differs
- Document --method tproxy and its limits in the user guide
- Fedora's agetty is sometimes denied reading the console issue file
  that NetworkManager's dispatcher writes; M2-15 no longer fails on it
- Record the denial in the design, where its record was missing
- Fresh journal reads, by unit, after a cursor or since a time, at times
  missed the plugin's newest entries, which a later read showed; syncing
  the journal before each read did not change that
- One journalctl --follow runs from the start of the run into a file;
  log_mark records the file's size, and plog keeps the lines after it
  whose syslog identifier is nm-sshuttle-service
- The DNS server and the IPv6 web server log under their own
  identifiers, and their checks read the same file since log_mark;
  M2-29 counts the tunnel's pinning lines in it
- journald tells followers about new entries in batches; a check that
  reads the log once first logs a marker and waits for it in the file
- Drop the journal syncs before each read
- A failing check prints 70 lines of state, so the run's log shows the
  plugin's journal tail too
@dennisklein
dennisklein force-pushed the claude/project-thread-kmxtqs-tproxy branch from afcadfd to a0f435f Compare October 11, 2026 19:56

Copy link
Copy Markdown
Owner Author

The vm check failed before any test ran, and the cause is on main, not in this PR. test-vm/vm.sh on main asks dnf for libnm-devel, but Fedora has no package by that name (it is NetworkManager-libnm-devel), so preparing the VM fails. The editor PR added that line, and no VM run had used it yet.

This PR now carries the fix as its first commit, fix(test-vm): install the editor's build dependencies by pkg-config name. That commit asks dnf for pkgconfig(gtk4), pkgconfig(libnm) and pkgconfig(json-glib-1.0), as the RPM spec does. The VM tests are running again on the new head.


Generated by Claude Code

@dennisklein
dennisklein added this pull request to stack #16 October 11, 2026 19:59
@dennisklein
dennisklein merged commit 17c8e21 into main Oct 11, 2026
8 checks passed
@dennisklein
dennisklein deleted the claude/project-thread-kmxtqs-tproxy branch October 11, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants