Expand description
A work-in-progress Tailscale library.
tailscale allows Rust programs to connect to a tailnet and exchange traffic with peers over
TCP and UDP. It can communicate with other tailscale-based peers, tailscaled (the Tailscale
Go client), tsnet, and libtailscale via public DERP servers.
We welcome enthusiasm and interest, but please do not build production software using these libraries or rely on it for data privacy until we have a chance to batten down some hatches and complete a third-party audit.
See the Caveats section for more details.
For language bindings, see the following crates:
For instructions on how to run tests, lints, etc., see CONTRIBUTING.md. For the high-level architecture and repository layout, see ARCHITECTURE.md.
§Code Sample
A simple UDP client that periodically sends messages to a tailnet peer at 100.64.0.1:5678:
// Open a new connection to the tailnet
let dev = tailscale::Device::new(
&tailscale::Config::default_with_key_file("tsrs_keys.json").await?,
Some("YOUR_AUTH_KEY_HERE".to_owned()),
).await?;
// Bind a UDP socket on our tailnet IP, port 1234
let sock = dev.udp_bind((dev.ipv4_addr().await?, 1234).into()).await?;
// Send a packet containing "hello, world!" to 100.64.0.1:5678 once per second
loop {
sock.send_to((Ipv4Addr::new(100, 64, 0, 1), 5678).into(), b"hello, world!").await?;
tokio::time::sleep(Duration::from_secs(1)).await;
}Additional examples of using the tailscale crate can be found in the examples/ directory.
§Using tailscale
To use this crate or the language bindings, you will need to set the TS_RS_EXPERIMENT env var
to this_is_unstable_software. We’ll remove this requirement after a third-party code/cryptography
audit and any necessary fixes.
Under the hood, we use Tokio for our async runtime. You must also use Tokio, any kind and most
configurations of Tokio runtimes should work, but there must be one available when you call any
async API functions. The easiest way to do this is to use #[tokio::main], see the
Tokio docs for more information. In the future, we would like to limit
our reliance on Tokio so that there are alternatives for users of other async runtimes.
§Caveats
This software is still a work-in-progress! We are providing it in the open at this stage out of a belief in open-source and to see where the community runs with it, but please be aware of a few important considerations:
- This implementation contains unaudited cryptography and hasn’t undergone a comprehensive security analysis. Conservatively, assume there could be a critical security hole meaning anything you send or receive could be in the clear on the public Internet.
- There are no compatibility guarantees at the moment. This is early-days software - we may break dependent code in order to get things right.
- Direct peer-to-peer connections via NAT traversal are implemented (STUN-discovered endpoints
and Disco, with
CallMeMaybehole-punching over DERP), with DERP relays as the fallback when no direct path is available. Hard/symmetric NATs get the same single fixed-local-port candidate (EndpointSTUN4LocalPort) Go Tailscale uses; behind a NAT with no static port mapping a flow may still stay relayed through DERP, which caps its throughput. (Upstream Go does not do a “256-port birthday-paradox spray” — that is a common misconception; the single-candidate guess is the actual behavior, and this fork matches it.)
§Feature Flags
axum: enables theaxummodule, which enables you to run anaxumHTTP server on top of anetstack::TcpListener.
§Platform Support
tailscale currently supports the following platforms:
- Linux (x86_64 and ARM64)
- macOS (ARM64)
§Component crates
The following crates are part of the tailscale-rs project and are dependencies of this one. For many tasks, just this crate should be sufficient and these other crates are an implementation detail. There are other crates too, see ARCHITECTURE.md or the GitHub repo.
- ts_runtime: for each API-level
Device, the runtime uses an actor architecture to manage the lifecycle of the control client, data plane components, netstack, etc. A message bus passes updates and communications between these top-level actors. - ts_netcheck: checks network availability and reports latency to DERP servers in different regions.
- ts_netstack_smoltcp: a smoltcp-based network stack that processes Layer 3+ packets to/from the overlay network.
- ts_control: control plane client that handles registration, authorization/authentication, configuration, and streaming updates.
- ts_dataplane: wires all the individual data plane functions together, flowing inbound and outbound packets through the components in the correct order.
- ts_tunnel: a partial implementation of the WireGuard specification that protects all data plane traffic, and is interoperable with other WireGuard clients, including Tailscale clients.
- ts_cli_util: helpers for writing command line tools and initializing logging, used in examples.
- ts_disco_protocol: incomplete implementation of Tailscale’s discovery protocol (disco).
Modules§
- axum
- Support for the
axumhttp server wrappingnetstack::TcpListener. - config
- Types and utilities for configuring a Tailscale
Device. - geneve
- Geneve (RFC 8926) framing for Tailscale peer-relay traffic. A peer that advertises
NodeInfo::is_peer_relayruns a UDP relay server; relayed disco + WireGuard frames are Geneve-encapsulated with a VNI. This module exposes the header codec so the framing is recognizable. NOTE: the active relay data path (the relay-allocation handshake + magicsock integration) is not yet implemented in this fork — this is the wire-aware slice. - keys
- Tailscale cryptographic key types.
- netstack
- Command-channel-driven userspace network stack.
- ssh
- Support for tailnet-native, in-process SSH servers.
- tka
- Tailnet Lock (TKA) verification: the
tka::Authoritychecks a peer’s node-key signature against the trusted-key state, mirroring Go’stkapackage. Pair withDevice::tka_status(the control-pushed head/disablement signal).
Structs§
- Certified
Key - A packaged-together certificate chain, matching
SigningKeyand optional stapled OCSP response. - Config
- Config for connecting to Tailscale.
- Device
- How a program connects to a tailnet and communicates with peers.
- Exit
Proxy Config - Transport-only description of an upstream proxy that exit-node egress is routed through, so a cloud exit node egresses via the proxy’s (e.g. residential) IP rather than its own origin IP.
- Fallback
TcpHandle - RAII deregistration handle for a fallback callback (mirrors the
unregister func()Go returns). - Loopback
Handle - RAII handle for a running loopback SOCKS5 proxy (mirrors
tsnet’s loopback teardown). - Node
Info - A node in a tailnet.
- Serve
Config - Configuration for terminating TLS on one tailnet port for one MagicDNS name.
- Serve
State - A complete multi-port Serve configuration for one node (mirrors upstream
ipn.ServeConfig’s per-portTCPmap). Stored on the device and reconciled into one accept loop per port by the Serve runtime;set_serve_configREPLACES the whole config (Go semantics). - SshAccept
- Details of an accepted SSH connection.
- SshAction
- The action taken when a rule matches. Mirrors
tailcfg.SSHAction. Only the fields this fork acts on are retained; recording (Recorders/OnRecordingFailure) and the interactiveHoldAndDelegatecontrol round-trip are out of scope for basicListenSSHparity. - SshConn
Identity - The identity of an incoming SSH connection, resolved from the connecting peer.
- SshPolicy
- An owned Tailscale SSH policy. Mirrors
tailcfg.SSHPolicy. - SshPrincipal
- A principal an
SshRulematches against. Mirrorstailcfg.SSHPrincipal. A principal matches ifanyis set, or any populated field matches the connection identity. - SshRule
- A single SSH policy rule. Mirrors
tailcfg.SSHRule. - Stable
Node Id - The stable ID of a node.
- Status
- A snapshot of the local netmap: this node plus every known peer.
- Status
Node - A single node entry in a
Statussnapshot. - TlsAcceptor
- A wrapper around a
rustls::ServerConfig, providing an asyncacceptmethod. - TlsStream
- A wrapper around an underlying raw stream which implements the TLS or SSL protocol.
- TunConfig
- Transport-only parameters for
TransportMode::Tun. - Waiting
File - A waiting (fully-received) Taildrop file, as reported to the embedder. Mirrors Go
apitype.WaitingFile(default field-name JSON marshalling:Name,Size). - WhoIs
- The result of a
Runtime::whoislookup: the node that owns a tailnet source address, plus its user and capabilities.
Enums§
- Cert
Error - Errors from certificate acquisition / TLS material assembly.
- Device
State - The control-plane lifecycle state of a device.
- Error
- Errors that may occur while interacting with a device.
- Exit
Node Selector - How this node selects which peer to use as its exit node (
--exit-nodein the Go client). - Exit
Proxy Scheme - Upstream-proxy wire protocol for
ExitProxyConfig. Mirrorsts_forwarder::ProxyScheme; kept as a separate type here becausets_controlmust not depend onts_forwarder(the runtime converts between them at the boundary). - IdToken
Error - Errors from an ID-token request.
- Internal
Error Kind - Informational detail on the kind of internal error.
- Logout
Error - Errors from a logout request.
- Ping
Error - Errors returned by
ping. - Registration
Error - A typed registration outcome, distinguishing a permanent failure (don’t retry — tell the user) from a transient one (worth retrying).
- Serve
Target - What to do with a stream once TLS is terminated (or, for
ServeTarget::TcpForward, a raw TCP stream with no TLS). - Service
Error - Why a VIP-service listen request was refused. Fail-closed by construction: there is no variant that yields a usable listen address without a genuine control-assigned VIP on a tagged host.
- Service
Mode - How a VIP service terminates incoming connections (a scoped mirror of tsnet’s
ServiceMode). - SshDecision
- The outcome of evaluating an
SshPolicyagainst a connection. - SshDeny
Reason - Why a connection was denied. Mirrors Go’s
rejected/rejectedUserresults plus an explicit reject action. - Transport
Mode - How the node’s application overlay data path is realized.
Constants§
- MISSING_
CERT_ RPC - Names exactly what this fork is missing to issue a real cert, surfaced
verbatim in
CertError::Unimplementedso the gap is self-documenting at runtime. There is no controlcert/<domain>RPC in real Tailscale — the node is the ACME client and only needs control to publish the DNS-01 TXT viaPOST /machine/set-dns(which a self-hosted control plane typically 501s). See the module docs.
Type Aliases§
- Fallback
Conn Future - The future returned by a
FallbackConnHandler; spawned to service one accepted flow. - Fallback
Conn Handler - Per-connection handler returned by a fallback callback that claims a flow. Consumes the
accepted overlay
TcpStreamand returns a future the manager spawns. Mirrors thefunc(net.Conn)Gotsnetreturns from its fallback callback. - Fallback
Decision - A fallback callback’s decision for one
(src, dst)flow: an optional per-connection handler and whether this callback intercepts the flow. Matches Go’s(handler func(net.Conn), intercept bool):