Tailscale SSH fails with "tailnet policy does not permit access"
Posted in Tailscale, Ssh, Networking, Linux
By Dušan Dželebdžić

Tailscale is one of those tools that, when it works, makes you wonder what you were doing with your life beforehand. You install it on two machines, log in on both, and the private network that used to take an afternoon of firewall rules and key juggling just appears. Servers are reachable. SSH stops being a chore. Infrastructure becomes boring, which is the highest compliment I can pay it.
Then one day I ran tailscale ssh against a box I'd connected a hundred times, and got this:
tailnet policy does not permit accessThe machine was online. Regular SSH still worked. tailscale ping came back clean. The node looked perfectly healthy from every angle I could think to check. And the error reads like a networking problem, so naturally I spent the first ten minutes debugging the network, which was fine the whole time.
Tailnet access and SSH access aren't the same thing
This is the bit that took me a minute to internalise. Being reachable on your tailnet and being allowed to tailscale ssh in are governed by two completely separate things. The first is your regular ACL connectivity. The second is a dedicated ssh section in your tailnet policy that nothing else touches.
So you can sit in a state where the box pings, MagicDNS resolves it, plain old ssh user@host over the tailnet works, and tailscale ssh user@host still slams the door. Everything points at the network because the network is the obvious suspect. The network is innocent.
The actual cause
In my case the policy simply had no SSH rules at all. Tailscale's default posture is to deny tailscale ssh unless a rule explicitly permits it, so an empty ssh section (or a missing one) means every attempt comes back as "tailnet policy does not permit access." It isn't refusing you because something's broken. It's refusing you because you never told it not to.
The fix is adding a rule to the ssh block in your tailnet policy:
{
"ssh": [
{
"action": "accept",
"src": ["autogroup:members"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot"]
}
]
}That one says: any member of the tailnet can SSH into devices they own, as any non-root user. Adjust src, dst, and users to taste, but the shape matters more than the specifics. No rule, no access.
You also have to enable SSH on the target
There's a second trap waiting even after the policy is right. The machine you're connecting to has to be running with Tailscale SSH switched on, and it isn't on by default. You turn it on with:
sudo tailscale up --sshYou can confirm the state with tailscale status. If SSH isn't enabled on the node itself, the policy can be flawless and you'll still get nowhere, because there's nothing on the other end listening for Tailscale to hand the connection to.
The root vs nonroot gotcha
This one's subtle enough that it deserves its own warning. Look again at that rule:
"users": ["autogroup:nonroot"]It does exactly what it says. If you've been testing with:
tailscale ssh root@serverit'll keep failing, and you'll keep staring at a policy that looks correct, because by that policy root genuinely isn't allowed. Most examples you'll copy off the internet exclude root on purpose, which is the sensible default. It's also the easiest thing in the world to forget while you're three coffees deep trying to work out why a "working" config won't let you in.
What I check now, in order
After getting bitten by this, I have a short mental checklist that catches it almost every time. Is the node online? Does tailscale ping reach it? Is SSH actually enabled on the target with --ssh? Does the tailnet policy contain an ssh section that covers this connection? And is the user I'm logging in as allowed by that rule, root included? The answer is usually sitting in step three or step five, and the network at the top of the list is almost never the culprit.
Takeaway
When tailscale ssh says the tailnet policy doesn't permit access, resist the urge to debug the network. Connectivity and SSH authorisation are separate systems in Tailscale, and this error lives entirely in the second one. Enable SSH on the target, make sure your policy has an ssh rule that matches both the device and the login user, and the door opens. If the message instead said "you're connected, but the SSH policy denies this", I'd have got there a lot faster. Maybe one day it will.
Wrestling with a tailnet ACL or some other piece of weird infrastructure? Tell me what's going on and I'll take a look.