Context

I use Tailscale to secure network communication, and I wanted to authenticate users coming from the Tailscale network.

The goal was to reproduce the behavior in the golink application. When users come from the Tailscale network, the application should authenticate them and give them access to the application based on roles defined in Tailscale ACL.

Solution

Tailscale exposes an HTTP API through a Unix Socket named LocalAPI.

It can be used for many things. In my case, I needed to authenticate a user coming from Tailnet. I used it to get information about a Tailscale user by calling the whois endpoint.

How to use it

Example with curl:

curl \
    --unix-socket /run/tailscale/tailscaled.sock \
    'http://local-tailscaled.sock/localapi/v0/whois?addr=remoteAddress[:remotePort]'

Update: the remotePort is not mandatory but can be useful to get information when using Tailscale in userspace mode. Thank you Brad Fitzpatrick for the clarification in Bluesky.

JSON returned (truncated):

{
	"Node": {
		"ID": 1234567890,
		"StableID": "foo",
		"Name": "computer.tailnet-name.ts.net.",
		"User": 12345,
		"ComputedName": "computer",
		"ComputedNameWithHost": "computer (localhost)"
	},
	"UserProfile": {
		"ID": 12345,
		"LoginName": "john.doe@company.tld",
		"DisplayName": "DOE John",
	},
	"CapMap": {
		"company.tld/cap/app": [
			{
				"role": [
					"foo",
					"bar"
				]
			}
		]
	}
}

We have enough information to authenticate the user, and we can even map some roles in Tailscale ACL thanks to the Grants section.

This API is equivalent to the tailscale whois command, but it can be used from any language without running the tailscale binary.

Documentation

Unfortunately, LocalAPI is not documented yet. A Go SDK exists to interact with it: Tailscale LocalClient. When using another language we have to check API calls in the source code to reproduce them:

Disclaimer

Use with caution, we have no official guarantee that this API will not change in the future due to the lack of documentation.