headscale icon indicating copy to clipboard operation
headscale copied to clipboard

[Bug] SSH autogroup:nonroot does not permit non-root usernames

Open mitchplze opened this issue 6 months ago • 4 comments

Is this a support request?

  • [x] This is not a support request

Is there an existing issue for this?

  • [x] I have searched the existing issues

Current Behavior

With the following block:

"ssh": [
    {
      "action": "accept",
      "src": ["tag:admin", "group:admin"],
      "dst": ["*"],
      "users": ["autogroup:nonroot", "root"]
    }
  ]

Despite --ssh being set and the user existing on the target system, admins are unable to SSH with the username test to the destination.

The error returned is:

mitch@Mitchs-MacBook ~ % ssh [email protected]
tailscale: failed to evaluate SSH policyConnection closed by 100.123.123.123 port 22

When I explicitly add "test" to users:, it works as expected.

"ssh": [
    {
      "action": "accept",
      "src": ["tag:admin", "group:admin"],
      "dst": ["*"],
      "users": ["autogroup:nonroot", "root", "test"]
    }
  ]

Expected Behavior

As per Tailscale docs, the autogroup:nonroot group in an SSH ACL policy, should permit any username that is not root to login to the machine. This does not currently appear to be the case with Headscale 0.26.1.

Steps To Reproduce

As per above, create an SSH block permitting autogroup:nonroot and users will not be able to login.

Environment

- OS: Debian 12 via Docker
- Headscale version: 0.26.1
- Tailscale version: 1.81.1

Runtime environment

  • [x] Headscale is behind a (reverse) proxy
  • [x] Headscale runs in a container

Debug information

N/A

mitchplze avatar Jun 09 '25 18:06 mitchplze

I don't think this is just affecting nonroot group, but same when explicitly defining usernames, ie "michael@". I also can't get my ssh group to connect after updating. Same message and my config hasn't changed (apart from @). Thought I was going mad or to do with changing my usernames so they contain an @

turnah avatar Jun 10 '25 05:06 turnah

There’s another bug If you didn’t know as well. Try restarting Headscale whenever you change ACLs or tags, or they won’t refresh.

mitchplze avatar Jun 10 '25 05:06 mitchplze

I don't think this is just affecting nonroot group, but same when explicitly defining usernames, ie "michael@".

The @ in the username is only required for Headscale users. The users list in the ssh section is just a list of unix usernames on the system you want to connect to with SSH.

nblock avatar Jun 10 '25 06:06 nblock

I don't think this is just affecting nonroot group, but same when explicitly defining usernames, ie "michael@".

The @ in the username is only required for Headscale users. The users list in the ssh section is just a list of unix usernames on the system you want to connect to with SSH.

Well this solves my problem! Thank you, sorry for cross posting on this issue

turnah avatar Jun 10 '25 07:06 turnah

I can confirm this on 0.26.1 and 0.25.1. It likely exists in many older versions as well.

The reason seems to be a difference in the netmap's sshUsers section found within SSHPolicy.rules[*].sshUsers. This can be observed by configuring a similar policy in Tailscale SaaS and Headscale and inspecting the netmap via e.g. tailscale debug netmap | jq .SSHPolicy.rules[0].

A few SSH policy examples with different allowed users follow. Note the differences in the sshUsers section.

Allow tailscale ssh as any local user except root

"ssh": [
  {
    ...
    "users": ["autogroup:nonroot"]
  }
]
SSHPolicy from Tailscale SaaS
{
  "ruleExpires": "2025-07-20T06:05:17Z",
  "principals": [{...}],
  "sshUsers": {
    "*": "=",
    "root": ""
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true,
    "allowRemotePortForwarding": true
  }
}
SSHPolicy Headscale 0.26.1 (policy v2)
{
  "principals": [{...}],
  "sshUsers": {
    "autogroup:nonroot": "="
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true
  }
}

Allow tailscale ssh as any user

"ssh": [
  {
    ...
    "users": ["autogroup:nonroot", "root"]
  }
]
SSHPolicy from Tailscale SaaS
{
  "ruleExpires": "2025-07-20T06:05:17Z",
  "principals": [{...}],
  "sshUsers": {
    "*": "=",
    "root": "root"
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true,
    "allowRemotePortForwarding": true
  }
}
SSHPolicy Headscale 0.26.1 (policy v2)
{
  "principals": [{...}],
  "sshUsers": {
    "autogroup:nonroot": "=",
    "root": "="
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true
  }
}

Allow tailscale ssh as ubuntu or root

"ssh": [
  {
    ...
    "users":  ["ubuntu", "root"],
  }
]
SSHPolicy from Tailscale SaaS
{
  "ruleExpires": "2025-07-20T06:05:17Z",
  "principals": [{...}],
  "sshUsers": {
    "root": "root",
    "ubuntu": "ubuntu"
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true,
    "allowRemotePortForwarding": true
  }
}
SSHPolicy Headscale 0.26.1 (policy v2)
{
  "principals": [{...}],
  "sshUsers": {
    "root": "=",
    "ubuntu": "="
  },
  "action": {
    "accept": true,
    "allowAgentForwarding": true,
    "allowLocalPortForwarding": true
  }
}

Summary

  • Headscale configures the username to be autogroup:nonroot while Tailscale SaaS uses a wildcard to match any username (except root)
  • The values also differ: = vs the literal username
  • Also note that Tailscale SaaS additionally configures allowRemotePortForwarding.

nblock avatar Jul 19 '25 19:07 nblock