c791102ad9e5d2a483bf387bf5277ff13931e37d
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c791102ad9 |
Fix overlay and virtual interface candidate leakage and WireGuard rekeying
On Windows (Wintun), iface.name in netdev is an adapter GUID, while
iface.friendly_name is 'tsun0'. Checking only iface.name failed to
identify and exclude Wintun overlay interfaces. Consequently, Windows
nodes announced their own overlay IP addresses (e.g. 10.13.37.26) as
candidate endpoints. Remote peers latched onto this overlay IP as the
WireGuard target, routing encrypted packets into tsun0 (recursive loop),
resulting in failed handshakes and subsequent HANDSHAKE(REKEY_TIMEOUT)
and CONNECTION_EXPIRED.
Similarly, Docker bridges (br-*, docker0) and virtual bridges were
announced and alphabetically sorted before physical LAN or Tailscale
interfaces, causing peers to attempt WireGuard handshakes with internal
Docker bridge addresses (e.g. 172.21.0.1).
Key changes:
- In crates/tsunagi-wireguard and crates/tsunagi-tcp-tls:
- Robustly identify overlay interfaces by inspecting iface.name,
iface.friendly_name, and iface.description for 'tsun', 'tsunagi',
and 'wintun'.
- Filter out container and virtual bridge interfaces (docker, br-,
veth, virbr, cni, flannel, cali, kube, dummy, lxc, podman, vethernet,
vboxnet, vmnet).
- Prioritize candidates by physical interface score: interfaces with
a default gateway (physical LAN/WAN) and Tailscale (100.64.0.0/10)
score highest and sort to candidate #0.
- In on_peer_capability, filter out any announced address falling inside
any active overlay IPv4 range (defense in depth on receiver).
- In crates/tsunagi-wireguard/src/transport.rs:
- In WireguardLink::send, broadcast Handshake Initiation packets (type 1)
to all candidate addresses so initial handshakes and rekeys probe all
candidate paths and recover if an endpoint roamed or NAT expired.
- In accept_loop, prioritize exact socket address matches before falling
back to candidate IP matches for NAT port roaming.
- In crates/tsunagi/src/net.rs:
- Update iroh AddrFilter to inspect friendly_name and description,
preventing Wintun overlay IPs from leaking into iroh discovery endpoints.
- Tests:
- Add unit tests for candidate interface filtering, IP validation, and
priority scoring in tsunagi-wireguard and tsunagi-tcp-tls.
Validation on Linux:
- cargo fmt --all -- --check passed.
- cargo clippy --locked --workspace --all-targets -- -D warnings passed.
- cargo test --locked --workspace --all-targets passed (350+ tests passed).
|
||
|
|
f80841da63 |
Fix WireGuard multi-network demux, candidate discovery, and status reporting
When a peer belongs to multiple overlay networks served by one agent (e.g.
a VM participating in both 'hexor' and 'ultradesu'), packets received on
the shared WireGuard UDP socket could not be differentiated by source IP/port
alone. Previously, `WireguardTransport::accept_loop` picked the first
matching link found in iteration order, starving other networks for that peer
and causing rekey timeouts (`WARN HANDSHAKE(REKEY_TIMEOUT)`,
`ERROR CONNECTION_EXPIRED(REKEY_ATTEMPT_TIME)`) and packet loss on the
starved network.
Additionally:
- When a peer had multiple candidate endpoints (such as LAN, VM bridge, and
Tailscale CGNAT addresses), `WireguardLink` sent handshake initiations
only to `addrs[0]`. If the first address was unreachable from the remote
side (e.g. host's Tailscale IP unreachable from inside a local VM), the
handshake never completed.
- In `build_report`, peers known to the `wg-quic` plugin were added with
empty tunnels even if an active `wg` (pure WireGuard) tunnel existed,
causing `tsunagi status` to falsely report 'no WireGuard handshake yet'
for connected and pingable peers.
- `local_ip_candidates` previously included IPv6 addresses which caused
OS error 22 (EINVAL) when passed to the IPv4-bound UDP socket.
Key fixes:
- In `crates/tsunagi-wireguard/src/transport.rs`:
- Store `peer_networks` as `HashSet<NetworkId>` per peer.
- In `WireguardLink`, maintain a list of candidate addresses and a
`confirmed` flag. Before a handshake response confirms the active path,
transmit datagrams (handshake initiations) to all candidate addresses.
Upon receiving an inbound datagram, lock onto the confirmed address.
- In `accept_loop`, deliver inbound datagrams to all active links
associated with the sender's endpoint ID across all networks. Boringtun's
cryptographic index and AEAD verification naturally demultiplexes and
accepts the packet in the correct network while discarding it in others.
- Filter candidate addresses to IPv4 to match the bound socket family.
- In `crates/tsunagi-wireguard/src/plugin.rs`:
- Only collect IPv4 interfaces in `local_ip_candidates`.
- In `crates/tsunagi-cli/src/main.rs`:
- Refactor `build_report` to evaluate all protocols per peer and prioritize
whichever protocol has an active tunnel (`wg` > `tcp-tls` > `wg-quic`).
- In `crates/tsunagi-wireguard/tests/plugin_tests.rs`:
- Add `test_wireguard_two_networks_same_peers` verifying simultaneous
bidirectional packet transmission between two peers sharing two networks
over a single `WireguardTransport`.
Validation on Linux:
- cargo fmt --all -- --check passed.
- cargo clippy --locked --workspace --all-targets -- -D warnings passed.
- cargo test --locked --workspace --all-targets passed (350+ tests passed).
|
||
|
|
7070a16104 |
Add pure WireGuard direct UDP data plane plugin and transport
Peers communicating on uncensored networks can benefit from raw WireGuard
performance over direct UDP without QUIC framing or double-encryption
overhead. This provides Tailscale-like direct tunnel performance while
preserving Tsunagi's decentralized mesh architecture.
Key changes:
- Create `tsunagi-wireguard` crate implementing the `IpPlugin` and
`PacketTransport` contracts:
- `WG_PROTOCOL = "wg"` with wire version 1.
- Bounded `WgAnnouncement` carrying WireGuard public keys, listening UDP
ports, and candidate IP addresses (filtering out overlay interfaces).
- `WgKeyStore` managing persistent per-network WireGuard X25519 keys
in SQLite with zeroization on drop.
- `WireguardTransport` providing direct UDP packet transmission, candidate
address tracking, inbound link delivery, and roaming support.
- `WireguardDevice` running userspace WireGuard encryption via `boringtun`,
timer maintenance, and AllowedIPs source enforcement.
- Integrate `wg` into `tsunagi-cli`:
- Set default protocols to `"wg,tcp-tls,wg-quic"` so all protocols are
enabled by default in order of throughput and performance.
- Support `"wireguard"` as a convenience alias for `"wg"` in `--protocol`.
- Bind `PureWgTransport` and launch the inbound datagram accept loop on `up`.
- Pass `pure_wg` plugin to `AgentControl` and update `build_report` so
active WireGuard tunnels and overlay metrics are reported in `tsunagi status`.
- Clean up WireGuard keys on network removal in `forget_protocol_state`.
- Comprehensive test coverage in `tsunagi-wireguard`:
- `announcement_tests`: round-trip serialization and verification of malformed,
mismatched network, zero port, or self-claiming announcements.
- `transport_tests`: loopback UDP link delivery between transports.
- `plugin_tests`: end-to-end two-peer handshake and bidirectional IP packet
transit over direct UDP.
Validation on Linux:
- cargo fmt --all -- --check passed.
- cargo clippy --locked --workspace --all-targets -- -D warnings passed.
- cargo test --locked --workspace --all-targets passed.
|
||
|
|
46575d0899 |
Prioritize stored agreed range over default range in reserve_range
When an agent restarted with multiple networks (e.g. hexor and ultradesu), Agent::reserve_range() previously attempted to reserve the default configured range (10.13.37.0/24) for whichever network happened to be listed first in storage, even if that network had already agreed upon a different range (such as 10.79.175.0/24). As a result, the first network seized 10.13.37.0/24 in the route table, causing the second network (which genuinely used 10.13.37.0/24) to trigger a spurious RouteError::Overlap warning at startup until the first network asynchronously loaded its signed records and released the reservation. Key changes: - In crates/tsunagi/src/agent/mod.rs, make reserve_range asynchronous and inspect stored signed records first using StateSet::agreed_range(). - If an agreed range already exists in storage, reserve that specific range in routes immediately instead of preemptively claiming the default overlay_ipv4_range. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets --jobs 2 -- --test-threads=2 passed (349 tests passed). |
||
|
|
a89e65475d |
Implement Debug manually for PipeSecurityAttributes on Windows
The windows_sys::Win32::Security::SECURITY_ATTRIBUTES struct is a raw C FFI struct that does not implement std::fmt::Debug. Deriving Debug on PipeSecurityAttributes failed Windows compilation with E0277. Key changes: - In crates/tsunagi/src/ipc/windows.rs, remove #[derive(Debug)] on PipeSecurityAttributes and provide an explicit std::fmt::Debug implementation formatting nLength, lpSecurityDescriptor, and bInheritHandle. Validation on Linux: - Verified type check against Windows target via rustc. - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets --jobs 2 -- --test-threads=2 passed (349 tests passed). |
||
|
|
2ed447ae86 |
Filter overlay interface from candidates and align status member columns
Peers previously advertised overlay interface (tsun0) addresses as physical candidate endpoints for both iroh discovery and tcp-tls announcements. When an intermediate tunnel was active between two peers (e.g., between a host and a VM), iroh probes to the remote peer's overlay address were routed through the existing overlay, causing iroh to select the overlay IP itself as a 'direct' endpoint. This caused recursive tunneling (QUIC over WireGuard over TLS) and confusing status output showing peers connecting to their own overlay IP addresses. Additionally, in tsunagi status, peer rows were displayed with unaligned columns, mixing control plane status and data plane metrics in a way that was difficult to scan. Key improvements: - In crates/tsunagi/src/net.rs, add an AddrFilter to the iroh EndpointBuilder to dynamically filter out any local addresses assigned to tsun* overlay interfaces before publishing to address discovery services (DHT/pkarr/STUN). - In crates/tsunagi-tcp-tls/src/plugin.rs, exclude tsun* interfaces from local_ip_candidates() so overlay IPs are never advertised as physical TCP endpoints. - In crates/tsunagi-cli/src/main.rs, exclude tsun* interfaces from netwatch_addresses() so the local addresses section reflects only actual physical/host network interfaces. - In crates/tsunagi-cli/src/main.rs, update member_row() to format member details with aligned columnar layout: the overlay IP is placed first and padded, followed by control plane transport and latency, and line 2 pads tunnel metrics so that the data link endpoint (via ...) aligns vertically. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets --jobs 2 -- --test-threads=2 passed (349 tests passed). |
||
|
|
ac5a1596ef |
Allow unelevated Windows processes to access control socket via SDDL and shared path
When running the tsunagi agent as an elevated Administrator or Windows Service, unprivileged user-space processes (such as a system tray application, user-level CLI commands, or status monitors) could not interact with the control socket. This was due to two constraints on Windows: 1. The default control socket path was derived from the per-user %APPDATA%, making an administrator's socket invisible or inaccessible to a non-elevated standard user profile. 2. Even if pointed to the same named pipe, Windows Mandatory Integrity Control (MIC) and default ACLs prevent a Medium-Integrity (unelevated) process from opening a High-Integrity (elevated) pipe server for write access unless explicitly allowed. Key changes: - In crates/tsunagi/src/ipc/windows.rs, implement PipeSecurityAttributes using Win32 ConvertStringSecurityDescriptorToSecurityDescriptorW with SDDL: "D:(A;;GRGW;;;WD)(A;;GA;;;BA)(A;;GA;;;SY)S:(ML;;NW;;;ME)". This grants generic read/write access to Everyone (WD), retains full control for Administrators (BA) and LocalSystem (SY), and sets the Mandatory Label SACL to Medium Integrity (ME) with No-Write-Up (NW). - In crates/tsunagi/src/ipc/mod.rs, introduce default_control_socket_path() which points to %ProgramData%\tsunagi\agent.sock on Windows (and default per-user state directory on Unix). - In crates/tsunagi-cli/src/main.rs, support TSUNAGI_CONTROL_SOCKET environment variable across all commands, and use default_control_socket_path() on Windows when running against the default state directory. Custom state directories (--state-dir) or explicit sockets (--control-socket) continue to take precedence, allowing multi-daemon setups to remain strictly isolated. - In workspace Cargo.toml, set unsafe_code = "deny" so the workspace forbids unsafe code while permitting explicit, documented unsafe Win32 API calls in windows.rs. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets --jobs 2 -- --test-threads=2 passed (349 tests passed). |
||
|
|
192ab25bbd |
Support RUST_LOG, enable UPnP portmapper and add NAT traversal logging
Previously, `tsunagi-cli` configured clap with a fixed default_value and `env = "TSUNAGI_LOG"`, which caused `RUST_LOG` to be completely ignored. In addition, `iroh` was built with `default-features = false` and lacked the `"portmapper"` feature, which caused it to compile a stub client and disable UPnP-IGD, PCP, and NAT-PMP port mapping. Furthermore, there was no visibility into STUN address discovery, UPnP port mapping status, or QUIC transmission path switching (hole punch vs relay). Key improvements: - Enable the `"portmapper"` feature on `iroh` in workspace Cargo.toml, pulling in `portmapper` and `igd-next` to actively request port mappings on local routers. - In `crates/tsunagi-cli/src/main.rs`, support `RUST_LOG` with fallback precedence (`--log` > `TSUNAGI_LOG` > `RUST_LOG` > default filter). When debug or trace filtering is active, automatically enable `.with_target(true)` so users can see which subsystem emitted the log (e.g., portmapper, iroh::magicsock, tsunagi::net). - In `crates/tsunagi/src/net.rs`, monitor `endpoint.watch_addr()` and `endpoint.home_relay_status()`. Emit `DEBUG` logs for local socket binding, discovered STUN reflexive/UPnP addresses, and relay connections. If no direct addresses are discovered after probe grace, emit a concise `WARN`. - In `crates/tsunagi/src/agent/session.rs`, monitor connection `path_events()` to log when transmission paths are opened, closed, or selected (switching between relay and direct hole-punched paths), with bounded task winding down on shutdown. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets passed (349 tests passed). - Live verification with `RUST_LOG=debug` confirmed SSDP broadcast to 239.255.255.250:1900, portmapper logs, STUN QAD discovery, and clean relay shutdown. |
||
|
|
c0a76f1942 |
Enable all available protocols by default with tcp-tls priority
By default, the agent now enables all available data plane protocols starting with `tcp-tls,wg-quic`, removing the need to manually pass `--protocol` to enable HTTPS-camouflage transport. The `--protocol` option remains available to explicitly restrict or select specific protocols (or reorder their priority). Furthermore, in `crates/tsunagi-tcp-tls`, if default port 443 is unavailable (e.g., due to permission constraints or address in use), the transport now attempts fallback port 8443, and if 8443 is also unavailable, gracefully falls back to an ephemeral port rather than failing agent startup. Key changes: - In `crates/tsunagi-cli/src/main.rs`, update `UpArgs::protocols` default_value to `"tcp-tls,wg-quic"`. - In `crates/tsunagi-cli/src/main.rs`, reorder `PROTOCOLS` registry with `tcp-tls` first, followed by `wg-quic`. - In `crates/tsunagi-tcp-tls/src/transport.rs`, add multi-tier fallback (443 -> 8443 -> ephemeral) for unconstrained port bindings. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets passed (349 tests passed). - cargo build --release passed. |
||
|
|
510116afb9 |
Add automatic protocol fallback with backoff and silence DHT bootstrap error
When multiple data plane protocols (such as tcp-tls and wg-quic) are configured, the agent previously attempted only the first mutually supported protocol in served order. If that protocol failed to connect or dropped (due to NAT, firewall, or handshake failure), ensure_links retried the exact same failed protocol on every tick without ever falling back to alternative supported protocols. Additionally, tcp-tls mistakenly included 127.0.0.1 in peer candidate lists, causing peers to connect to their own local listener and fail certificate verification (NotValidForName). Finally, BitTorrent DHT bootstrap failures in the third-party mainline crate were logged as noisy ERRORs despite overlay connectivity operating normally. Key improvements: - In crates/tsunagi/src/agent/network.rs, implement ProtocolDialState with exponential backoff (3s, 6s, 12s, 24s, 48s, capped at 60s). - In ensure_links, select the highest-priority mutually supported protocol that is not currently on backoff cooldown. If a protocol fails, immediately record the failure and fall back to alternative mutually supported protocols without waiting for the periodic ticker. - In crates/tsunagi-tcp-tls, remove localhost loopback from local_ip_candidates and capability announcements, and prevent open() from connecting to our own listening port. - In crates/tsunagi-cli/src/main.rs, set mainline=off in the default tracing filter so benign BitTorrent DHT routing table bootstrap errors do not alarm users. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets passed (349 tests passed). - cargo build --release passed. |
||
|
|
e24548b3b0 |
Fix Windows address allocation check and add custom SNI for tcp-tls
When running on Windows with Wintun, assigning an overlay IPv4 address
via netsh triggers Duplicate Address Detection (DAD). During this tentative
window, binding a UDP socket to the address returns WSAEADDRNOTAVAIL
(os error 10049), causing the agent to prematurely log a false-positive
PluginError ('allocated ... but the address is not on any interface').
Furthermore, Russian TSPU / DPI filters block unrecognized SNIs and
non-standard ALPNs on TCP port 443 with TCP RST injection (os error 10054).
The default 'tsunagi.local' SNI and 'tsunagi-tls/1' ALPN were trivially
fingerprinted.
Key improvements:
- In crates/tsunagi/src/overlay/tun.rs, enhance address_is_local on Windows
to inspect active OS interfaces via netdev::get_interfaces() if UDP
bind fails.
- In crates/tsunagi/src/agent/network.rs, introduce an asynchronous retry
loop in publish_allocations on Windows (up to 1.5s in 150ms intervals)
to gracefully allow DAD and IP helper updates to complete before
reporting missing addresses.
- In crates/tsunagi-tcp-tls, add support for custom SNI domain via
protocol options (-o sni=<domain>), defaulting to 'cloudflare.com'
instead of 'tsunagi.local'.
- Switch TLS ALPN in tsunagi-tcp-tls from 'tsunagi-tls/1' to 'http/1.1'
to camouflage handshake traffic as standard HTTPS web traffic.
- Update tests to verify custom and default SNI behavior.
Validation on Linux:
- cargo fmt --all -- --check passed.
- cargo clippy --locked --workspace --all-targets -- -D warnings passed.
- cargo test --locked --workspace --all-targets passed (348 tests passed).
- cargo build --release passed.
|
||
|
|
a4e341cd66 |
Report peer tunnel protocols accurately and eliminate overlay shutdown timeout
The status output previously reported all overlay peers under WireGuard diagnostics: when a non-WireGuard protocol (like tcp-tls) was actively carrying traffic, the report still complained that there was 'no WireGuard handshake yet; the tunnel cannot carry traffic' and marked peer health as degraded (warn). Furthermore, during agent shutdown or interface removal, the overlay packet loop waited on device.recv() for the full 5-second TASK_GRACE period, producing a spurious warning: 'task did not wind down in time; aborting it task="overlay packet loop"'. Key improvements: - Add 'protocol' field to OverlayPeerReport in tsunagi::ipc. - Track per-peer packet metrics (tx, rx, dropped) and uptime in GenericTunnelPlugin via TunnelPeerReport. - Update build_report to accurately inspect and populate active tunnel protocols (tcp-tls, wg-quic) and their real traffic counters. - Format member rows in CLI status with the actual protocol name, uptime, and real traffic metrics; mark health as Good when an active tunnel exists. - In Interface::remove(), abort the overlay packet loop task immediately before destroying the device to remove the 5s hang and spurious timeout warning. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets passed (348 tests passed). - cargo build --release passed. |
||
|
|
41990c9934 |
Refactor agent logging and report active tcp-tls tunnels
Previous agent logging suffered from noisy repetitive '--- status ---' dumps to stdout every 15 seconds, intermingled with raw println! event lines and lacking structured log levels. Furthermore, the status report only inspected WireguardPlugin tunnels; when tcp-tls took precedence and established a link, the status falsely showed wg-quic as '0/1 tunnels established, waiting for a data link' without mentioning the active tcp-tls link. Key changes: - Adopt compact, clean tracing output: initialize tracing_subscriber with .with_target(false).compact() and default filter 'info,iroh=warn,quinn=warn,rustls=warn,boringtun=warn,mainline=warn' to keep standard agent events at INFO while silencing noisy internals. - Route agent events through structured tracing (log_event): emit INFO for peer connect/disconnect, data link up/down, and network activation; WARN for rejections and plugin errors; DEBUG for dials. - Disable periodic status dump by default (set status_interval default to 0), and re-route periodic ticks to tracing::debug! (log_status_summary). - Add active_peers() to GenericTunnelPlugin and update build_report to include active tcp-tls tunnels in OverlayReport alongside WireGuard peers. - Display enabled protocol priority on agent startup. Validation on Linux: - cargo fmt --all -- --check passed. - cargo clippy --locked --workspace --all-targets -- -D warnings passed. - cargo test --locked --workspace --all-targets passed (347 tests passed). |
||
|
|
254a25ebd6 |
Display supported protocols in status report host section
The `host` section in `tsunagi status` previously hardcoded `implementation: userspace WireGuard (boringtun); no kernel module needed` and omitted information about data plane protocols supported by the build. Changes: - Add `protocols` row to `host_section` listing supported protocols and their wire versions (`wg-quic (wire v4), tcp-tls (wire v1)`) with a note directing to `tsunagi protocols` for detailed options. - Update `implementation` row to reflect the multi-protocol userspace data plane (`userspace overlay (boringtun WireGuard, TCP TLS 1.3); no kernel module needed`). Validation on Linux: - `cargo fmt --all -- --check` passed. - `cargo clippy --locked --workspace --all-targets -- -D warnings` passed. - `cargo test --locked --workspace --all-targets` passed. |
||
|
|
5495cb5b34 |
Support CAP_NET_BIND_SERVICE raising for privileged TCP TLS ports
When file capabilities are granted with `sudo setcap ...+p`, capabilities are placed into the thread's permitted set rather than its effective set. While TUN interface provisioning in `tsunagi` properly acquired and dropped CAP_NET_ADMIN using `caps::raise`, `tsunagi-tcp-tls` attempted to bind port 443 directly without activating CAP_NET_BIND_SERVICE, resulting in EACCES (os error 13 Permission denied). Fixes and improvements: - Add `caps` dependency to `tsunagi-tcp-tls` on Linux. - Implement `NetBindServiceGuard` to temporarily raise CAP_NET_BIND_SERVICE into the effective set for the synchronous `TcpListener::bind` invocation and immediately drop it afterwards. - Clarify diagnostic warnings to distinguish between default port 443 fallback and explicitly requested port failure. - Update all privilege diagnostics and error hints across the codebase to uniformly recommend granting both capabilities: `sudo setcap cap_net_admin,cap_net_bind_service+p <path>`. Validation on Linux: - `cargo fmt --all -- --check` passed. - `cargo clippy --locked --workspace --all-targets -- -D warnings` passed. - `cargo test --locked --workspace --all-targets` passed (all workspace unit, integration, and protocol tests passed). |
||
|
|
fac3bd7bb9 |
Add tcp-tls transport and tunnel protocol disguised as HTTPS
Implement the `tcp-tls` protocol crate and generic tunnel codec abstractions alongside `wg-quic` to circumvent deep packet inspection (DPI/TSPU), UDP blocking, and QUIC throttling. Traffic uses standard TCP on port 443 with TLS 1.3 framing, looking identical to regular HTTPS traffic to network observers. Architectural highlights: - TunnelCodec & GenericTunnelPlugin: Provide an extensible plugin abstraction in `crates/tsunagi/src/dataplane/tunnel.rs` that encapsulates background task lifecycles, per-peer channels, and sink dispatch, so new transport plugins only define cryptographic transformations and handshake payloads. - MultiPacketTransport & Protocol Priority Selection: Support multiple simultaneous transports in `AgentConfig` and dynamically dispatch `open()` calls by protocol identifier. In `ensure_links`, choose the highest-priority mutually supported protocol per peer to avoid duplicate links. - TLS 1.3 & Certificate Pinning: Generate self-signed Ed25519 X.509 certificates derived from the persistent `DeviceIdentity` secret. Clients pin peer certificates using `PinnedEndpointVerifier` against `iroh::EndpointId`. - End-to-end Encryption: `PeerCipher` uses X25519 static-static ECDH, HKDF-SHA256, and ChaCha20-Poly1305 with incrementing 64-bit nonces. Relayed traffic across intermediate nodes remains confidential and tamper-proof. - Privilege handling: Default to TCP port 443 with fallback to 8443 on `PermissionDenied`, providing an actionable message recommending: `sudo setcap cap_net_admin,cap_net_bind_service+p <path>`. Validation on Linux: - `cargo fmt --all -- --check` passed. - `cargo clippy --locked --workspace --all-targets -- -D warnings` passed. - `cargo test --locked --workspace --all-targets` passed (all workspace unit, integration, CLI, and new `tsunagi-tcp-tls` tests passed). |