Introducing tailnet lock: use Tailscale without trusting our infrastructure!

Tom D'Netto and Adrian Dewhurst on
Photo of Tom D'Netto
Photo of Adrian Dewhurst

Users sometimes ask us, “How can I trust Tailscale?” From the beginning, we’ve tried to make it so you don’t have to, by architecting our infrastructure with security and privacy in mind. When you use Tailscale, your data is end-to-end encrypted. Tailscale doesn’t have the private key, so we can’t see your traffic. While Tailscale can’t observe the data transiting your tailnet, we are responsible for managing the control plane, where our coordination server distributes public keys and settings for your tailnet.

Which brings us to one glaring issue that has remained with our architecture: You have still needed to trust our coordination server. What if we were malicious, and stealthily inserted new nodes into your network? Tailscale could hypothetically use a secretly-added node to send or receive traffic to your existing nodes — meaning it wouldn’t matter that the traffic is encrypted because the peer itself would be malicious.

You should decide who to trust when it comes to your tailnet’s coordination server and how nodes are added to your tailnet. We don’t want you to have to trust us to get it right. So today, we’re taking the first steps with tailnet lock, a security feature where your nodes verify the public keys distributed by the coordination server before trusting them for network connectivity.

Lock out untrusted nodes

When you install Tailscale and authenticate a device, a node keypair is automatically generated — that’s the static keypair used as part of the WireGuard® protocol. The private key stays on the device, and the public key is shared with the Tailscale coordination server. The coordination server then distributes that public key to other existing nodes in your tailnet the new device is allowed to connect to, based on ACLs.

Without tailnet lock, when a new node is added to the tailnet, its public key is distributed to peer nodes by the coordination server.

Without tailnet lock, when a new node is added to the tailnet, its public key is distributed to peer nodes by the coordination server.

With tailnet lock enabled, we add an extra step. Existing nodes still receive new nodes’ public keys from the coordination server, but don’t immediately trust them — the new node is locked out. New nodes added to your tailnet must be signed by a trusted tailnet lock key before they are allowed to connect to other nodes, and you decide which tailnet lock keys your tailnet’s existing nodes will trust. (In nerd-speak, we can say that your Tailscale nodes will only trust a node public key from Tailscale’s coordination server if it is endorsed by a cryptographic signature using tailnet-controlled tailnet lock keys.) The coordination server doesn’t generate, store, or see the tailnet lock key material, so even if the coordination server were compromised, it couldn’t sign a new node and have it participate in your tailnet.

With tailnet lock, when a new node is added to the tailnet, it is signed by a trusted tailnet lock key, then distributed to peer nodes, which can verify the signature before allowing connections.

With tailnet lock, when a new node is added to the tailnet, it is signed by a trusted tailnet lock key, then distributed to peer nodes, which can verify the signature before allowing connections.

As with any security technology, the devil is in the details. A crypto-scheme based on endorsing one set of key material (WireGuard node keys) with another (tailnet lock keys) introduces a new problem, as nodes now need some way to track which tailnet lock keys are trusted. So how do nodes decide which tailnet lock keys to trust?

The tailnet key authority tells your nodes which keys to trust

To determine which tailnet lock keys to trust, nodes implement a new subsystem, the tailnet key authority, to track and update the set of tailnet lock keys that can be used to sign node keys.

Before a new trusted tailnet lock key is added, it must be signed by an existing trusted tailnet lock key. The coordination server distributes the new key to all nodes in the tailnet, which can verify the signature before trusting it.

Before a new trusted tailnet lock key is added, it must be signed by an existing trusted tailnet lock key. The coordination server distributes the new key to all nodes in the tailnet, which can verify the signature before trusting it.

Tailnet lock keys are automatically generated by every node in your tailnet — but you select which ones to trust. (For now, these keys are stored on disk in each node, but in the future we hope to support more secure storage mediums, such as TPMs and FIDO2 keys.)

When tailnet lock is first enabled, you specify an initial set of tailnet lock keys to sign all node keys within the tailnet. This set of trusted tailnet lock keys is also distributed to the existing nodes. From that moment on, your tailnet is locked, and all new node keys must be endorsed using an existing tailnet lock key.

Changing the set of trusted tailnet lock keys to add or remove a key is done by encoding and signing the change with an existing tailnet lock key, which is then published across the tailnet.

Enable tailnet lock

To get started with tailnet lock, request access from the Feature Previews page of the admin console.

Update nodes in your tailnet to Tailscale v1.34 or later.

For the nodes whose tailnet lock keys you want to trust, get the public key on each node by running:

tailscale lock status

We recommend setting up tailnet lock with multiple trusted nodes.

Next, decide the number of disablement secrets you wish to use. Disablement secrets are used to disable tailnet lock. We recommend you use at least two, and store these securely, such as in a password manager or secret vault. You can optionally generate an extra disablement secret that is automatically passed to Tailscale support, so we can disable tailnet lock in case of an issue with how we’ve implemented it.

Then, on any node in your tailnet, enable tailnet lock, specifying the X trusted tailnet lock keys, and the desired number of disablement secrets N:

tailscale lock init --gen-disablements=N [--gen-disablement-for-support] tlpub:$trusted-tailnet-lock-key-1 … tlpub:$trusted-tailnet-lock-key-X

The disablement secrets are printed out, all existing nodes in the tailnet are signed by the trusted tailnet lock keys, and tailnet lock is enabled.

Learn more about how to set up tailnet lock for your tailnet in the documentation.

The A in Alpha is for Almost

We’re excited to share tailnet lock with the world, but like all other security features, it’s important to us to get it right.

We’d like to further improve the user experience of tailnet lock. Right now, all operations are done via the command line. And there are still some complications that our current implementation of tailnet lock doesn’t address, such as signing shared nodes. We have plans to address these issues but haven’t yet implemented them. As a result, we’re not making tailnet lock available to everyone just yet. If you’d like to test it out, request access for your tailnet from the Feature Previews page of the admin console. We’ll open it up to a few tailnets at a time, so that we can address any issues you hit or feedback you have.

We’re also publishing a more detailed white paper explaining how tailnet lock works, including cryptographic details on the key generation and signing used, the tailnet key authority, unlocking the tailnet, achieving consensus, and recovering from a tailnet lock key compromise. Interested parties can read up on the gory details in our tailnet lock white paper. Email us to share any feedback you have.

Share via

Subscribe for monthly updates

Product updates, blog posts, company news, and more.

Too much email? RSS Twitter