Tagged devices should not have access permissions of their owning user #481

Closed
opened 2025-12-29 02:18:47 +01:00 by adam · 18 comments
Owner

Originally created by @vbrandl on GitHub (Apr 21, 2023).

Bug description

I want to allow my personal devices to ssh into my servers but not allow my servers to ssh between each other. All devices belong to the same headscale user. My servers are tagged ssh, my personal devices are untagged and my user is in the sshuser group.

The Tailscale documentation states (https://tailscale.com/kb/1068/acl-tags/#authentication-and-authorization):

Once a device has been tagged, it loses the access permissions of the human user who tagged it, and acquires any access permissions granted to its tags. In other words, if you log into a device as dave@tailscale.com and then tag it with tag:server, the device no longer has any of the network permissions granted to dave@tailscale.com, and instead is subject to the access rules for tag:server. If the user who added the device is deleted, the device will remain.

According to the Tailscale documentation, I would expect a ACL allowing ssh from group:sshuser to tag:ssh to produce the described behaviour. All my untagged devices should be able to ssh into the tagged servers (which they do) but my servers are also able to ssh between each other.

To Reproduce

{
    "groups":{
        "group:sshuser":[
            "me"
        ]
    },
    "tagOwners": {
        "tag:ssh": ["me"]
    },
    "ssh": [
        {
            "action": "accept",
            "src": ["group:sshuser"],
            "dst": ["tag:ssh"],
            "users": ["allowlisted-user"]
        }
    ]
}

Context info

  • Headscale: v0.22.1
Originally created by @vbrandl on GitHub (Apr 21, 2023). **Bug description** I want to allow my personal devices to ssh into my servers but not allow my servers to ssh between each other. All devices belong to the same headscale user. My servers are tagged `ssh`, my personal devices are untagged and my user is in the `sshuser` group. The Tailscale documentation states (https://tailscale.com/kb/1068/acl-tags/#authentication-and-authorization): > Once a device has been tagged, it loses the access permissions of the human user who tagged it, and acquires any access permissions granted to its tags. In other words, if you log into a device as dave@tailscale.com and then tag it with tag:server, the device no longer has any of the network permissions granted to dave@tailscale.com, and instead is subject to the access rules for tag:server. If the user who added the device is deleted, the device will remain. According to the Tailscale documentation, I would expect a ACL allowing ssh from `group:sshuser` to `tag:ssh` to produce the described behaviour. All my untagged devices should be able to ssh into the tagged servers (which they do) but my servers are also able to ssh between each other. **To Reproduce** ```json { "groups":{ "group:sshuser":[ "me" ] }, "tagOwners": { "tag:ssh": ["me"] }, "ssh": [ { "action": "accept", "src": ["group:sshuser"], "dst": ["tag:ssh"], "users": ["allowlisted-user"] } ] } ``` **Context info** * Headscale: v0.22.1
adam added the bugno-stale-botpolicy 📝tags labels 2025-12-29 02:18:47 +01:00
adam closed this issue 2025-12-29 02:18:48 +01:00
Author
Owner

@github-actions[bot] commented on GitHub (Oct 18, 2023):

This issue is stale because it has been open for 180 days with no activity.

@github-actions[bot] commented on GitHub (Oct 18, 2023): This issue is stale because it has been open for 180 days with no activity.
Author
Owner

@vbrandl commented on GitHub (Oct 29, 2023):

This seems like a security bug to me, since it does not match the behavior of tailscale. Can anyone with more in-depth knowledge comment on this?

@vbrandl commented on GitHub (Oct 29, 2023): This seems like a security bug to me, since it does not match the behavior of tailscale. Can anyone with more in-depth knowledge comment on this?
Author
Owner

@github-actions[bot] commented on GitHub (Jan 28, 2024):

This issue is stale because it has been open for 90 days with no activity.

@github-actions[bot] commented on GitHub (Jan 28, 2024): This issue is stale because it has been open for 90 days with no activity.
Author
Owner

@kradalby commented on GitHub (Jan 30, 2024):

Yes, you are right, they should be "detached" from the user when they are tagged. Right now we dont have the code necessary to handle this. I have removed the stale tag for further tracking.

Per now I would classify it as "ACLs are not fully implemented" rather than a security bug as we do not support all features.

@kradalby commented on GitHub (Jan 30, 2024): Yes, you are right, they should be "detached" from the user when they are tagged. Right now we dont have the code necessary to handle this. I have removed the stale tag for further tracking. Per now I would classify it as "ACLs are not fully implemented" rather than a security bug as we do not support all features.
Author
Owner

@vbrandl commented on GitHub (Jan 30, 2024):

Since there are various places in the headscale documentation, that link to https://tailscale.com/kb/1068/acl-tags, where the "detaching" behavior is described, I would still consider this a security bug. As a user reading the documentation, it is not clear, where and how headscale diverges from tailscale. This seems dangerous to me...

@vbrandl commented on GitHub (Jan 30, 2024): Since there are various places in the headscale documentation, that link to https://tailscale.com/kb/1068/acl-tags, where the "detaching" behavior is described, I would still consider this a security bug. As a user reading the documentation, it is not clear, where and how headscale diverges from tailscale. This seems dangerous to me...
Author
Owner

@github-actions[bot] commented on GitHub (May 16, 2024):

This issue is stale because it has been open for 90 days with no activity.

@github-actions[bot] commented on GitHub (May 16, 2024): This issue is stale because it has been open for 90 days with no activity.
Author
Owner

@vbrandl commented on GitHub (May 16, 2024):

I don't think this should be marked as stale and forgotten...

@vbrandl commented on GitHub (May 16, 2024): I don't think this should be marked as stale and forgotten...
Author
Owner

@kradalby commented on GitHub (May 16, 2024):

I agree, removed the mark, we just have not had capacity to get to it.

@kradalby commented on GitHub (May 16, 2024): I agree, removed the mark, we just have not had capacity to get to it.
Author
Owner

@kradalby commented on GitHub (Jun 20, 2024):

I made a PR doing some of the untangeling work to make this possible, removing the username from magicdns names. Which should make this a tiny bit easier.

@kradalby commented on GitHub (Jun 20, 2024): I made a PR doing some of the untangeling work to make this possible, removing the username from magicdns names. Which should make this a tiny bit easier.
Author
Owner

@mikelococo commented on GitHub (Jul 23, 2024):

... removing the username from magicdns names.

Are there any suggested strategies for maintaining hostname continuity in the face of this change? This seems like a more-significant-than-usual breaking change in that it's likely to require changes not just to the headscale config but to potentially all services and clients on the tailnet that know each other by their MagicDNS name.

I see in https://github.com/juanfont/headscale/releases/tag/v0.23.0-beta1 that it suggests a temporary workaround, but one that is planned to be removed:

use_username_in_magic_dns can be used to turn this behaviour on again, but note that this option will be removed when tags are fixed.

Does extra_records provide a path to maintain the existing MagicDNS entries? https://github.com/juanfont/headscale/blob/main/docs/dns-records.md discusses the config-format but is silent on the relationship between extra_records, base_domain, and auto-assigned MagicDNS entries. Is using extra_records to emulate the old username-style subdomains valid?

If one only cares about dns name continuity for the nodes registered to a single user, is modifying the base_domain of an existing tailscale instance a valid migration strategy? For example, chaging the base_domain from example.com to myuser.example.com such that all nodes now appear at the FQDN previously reserved for nodes registered to myuser.

I feel like this change in particular is likely to generate migration/support questions due to the very disruptive nature of changing existing names, some amount of migration guidance would be quite helpful as the release nears.

@mikelococo commented on GitHub (Jul 23, 2024): > ... removing the username from magicdns names. Are there any suggested strategies for maintaining hostname continuity in the face of this change? This seems like a more-significant-than-usual breaking change in that it's likely to require changes not just to the headscale config but to potentially all services and clients on the tailnet that know each other by their MagicDNS name. I see in https://github.com/juanfont/headscale/releases/tag/v0.23.0-beta1 that it suggests a temporary workaround, but one that is planned to be removed: > use_username_in_magic_dns can be used to turn this behaviour on again, but note that this option will be removed when tags are fixed. Does `extra_records` provide a path to maintain the existing MagicDNS entries? https://github.com/juanfont/headscale/blob/main/docs/dns-records.md discusses the config-format but is silent on the relationship between `extra_records`, `base_domain`, and auto-assigned MagicDNS entries. Is using `extra_records` to emulate the old username-style subdomains valid? If one only cares about dns name continuity for the nodes registered to a single user, is modifying the `base_domain` of an existing tailscale instance a valid migration strategy? For example, chaging the `base_domain` from `example.com` to `myuser.example.com` such that all nodes now appear at the FQDN previously reserved for nodes registered to `myuser`. I feel like this change in particular is likely to generate migration/support questions due to the very disruptive nature of changing existing names, some amount of migration guidance would be quite helpful as the release nears.
Author
Owner

@nblock commented on GitHub (Sep 10, 2024):

Does extra_records provide a path to maintain the existing MagicDNS entries? https://github.com/juanfont/headscale/blob/main/docs/dns-records.md discusses the config-format but is silent on the relationship between extra_records, base_domain, and auto-assigned MagicDNS entries. Is using extra_records to emulate the old username-style subdomains valid?

I tested the extra_records approach you mentioned and it works:

Setup

  • laptop assigned to user alice: 100.64.0.2, fd7a:115c:a1e0::2
  • server assigned to user bob: 100.64.0.3, fd7a:115c:a1e0::3
  • Tailscale 1.72.1 on Debian 12, MagicDNS enabled
  • Headscale 0.23.0-beta.4

Results

Testcase: use_username_in_magic_dns=true

Headscale configuration:

dns:
  use_username_in_magic_dns: true
  base_domain: tn.example.com
  extra_records:
    - {name: "extra.example.com", type: "A", value: "198.51.100.1"}
    - {name: "extra.example.com", type: "AAAA", value: "2001:db8::1"}

Content of: /etc/resolv.conf on server:

nameserver 100.100.100.100
search tn.example.com bob.tn.example.com

Lookup results (via: dig +short @100.100.100.100 NAME A/AAAA)

Name IPv4 IPv6
server.tn.example.com - -
server.bob.tn.example.com 100.64.0.3 -
laptop.tn.example.com - -
laptop.alice.tn.example.com 100.64.0.2 -
extra.example.com 198.51.100.1 2001:db8::1

Testcase: use_username_in_magic_dns=false and extra records for nodes

Headscale configuration:

dns:
  use_username_in_magic_dns: false
  base_domain: tn.example.com
  extra_records:
    - {name: "extra.example.com", type: "A", value: "198.51.100.1"}
    - {name: "extra.example.com", type: "AAAA", value: "2001:db8::1"}
    - {name: "laptop.alice.tn.example.com", type: "A", value: "100.64.0.2"}
    - {name: "laptop.alice.tn.example.com", type: "AAAA", value: "fd7a:115c:a1e0::2"}
    - {name: "server.bob.tn.example.com", type: "A", value: "100.64.0.3"}
    - {name: "server.bob.tn.example.com", type: "AAAA", value: "fd7a:115c:a1e0::3"}

Content of: /etc/resolv.conf on server:

nameserver 100.100.100.100
search tn.example.com

Lookup results (via: dig +short @100.100.100.100 NAME A/AAAA)

Name IPv4 IPv6
server.tn.example.com 100.64.0.3 -
server.bob.tn.example.com 100.64.0.3 fd7a:115c:a1e0::3
laptop.tn.example.com 100.64.0.2 -
laptop.alice.tn.example.com 100.64.0.2 fd7a:115c:a1e0::2
extra.example.com 198.51.100.1 2001:db8::1

@kradalby do you consider the above approach as recommended, long-term "workaround" once dns.use_username_in_magic_dns is completely removed? If so, I'm going to follow-up with a PR for the DNS docs where this approach is documented for users.

@nblock commented on GitHub (Sep 10, 2024): > Does `extra_records` provide a path to maintain the existing MagicDNS entries? https://github.com/juanfont/headscale/blob/main/docs/dns-records.md discusses the config-format but is silent on the relationship between `extra_records`, `base_domain`, and auto-assigned MagicDNS entries. Is using `extra_records` to emulate the old username-style subdomains valid? I tested the `extra_records` approach you mentioned and it works: ### Setup * `laptop` assigned to user `alice`: 100.64.0.2, fd7a:115c:a1e0::2 * `server` assigned to user `bob`: 100.64.0.3, fd7a:115c:a1e0::3 * Tailscale 1.72.1 on Debian 12, MagicDNS enabled * Headscale 0.23.0-beta.4 ### Results #### Testcase: use_username_in_magic_dns=true Headscale configuration: ```yaml dns: use_username_in_magic_dns: true base_domain: tn.example.com extra_records: - {name: "extra.example.com", type: "A", value: "198.51.100.1"} - {name: "extra.example.com", type: "AAAA", value: "2001:db8::1"} ``` Content of: `/etc/resolv.conf` on `server`: ``` nameserver 100.100.100.100 search tn.example.com bob.tn.example.com ``` Lookup results (via: `dig +short @100.100.100.100 NAME A/AAAA`) | Name | IPv4 | IPv6 | --------------------------- | ------------ | ---- | server.tn.example.com | - | - | server.bob.tn.example.com | 100.64.0.3 | - | laptop.tn.example.com | - | - | laptop.alice.tn.example.com | 100.64.0.2 | - | extra.example.com | 198.51.100.1 | 2001:db8::1 #### Testcase: use_username_in_magic_dns=false and extra records for nodes Headscale configuration: ```yaml dns: use_username_in_magic_dns: false base_domain: tn.example.com extra_records: - {name: "extra.example.com", type: "A", value: "198.51.100.1"} - {name: "extra.example.com", type: "AAAA", value: "2001:db8::1"} - {name: "laptop.alice.tn.example.com", type: "A", value: "100.64.0.2"} - {name: "laptop.alice.tn.example.com", type: "AAAA", value: "fd7a:115c:a1e0::2"} - {name: "server.bob.tn.example.com", type: "A", value: "100.64.0.3"} - {name: "server.bob.tn.example.com", type: "AAAA", value: "fd7a:115c:a1e0::3"} ``` Content of: `/etc/resolv.conf` on `server`: ``` nameserver 100.100.100.100 search tn.example.com ``` Lookup results (via: `dig +short @100.100.100.100 NAME A/AAAA`) | Name | IPv4 | IPv6 | --------------------------- | ------------ | ---- | server.tn.example.com | 100.64.0.3 | - | server.bob.tn.example.com | 100.64.0.3 | fd7a:115c:a1e0::3 | laptop.tn.example.com | 100.64.0.2 | - | laptop.alice.tn.example.com | 100.64.0.2 | fd7a:115c:a1e0::2 | extra.example.com | 198.51.100.1 | 2001:db8::1 --- @kradalby do you consider the above approach as recommended, long-term "workaround" once `dns.use_username_in_magic_dns` is completely removed? If so, I'm going to follow-up with a PR for the DNS docs where this approach is documented for users.
Author
Owner

@kradalby commented on GitHub (Sep 11, 2024):

@kradalby do you consider the above approach as recommended, long-term "workaround" once dns.use_username_in_magic_dns is completely removed? If so, I'm going to follow-up with a PR for the DNS docs where this approach is documented for users.

I suppose it is a fair workaround, I would recommend not relying on the username based dns names as it will be hard to keep up to date for individuals, but if they are up for it, I suppose it doesnt hurt to call out that this is possible.

@kradalby commented on GitHub (Sep 11, 2024): > @kradalby do you consider the above approach as recommended, long-term "workaround" once dns.use_username_in_magic_dns is completely removed? If so, I'm going to follow-up with a PR for the DNS docs where this approach is documented for users. I suppose it is a fair workaround, I would recommend not relying on the username based dns names as it will be hard to keep up to date for individuals, but if they are up for it, I suppose it doesnt hurt to call out that this is possible.
Author
Owner

@almereyda commented on GitHub (Sep 12, 2024):

Just as a side-note, the IPv6 address used in the example is the same for both nodes: fd7a:115c:a1e0::2

server.bob is probably fd7a:115c:a1e0::3 instead.

@almereyda commented on GitHub (Sep 12, 2024): Just as a side-note, the IPv6 address used in the example is the same for both nodes: `fd7a:115c:a1e0::2` `server.bob` is probably `fd7a:115c:a1e0::3` instead.
Author
Owner

@nblock commented on GitHub (Sep 12, 2024):

server.bob is probably fd7a:115c:a1e0::3 instead.

Fixed, thx!

@nblock commented on GitHub (Sep 12, 2024): > `server.bob` is probably `fd7a:115c:a1e0::3` instead. Fixed, thx!
Author
Owner

@klausmark commented on GitHub (Mar 1, 2025):

Could a possible workaround be creating a system user and transferring all tagged nodes to this user?

@klausmark commented on GitHub (Mar 1, 2025): Could a possible workaround be creating a system user and transferring all tagged nodes to this user?
Author
Owner

@kradalby commented on GitHub (May 7, 2025):

While tags has not been focused on in this release, it might have been touched up and I would be grateful to hear feedback if the current beta changed anything for this issue.

@kradalby commented on GitHub (May 7, 2025): While tags has not been focused on in this release, it might have been touched up and I would be grateful to hear feedback if the current beta changed anything for this issue.
Author
Owner

@almereyda commented on GitHub (Sep 23, 2025):

The overlapping issue #2325 has a little update on this from May 16 https://github.com/juanfont/headscale/issues/2325#issuecomment-2886586434, which might be of interest to people not subscribed there:

We will make it so when you create a pre auth key it is either attached to a user (if no tags are given) or a tag. When a device is tagged, it will not be associated with a user.

Implementation has begun in:

@almereyda commented on GitHub (Sep 23, 2025): The overlapping issue #2325 has a little update on this from May 16 https://github.com/juanfont/headscale/issues/2325#issuecomment-2886586434, which might be of interest to people not subscribed there: > We will make it so when you create a pre auth key it is _either_ attached to a user (if no tags are given) or a tag. When a device is tagged, it will not be associated with a user. Implementation has begun in: - #2619
Author
Owner

@kradalby commented on GitHub (Dec 11, 2025):

Changes to separate the tags from users has been merged into main in #2885 and #2931. I will encourage you to help testing this if you are able to build main and run it.

I will close this to track progress, but there might still be bugs and the likes related to this change. As part of hardening this feature, we are tracking all related tags bugs over time in v0.28.0 milestone.

@kradalby commented on GitHub (Dec 11, 2025): Changes to separate the tags from users has been merged into `main` in #2885 and #2931. I will encourage you to help testing this if you are able to build `main` and run it. I will close this to track progress, but there might still be bugs and the likes related to this change. As part of hardening this feature, we are tracking all related tags bugs over time in [v0.28.0 milestone](https://github.com/juanfont/headscale/milestone/13).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#481