I was on a plane, a long way from home, and wanted to check on the Raspberry Pi that does my backups. It's on my tailnet, I'd SSHed into it plenty of times, and the plane Wi-Fi was behaving. Easy.

ssh: connect to host 100.124.143.11 port 22: Operation timed out

That one was me. I'd typed the IP with the last digit missing, and tailscale status politely pointed out that no such node exists. The real address got me something more interesting:

$ ssh dusan@100.124.143.118
tailscale: tailnet policy does not permit you to SSH as user "dusan"
Connection closed by 100.124.143.118 port 22

The node was online. Tailscale SSH answered, which is how I got a sentence instead of a timeout. And it was telling me I'm not allowed to be dusan on a box where I am, as far as I'm concerned, dusan.

You and dusan aren't the same person

The error reads like an identity crisis, so it's worth untangling first. Tailscale SSH checks two different identities:

  • Who you are on the tailnet. For me that's dusandz@github, the account the Tailscale app on my Mac is logged in with.
  • Which account you want on the box. dusan, a plain Linux user on the Pi.

Every rule in the ssh section of the tailnet policy connects the two: this source identity may reach these devices as these local users. The error doesn't doubt that a dusan exists. It says no rule lets dusandz@github become dusan on that particular machine. Tailscale decides this before the Pi's own accounts, keys or sshd_config get a vote.

So the question was why the rule that had always let me in had stopped matching.

The tag took the device away from me

The default SSH rule in a fresh tailnet looks like this:

{
  "action": "check",
  "src": ["autogroup:member"],
  "dst": ["autogroup:self"],
  "users": ["autogroup:nonroot", "root"]
}

autogroup:self means devices owned by the user who's connecting. And a few weeks earlier I'd tagged the Pi tag:bastion.

A tagged device isn't owned by you, even if you're the one who set it up and tagged it. Tailscale moves ownership to the tag. In the machine list it stops showing your name and shows tagged-devices instead. From then on, autogroup:self doesn't include it, the default rule doesn't cover it, and no other rule covers it either.

Why had I tagged it? I wanted the Pi to work as an exit node and somehow decided that needed a tag. It doesn't. An exit node needs two things: the device advertising itself (tailscale set --advertise-exit-node) and an admin approving it in the machine's route settings. Tags don't come into it.

To make it worse, tag:bastion wasn't even a Pi tag. It belongs to a bastion host in a different project, so any rule written for that bastion now quietly applied to my backup box as well. My tailnet still runs the default allow-all network rule, so nothing leaked this time. In a tailnet with real network rules, borrowing a tag means borrowing everything it grants.

Why you can't remove the last tag

The obvious fix: remove the tag, give the device back to me, and let the default rule match again. Admin console, Machines, pi-backup, Edit ACL tags, remove tag:bastion. The Save button stays disabled and the dialog says:

You can't save your changes because the only way to remove all tags from a machine is to
reauthenticate it. The user who reauthenticates the machine will become its manager.

That's how it's meant to work. A user-owned device is tied to a user's login. A tagged one has no user. To give it an owner again, someone has to log in on the device itself, which ties it to that person's account. On the Pi that means something like:

sudo tailscale up --force-reauth

followed by opening the login link it prints. Run it over a Tailscale SSH session and you've just cut the connection you're running it from. And I couldn't run it at all, because the whole problem was that I couldn't get a shell on the Pi. The Pi was at home and I very much wasn't.

Swap the tag instead

The console won't take a machine down to zero tags. It's perfectly happy to change which tag it has. So instead of returning the Pi to me, I gave it a tag of its own and wrote the SSH rule for that tag.

First the policy. Under Access controls there's a JSON editor next to the visual one, and two additions do the job:

"tagOwners": {
  "tag:homelab": ["autogroup:admin"]
},
"ssh": [
  {
    "action": "accept",
    "src": ["dusandz@github"],
    "dst": ["tag:homelab"],
    "users": ["dusan", "autogroup:nonroot"]
  }
]

If your policy already has a tagOwners or ssh section, add to it rather than adding a second one. The visual editor has the same pieces: tags live under Definitions, and the SSH rule goes on the Tailscale SSH tab under Policies.

src has to be your tailnet identity exactly as the console shows it. Mine is dusandz@github because I log in with GitHub, and that's the address the rule has to name. My Gmail address would have looked right and matched nothing.

The tag must exist in the policy before you can assign it, so save the policy first. Then go back to Edit ACL tags on the machine, add tag:homelab, remove tag:bastion, and save. The machine has a tag at every point, so the console has nothing to object to.

No error this time, just a login. Done from a plane seat, no re-login, nobody at home asked to go and find a keyboard.

I set the rule to accept rather than check on purpose. Check mode sends you to a browser to log in again before the session starts, and over plane Wi-Fi that's one more thing to time out. Switch it back once you're on the ground if you want it.

Tag or no tag, long term

The swap leaves the Pi as a tagged device, which is a fine way to run a server. It doesn't belong to my user account, so it doesn't go away if I ever lose that account. Key expiry is off by default for tagged devices, so a headless box doesn't silently drop off the tailnet six months from now. And tag:homelab gives me one obvious place to write rules for every box at home.

If you'd rather have the machine back under your own name, the re-login is still the only way, and you can do it the next time you're near the box. When you do, turn on Disable key expiry for that machine in the console. Otherwise the user-owned device will need another login once its key expires.

Takeaway

Tagging a machine in Tailscale isn't a label. It transfers ownership, and every rule written against autogroup:self stops seeing the device. If you're locked out of a tagged box and can't get to it, don't try to remove the tag. Swap it for one you've written a rule for.


Locked out of your own tailnet from somewhere inconvenient? Tell me what's going on and I'll take a look.