Skip to main content

Module serve

Module serve 

Source
Expand description

Stored Serve config + accept-loop runtime (tsnet’s Get/SetServeConfig + serving runtime). Stored Serve config + accept-loop runtime (tsnet’s Get/SetServeConfig + serving runtime).

Go tsnet stores an ipn.ServeConfig on the node and runs one accept loop per configured tailnet port, dispatching each accepted connection per its handler (proxy / text / raw TCP forward / hand-back). This module is the faithful equivalent on the application netstack: a ServeManager owns the current ServeState, one accept-loop task per bound port, and tears every loop down on drop / on the next set.

§Storage + reconcile (full-replace)

The manager holds the current ServeState plus one tokio::task::AbortHandle per bound port behind a single Arc<Mutex<Inner>> (mirroring crate::fallback_tcp::FallbackTcpManager). ServeManager::set uses full-replace semantics: it aborts every existing accept loop and respawns from the new config. Go reconciles incrementally (leaving unchanged ports running); we do full-replace because it is simpler and correct, and a SetServeConfig is a rare control-plane operation, not a hot path. The passed ServeState becomes the whole config (REPLACE, matching Go). pure_reconcile computes the add/remove port deltas for testing and documentation, even though the live path replaces wholesale.

§TLS termination

TLS-terminating ports (ServeTarget::terminates_tls) need a TlsAcceptor; the caller (Device::set_serve_config) obtains it once via the cert path and hands it in per port. The manager never builds an acceptor and never touches the cert/ACME machinery — that keeps ts_runtime off the cert path and lets the device fail the whole set closed if a cert cannot be issued (no plaintext downgrade).

§Anti-leak

Every accept loop binds the overlay netstack only (via Channel::tcp_listen on the device’s own tailnet IPv4) — never a host socket. The ServeTarget::Proxy / ServeTarget::TcpForward backend dial is a local host socket to the embedder’s own backend (exactly like Go’s reverse-proxy to 127.0.0.1 and like crate::Runtime’s loopback proxy) — it is intentionally NOT routed through the ts_forwarder exit-egress path, so the exit-node anti-leak chokepoint is untouched. A backend dial failure drops the connection (fail-closed, logged); it never falls back to anything.

Structs§

ResolvedPort
A fully-resolved per-port serve plan: the target plus, for TLS-terminating targets, the acceptor the device built up-front from the cert path. The caller guarantees acceptor.is_some() exactly when target.terminates_tls() — the manager asserts this is never violated by failing the bind.
ServeAccepted
A connection handed back to the embedder for a ServeTarget::Accept port (the in-process stand-in for Go tsnet’s ListenTLS-returned net.Listener).
ServeManager
Owns the stored Serve config and the live per-port accept loops (tsnet serving runtime).

Traits§

AsyncReadWrite
Object-safe alias for the boxed accepted stream: an AsyncRead + AsyncWrite the embedder drives.

Type Aliases§

ServeAcceptedReceiver
Receiver side of the ServeTarget::Accept hand-back channel (mirrors a net.Listener’s accept queue). ServeManager::set returns one; await recv to take the next accepted, TLS-terminated connection. Dropped/replaced when the next set runs.