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

Closed
opened 2025-12-29 02:27:53 +01:00 by adam · 5 comments
Owner

Originally created by @mitchplze on GitHub (Jun 9, 2025).

Is this a support request?

  • This is not a support request

Is there an existing issue for this?

  • 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 test@100.123.123.123
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

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

Debug information

N/A

Originally created by @mitchplze on GitHub (Jun 9, 2025). ### 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: ```yaml "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 test@100.123.123.123 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. ```yaml "ssh": [ { "action": "accept", "src": ["tag:admin", "group:admin"], "dst": ["*"], "users": ["autogroup:nonroot", "root", "test"] } ] ``` ### Expected Behavior As per [Tailscale docs](https://tailscale.com/kb/1337/policy-syntax#users), 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 ```markdown - 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
adam added the bugtailscale-feature-gappolicy 📝SSH labels 2025-12-29 02:27:53 +01:00
adam closed this issue 2025-12-29 02:27:54 +01:00
Author
Owner

@turnah commented on GitHub (Jun 10, 2025):

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 commented on GitHub (Jun 10, 2025): 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 @
Author
Owner

@mitchplze commented on GitHub (Jun 10, 2025):

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 commented on GitHub (Jun 10, 2025): 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.
Author
Owner

@nblock commented on GitHub (Jun 10, 2025):

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 commented on GitHub (Jun 10, 2025): > 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.
Author
Owner

@turnah commented on GitHub (Jun 10, 2025):

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 commented on GitHub (Jun 10, 2025): > > 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
Author
Owner

@nblock commented on GitHub (Jul 19, 2025):

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 commented on GitHub (Jul 19, 2025): 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` ```json5 "ssh": [ { ... "users": ["autogroup:nonroot"] } ] ``` <details> <summary>SSHPolicy from Tailscale SaaS</summary> ```json5 { "ruleExpires": "2025-07-20T06:05:17Z", "principals": [{...}], "sshUsers": { "*": "=", "root": "" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true, "allowRemotePortForwarding": true } } ``` </details> <details> <summary>SSHPolicy Headscale 0.26.1 (policy v2)</summary> ```json5 { "principals": [{...}], "sshUsers": { "autogroup:nonroot": "=" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true } } ``` </details> ### Allow tailscale ssh as any user ```json5 "ssh": [ { ... "users": ["autogroup:nonroot", "root"] } ] ``` <details> <summary>SSHPolicy from Tailscale SaaS</summary> ```json5 { "ruleExpires": "2025-07-20T06:05:17Z", "principals": [{...}], "sshUsers": { "*": "=", "root": "root" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true, "allowRemotePortForwarding": true } } ``` </details> <details> <summary>SSHPolicy Headscale 0.26.1 (policy v2)</summary> ```json5 { "principals": [{...}], "sshUsers": { "autogroup:nonroot": "=", "root": "=" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true } } ``` </details> ### Allow tailscale ssh as `ubuntu` or `root` ```json5 "ssh": [ { ... "users": ["ubuntu", "root"], } ] ``` <details> <summary>SSHPolicy from Tailscale SaaS</summary> ```json5 { "ruleExpires": "2025-07-20T06:05:17Z", "principals": [{...}], "sshUsers": { "root": "root", "ubuntu": "ubuntu" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true, "allowRemotePortForwarding": true } } ``` </details> <details> <summary>SSHPolicy Headscale 0.26.1 (policy v2)</summary> ```json5 { "principals": [{...}], "sshUsers": { "root": "=", "ubuntu": "=" }, "action": { "accept": true, "allowAgentForwarding": true, "allowLocalPortForwarding": true } } ``` </details> ### 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`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#1044