Add architecture specification for wraith SSH tunnel tool

Docs:
- README.md: index with doc table, ADR table, lifecycle definitions
- overview.md: purpose, exports, dependencies, constraints
- transport.md: Transport trait, TCP/TLS/iroh implementations, stream join
- client.md: SOCKS5 server, port forwarding, channel manager, reconnection
- server.md: auth, channel handling, stealth mode, outbound proxy
- tun-shim.md: separate privileged process, virtual DNS, --unshare mode
- napi-and-pubsub.md: NAPI wrapper, pubsub event target adapter

ADRs:
- 001: Pluggable transport via AsyncRead+AsyncWrite trait
- 002: TUN shim as separate process
- 003: iroh stream via tokio::io::join
- 004: SSH runs over transport, not alongside
- 005: SOCKS5 as primary interface, TUN as add-on
- 006(007): NAPI exposes single duplex stream

Open questions: 11 items covering TLS certs, iroh relay defaults,
Windows TUN, auth expansion, NAPI surface, TCP reconstruction
This commit is contained in:
2026-06-01 15:01:45 +00:00
parent c1275e2dfd
commit dad8224686
14 changed files with 1116 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
---
status: draft
last_updated: 2026-06-01
---
# TUN Shim
## What
A separate process (`wraith-tun`) that creates a TUN interface, reads IP packets from it, and forwards them through the core wraith client's SOCKS5 port. Requires root or `CAP_NET_ADMIN`.
## Why
The core wraith binary must never require root. TUN interfaces need elevated privileges. By separating TUN into its own minimal process, we:
- Minimize the root-required code surface (auditable in an afternoon)
- Keep the core binary unprivileged for SOCKS5 and port forwarding
- Allow the TUN shim to crash without affecting the SSH session
- Match the proven tun2proxy architecture
(ADR-002)
## Architecture
```
┌─────────────────┐ ┌──────────────────────────────┐
│ wraith-tun │ │ wraith connect │
│ (root) │ │ (unprivileged) │
│ │ │ │
│ ┌────────────┐ │ │ ┌──────────────────────────┐ │
│ │ TUN Device │ │ │ │ SOCKS5 Server │ │
│ │ (tun-rs) │◄├─────┤├►│ :1080 │ │
│ │ 10.0.0.1/24│ │ │ └──────────────────────────┘ │
│ └────────────┘ │ │ │
│ │ │ ┌──────────────────────────┐ │
│ Route all │ │ │ SSH Client (russh) │ │
│ traffic via │ │ │ connect via Transport │ │
│ TUN device │ │ └──────────────────────────┘ │
└─────────────────┘ └──────────────────────────────┘
```
### Data Flow
1. OS routing table sends all traffic through TUN device `tun0`
2. `wraith-tun` reads IP packets from TUN device
3. `wraith-tun` extracts destination IP:port from each packet
4. `wraith-tun` connects to `127.0.0.1:1080` (wraith SOCKS5) with `SOCKS5h` (domain resolution by proxy)
5. `wraith-tun` proxies the TCP connection through SOCKS5
6. wraith's SOCKS5 server opens an SSH direct-tcpip channel to the destination
7. Bytes flow: application → TUN → SOCKS5 → SSH channel → server → target
### Virtual DNS
The TUN shim implements virtual DNS (same approach as tun2proxy):
- DNS queries to port 53 arriving at the TUN device are intercepted
- Query names are mapped to fake IPs from `198.18.0.0/15`
- Connections to fake IPs are resolved to the original domain name via SOCKS5h
- This prevents DNS leaks (all DNS resolution happens server-side)
### CLI Interface
```bash
# Basic TUN mode (uses wraith's SOCKS5 on 127.0.0.1:1080)
sudo wraith-tun --socks5 127.0.0.1:1080
# With custom TUN address
sudo wraith-tun --socks5 127.0.0.1:1080 --tun-addr 10.0.0.1/24
# With DNS configuration
sudo wraith-tun --socks5 127.0.0.1:1080 --dns virtual
# Unprivileged mode (creates network namespace)
wraith-tun --socks5 127.0.0.1:1080 --unshare
```
### Unprivileged Mode
The `--unshare` flag creates a new network namespace, sets up the TUN device inside it, and maintains connectivity to the SOCKS5 proxy via the global namespace. This allows running without root, using only `CAP_NET_ADMIN` capability or namespace creation permissions.
## Scope
This is Phase 3 of the implementation plan. The core (`wraith serve`, `wraith connect` with SOCKS5 and port forwarding) comes first. TUN is an add-on.
### What `wraith-tun` Does NOT Do
- It does not manage SSH sessions
- It does not know about transports
- It does not handle authentication
- It does not read SSH keys
It only: reads packets from TUN, forwards to SOCKS5. That's it.
## Constraints
- Requires root or `CAP_NET_ADMIN` (or `--unshare` namespace isolation)
- IPv4 only in initial release, IPv6 follow-up
- UDP over TCP (DNS queries are handled via SOCKS5h, other UDP is dropped in initial release)
- Approximately 200-500 lines of Rust for the initial implementation
## Open Questions
- **OQ-03**: Windows TUN support scope (wintun.dll dependency)
- **OQ-09**: Whether to use tun2proxy's `ip-stack` crate for TCP reconstruction or implement a simpler packet-level approach
## Design Decisions
| ADR | Decision | Summary |
|-----|----------|---------|
| [002](decisions/002-tun-separate-process.md) | TUN separate process | Core never needs root, TUN is thin wrapper |
| [005](decisions/005-socks5-before-tun.md) | SOCKS5 first | TUN forwards to SOCKS5, not to SSH directly |