geiserx_tailscale 0.55.2

A work-in-progress pure-Rust Tailscale implementation (fork of tailscale/tailscale-rs)
Documentation
# AGENTS.md

- This project is written in Rust.
- Follow the guidelines in CONTRIBUTING.md.
- Use GitHub markdown for docs, summaries of your work, etc.
- Commits must be signed off by the user in the form `Signed-off-by: "COMMITTER" <COMMITTER_EMAIL>`.
- When submitting a PR or filing an issue, include the text `Created using AGENT_NAME` where AGENT_NAME is your name.

## Proxy egress for exit nodes (capability beyond strict tsnet parity)

This fork carries one capability Go `tsnet` does not: an exit node can egress the traffic it
forwards through an **upstream proxy** instead of out its own origin IP. An exit node (e.g. on a
cloud VPS) can route a peer's internet-bound traffic through a **residential proxy** — the cloud
host's real IP never appears upstream.

- **Provider:** a residential proxy (configured by the deployer).
- **Where it lives:** the `RealDialer` trait in `ts_forwarder` is the single anti-leak
  chokepoint. `ProxyExitDialer` implements SOCKS5 (RFC 1928/1929) and HTTP `CONNECT`
  hand-rolled with **zero new dependencies** (keeps the `ring`-only, musl-clean egress path
  intact — never pull in `aws-lc-rs`/`openssl`/native-TLS on this path).
- **Config chain:** `tailscale::Config::exit_proxy` (`ExitProxyConfig` / `ExitProxyScheme`) →
  `ts_control::Config` (transport-only; `ts_control` never reads it and must not depend on
  `ts_forwarder`) → `ForwarderConfig::from_control_config` (converted to
  `ts_forwarder::ProxyConfig` at the `ts_runtime` boundary) → `forwarder_actor::dialer_choice`.
- **Fail-closed is sacred.** The proxy dialer is only selected when `forward_exit_egress` is
  set AND an `exit_proxy` is configured. Any proxy connect/handshake failure **drops the flow**
  — it never falls back to a direct host-IP dial. UDP over proxy fails closed. An SSRF guard
  rejects forbidden exit destinations (loopback / link-local / unspecified). Proxy credentials
  are redacted from `Debug`. The default `DirectDialer` structurally refuses exit egress, so the
  real origin IP can never leak by accident.
- **Deployer note — the SSRF guard is scoped to `ExitNode` flows by design.** The
  forbidden-destination check (`exit_dst_is_forbidden`) gates only the `0.0.0.0/0` exit class; a
  `Subnet` flow (a destination covered by a *narrower* advertised route) skips it, because a subnet
  route legitimately targets a private range — that private range **is** the routed subnet. This is
  **not** an attacker-controllable SSRF: the flow class is derived from the operator's **own
  `advertised_routes`**, never from the peer. But it does mean that **if an operator advertises a
  sensitive internal range as a forwarded subnet route, the forwarder will dial into it.** Do not
  advertise ranges you don't intend peers to reach — in particular never advertise the host's
  link-local (`169.254.0.0/16`), the tailnet CGNAT range (`100.64.0.0/10`), or loopback as a
  forwardable subnet. (Advertising `0.0.0.0/0` as an exit node is the normal case and stays behind
  the SSRF guard.)

## Exit-node memory: `tcp_buffer_size` at scale

The userspace netstack has **no TCP window auto-tuning**, so `Config::tcp_buffer_size` (default
**256 KiB per direction**, raised from 16 KiB in v0.5.3 to stop a single flow being throttled to
~1.6 Mbps at 80 ms RTT) is allocated **eagerly per socket** — one rx buffer and one tx buffer, so
~512 KiB per TCP socket. The **forwarder** netstack opens one socket per forwarded exit/subnet
flow, so concurrent-flow count multiplies this directly: a host carrying ~1,000 simultaneous
forwarded flows pins ~512 MB in TCP buffers alone — a real fraction of a small cloud VPS exit
node. On a small box that forwards many concurrent flows, set
`Config::tcp_buffer_size` lower (e.g. `Some(64 * 1024)`) and accept the per-flow throughput cap as
the trade. `None` keeps the throughput-optimized 256 KiB default. Both the application and
forwarder netstacks share this one value (see `ts_runtime::netstack_config_from`).