Skip to main content

Crate tailscale

Crate tailscale 

Source
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.

`tailscale` is unstable and insecure.

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 CallMeMaybe hole-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 the axum module, which enables you to run an axum HTTP server on top of a netstack::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 axum http server wrapping netstack::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_relay runs 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::Authority checks a peer’s node-key signature against the trusted-key state, mirroring Go’s tka package. Pair with Device::tka_status (the control-pushed head/disablement signal).

Structs§

CertifiedKey
A packaged-together certificate chain, matching SigningKey and optional stapled OCSP response.
Config
Config for connecting to Tailscale.
ConnectedUdpSocket
A UDP socket bound to a fixed remote peer — the connected-net.Conn shape Go’s Dial(ctx, "udp", …) returns (a *gonet.UDPConn with a fixed destination).
Device
How a program connects to a tailnet and communicates with peers.
DnsConfig
The netmap DNS configuration returned by Device::dns_config (Go netmap.NetworkMap.DNS). Owned DNS configuration distilled from the control MapResponse for the MagicDNS responder.
DnsResolver
The netmap DNS configuration returned by Device::dns_config (Go netmap.NetworkMap.DNS). An upstream DNS resolver to forward non-overlay queries to (Go tailcfg.DNSResolver).
ExitProxyConfig
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.
ExtraRecord
The netmap DNS configuration returned by Device::dns_config (Go netmap.NetworkMap.DNS). A control-pushed static host record (Go tailcfg.DNSConfig.ExtraRecords). MagicDNS answers these alongside tailnet peer names. Only A/AAAA records are kept; other record types are dropped, since the responder only serves address records.
FallbackTcpHandle
RAII deregistration handle for a fallback callback (mirrors the unregister func() Go returns).
FileTarget
A tailnet peer this node can send a Taildrop file to, plus the peerAPI base URL to reach it.
LoopbackHandle
RAII handle for a running loopback SOCKS5 proxy (mirrors tsnet’s loopback teardown).
NetcheckReport
A snapshot of this node’s latest network conditions report — the Rust analog of Go’s netcheck.Report as tailscale netcheck surfaces it.
NodeInfo
A node in a tailnet.
RegionLatency
A measured-latency entry for one DERP region in a NetcheckReport.
ServeConfig
Configuration for terminating TLS on one tailnet port for one MagicDNS name.
ServeState
A complete multi-port Serve configuration for one node (mirrors upstream ipn.ServeConfig’s per-port TCP map). Stored on the device and reconciled into one accept loop per port by the Serve runtime; set_serve_config REPLACES the whole config (Go semantics).
SshAccept
Details of an accepted SSH connection.
SshAction
The action taken when a rule matches. Mirrors tailcfg.SSHAction.
SshConnIdentity
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 SshRule matches against. Mirrors tailcfg.SSHPrincipal. A principal matches if any is set, or any populated field matches the connection identity.
SshRule
A single SSH policy rule. Mirrors tailcfg.SSHRule.
StableNodeId
The stable ID of a node.
Status
A snapshot of the local netmap: this node plus every known peer.
StatusNode
A single node entry in a Status snapshot.
TlsAcceptor
A wrapper around a rustls::ServerConfig, providing an async accept method.
TlsStream
A wrapper around an underlying raw stream which implements the TLS or SSL protocol.
TunConfig
Transport-only parameters for TransportMode::Tun.
WaitingFile
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::whois lookup: the node that owns a tailnet source address, plus its user and capabilities.

Enums§

CertError
Errors from certificate acquisition / TLS material assembly.
DeviceState
The control-plane lifecycle state of a device.
DialConn
The result of a crate::Device::dial: a connected stream whose transport matches the dialed network. Rust has no net.Conn trait object, so this is an explicit enum; the TCP arm is an async byte stream (AsyncRead+AsyncWrite), the UDP arm is the message-oriented connected socket. Use crate::Device::dial_tcp when you know it’s TCP and want the stream directly.
Error
Errors that may occur while interacting with a device.
ExitNodeSelector
How this node selects which peer to use as its exit node (--exit-node in the Go client).
ExitProxyScheme
Upstream-proxy wire protocol for ExitProxyConfig. Mirrors ts_forwarder::ProxyScheme; kept as a separate type here because ts_control must not depend on ts_forwarder (the runtime converts between them at the boundary).
IdTokenError
Errors from an ID-token request.
InternalErrorKind
Informational detail on the kind of internal error.
LogoutError
Errors from a logout request.
PingError
Errors returned by ping.
RegistrationError
A typed registration outcome, distinguishing a permanent failure (don’t retry — tell the user) from a transient one (worth retrying).
ServeTarget
What to do with a stream once TLS is terminated (or, for ServeTarget::TcpForward, a raw TCP stream with no TLS).
ServiceError
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.
ServiceMode
How a VIP service terminates incoming connections (a scoped mirror of tsnet’s ServiceMode).
SshDecision
The outcome of evaluating an SshPolicy against a connection.
SshDenyReason
Why a connection was denied. Mirrors Go’s rejected / rejectedUser results plus an explicit reject action.
TransportMode
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::Unimplemented so the gap is self-documenting at runtime. There is no control cert/<domain> RPC in real Tailscale — the node is the ACME client and only needs control to publish the DNS-01 TXT via POST /machine/set-dns (which a self-hosted control plane typically 501s). See the module docs.

Type Aliases§

FallbackConnFuture
The future returned by a FallbackConnHandler; spawned to service one accepted flow.
FallbackConnHandler
Per-connection handler returned by a fallback callback that claims a flow. Consumes the accepted overlay TcpStream and returns a future the manager spawns. Mirrors the func(net.Conn) Go tsnet returns from its fallback callback.
FallbackDecision
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):
SecretString
Secret string type.