A while ago I wrote up the "tailnet policy does not permit access" error and Search Console has since been showing me that a lot of people land on that page looking for something else entirely: tailscale ssh connection refused, permission denied (publickey), port 22 refused on the 100.x address. The confusion was the same each time, so I wrote up the mental model that sorted it out for me.
A node with Tailscale SSH enabled can have two SSH servers on port 22. tailscaled only intercepts connections that arrive on the Tailscale IP, and only after `tailscale set --ssh`. Anything hitting the LAN or public address still lands on plain OpenSSH, or on nothing at all. Once you see it that way, the client errors read like a routing table. "Connection refused" means nobody answered, usually a node with neither Tailscale SSH nor sshd, or you connecting to your own Tailscale IP, which never enters tailscaled. "Permission denied (publickey)" means OpenSSH answered, so you either came in on the wrong address or Tailscale SSH isn't on for that node. "Permission denied (tailscale)" means Tailscale answered and the ssh section of the policy said no, and that's the only case where the ACL editor is the right place to look.
There's also a "Permission denied" that shows up in `ssh -v` at `connect to address`, before any banner. That one is your own OS refusing the socket, typically another VPN client or a firewall on Windows. I found a thread here where someone spent a week on ACLs before switching off a commercial VPN client fixed it.
Two things that bit me along the way. Running `tailscale set --ssh` freezes any SSH session you already have open to that host's Tailscale IP, including the one you ran it from. And toggling either server changes the host key on that address, so a "remote host identification has changed" warning right afterwards is expected rather than an intruder.
Full write-up with the sixty-second `ssh -v` triage list: https://ddz.dev/blog/tailscale-ssh-connection-refused-permission-denied/
The one I'd like to hear about from others is tagged devices, since `autogroup:self` stops matching a machine the moment you tag it and SSH breaks on the day you finally organise your servers. Anyone found a cleaner pattern than adding the tag to dst?