No support for "autogroup:" in ACL rules #280

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

Originally created by @nicka101 on GitHub (Jun 20, 2022).

Bug description

The 2 recent beta releases for 0.16.0 reference the tailscale ACL documentation here, however headscale doesn't support the autogroup:members and autogroup:self functionality referenced there. I would guess it additionally doesn't support the autogroup:internet or autogroup:shared functionality, but I havent tested those

To Reproduce

Attempt to use similar rules to those in the example policy file in the tailscale's ACL documentation or use the recommended initial ACL from tailscale's ACL samples
Observe that headscale constantly logs:
WRN No IPs found with the alias autogroup:members
WRN No IPs found with the alias autogroup:self
And also that with the recommended initial ACL, no devices can see or access each other

Context info

  • headscale 0.16.0-beta4
  • tailscale 1.26.0 (but not relevant)
Originally created by @nicka101 on GitHub (Jun 20, 2022). <!-- Headscale is a multinational community across the globe. Our common language is English. Please consider raising the bug report in this language. --> **Bug description** The 2 recent beta releases for 0.16.0 reference the tailscale ACL documentation [here](https://tailscale.com/kb/1018/acls/), however headscale doesn't support the autogroup:members and autogroup:self functionality referenced there. I would guess it additionally doesn't support the autogroup:internet or autogroup:shared functionality, but I havent tested those **To Reproduce** Attempt to use similar rules to those in the example policy file in the [tailscale's ACL documentation](https://tailscale.com/kb/1018/acls/#tailscale-policy-syntax) or use the recommended initial ACL from [tailscale's ACL samples](https://tailscale.com/kb/1192/acl-samples/#remote-access-to-corp-devices-recommended-initial-acl) Observe that headscale constantly logs: `WRN No IPs found with the alias autogroup:members` `WRN No IPs found with the alias autogroup:self` And also that with the recommended initial ACL, no devices can see or access each other **Context info** - headscale 0.16.0-beta4 - tailscale 1.26.0 (but not relevant)
adam added the bugno-stale-botpolicy 📝 labels 2025-12-29 01:25:53 +01:00
adam closed this issue 2025-12-29 01:25:53 +01:00
Author
Owner

@Patralos commented on GitHub (Jun 25, 2022):

Support for autogroup:internet would be great +1

@Patralos commented on GitHub (Jun 25, 2022): Support for `autogroup:internet` would be great +1
Author
Owner

@mirkosemler commented on GitHub (Jun 27, 2022):

As i am currently also testing this:
The ACL System on Tailscale is for commercial reasons centered around payable named user nodes to manage Tags and Group-Memberships including binding non-named users with autogroup:members to the least privileged group, maybe Users.

for correct headcale acls see:
https://github.com/juanfont/headscale/blob/main/docs/acls.md
for tailscale see:
https://tailscale.com/kb/1018/acls/

given:
autogroup:members # unique namespaces/users
autogroup:internet # unique nodes who export route 0/0
autogroup:self # user self reference
autogroup:shared # foreign user

my result:
things could be done with a serverside toolset to resolve:

  • members: all users (domestic) except foreign domain (see oidc integration, i propose keycloak), can be resolved by grouping
  • shared: all users except foreign (non domestic) users go into separate access management; oposite of above, normal grouping
  • internet: all nodes who export 0/0; maybe tag them as internet for rule to exclude groups from routing? how can routing and direct access be managed via tag? portwise access + consuption of route.
  • self: replace with username?

autogroup:internet expands to 1 rule per node who provides the route 0/0
autogroup:self expands to 1 rule per user having himself in src and dest.
therefore they are not easy to derive from headscale shell commands.

go serverside autogroup. lets not have this feature with bash scaping and cron.

@mirkosemler commented on GitHub (Jun 27, 2022): As i am currently also testing this: The ACL System on Tailscale is for commercial reasons centered around payable named user nodes to manage Tags and Group-Memberships including binding non-named users with autogroup:members to the least privileged group, maybe Users. for correct headcale acls see: https://github.com/juanfont/headscale/blob/main/docs/acls.md for tailscale see: https://tailscale.com/kb/1018/acls/ given: autogroup:members # unique namespaces/users autogroup:internet # unique nodes who export route 0/0 autogroup:self # user self reference autogroup:shared # foreign user my result: things could be done with a serverside toolset to resolve: - members: all users (domestic) except foreign domain (see oidc integration, i propose keycloak), can be resolved by grouping - shared: all users except foreign (non domestic) users go into separate access management; oposite of above, normal grouping - internet: all nodes who export 0/0; maybe tag them as internet for rule to exclude groups from routing? how can routing and direct access be managed via tag? portwise access + consuption of route. - self: replace with username? autogroup:internet expands to 1 rule per node who provides the route 0/0 autogroup:self expands to 1 rule per user having himself in src and dest. therefore they are not easy to derive from headscale shell commands. go serverside autogroup. lets not have this feature with bash scaping and cron.
Author
Owner

@restanrm commented on GitHub (Aug 4, 2022):

For autogroup:members and autogroup:shared I think that this have currently no possible meaning in headscale. Since we cannot federate headscale instances. When/if we do this we would implement this feature.

For autogroup:self it has some sense that should even simplify the documentation since we added each user their own name to access their devices. For this feature, it doesn't seem as easy as adding the username but close to it.

For autogroup:internet some hacks has been presented on discord based on https://www.procustodibus.com/blog/2021/03/wireguard-allowedips-calculator/. Should we build a list of excluded network that cannot be a private network or routed through headscale in this section ? For IPv4 I can propose the following list of disallowed networks:

100.64.0.0/10,192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,224.0.0.0/4

For IPv6, the list of disallowed networks could be :

::1/128,fc00::/7,fe80::/10

These 2 lists have been built based on https://en.wikipedia.org/wiki/Reserved_IP_addresses

Is this the best way to handle outgoing traffic ?

@kradalby, @juanfont any thoughts on this ?

@restanrm commented on GitHub (Aug 4, 2022): For `autogroup:members` and `autogroup:shared` I think that this have currently no possible meaning in headscale. Since we cannot federate headscale instances. When/if we do this we would implement this feature. For `autogroup:self` it has some sense that should even simplify the documentation since we added each user their own name to access their devices. For this feature, it doesn't seem as easy as adding the username but close to it. For `autogroup:internet` some hacks has been presented on discord based on https://www.procustodibus.com/blog/2021/03/wireguard-allowedips-calculator/. Should we build a list of excluded network that cannot be a private network or routed through headscale in this section ? For IPv4 I can propose the following list of disallowed networks: ``` 100.64.0.0/10,192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.0/8,169.254.0.0/16,224.0.0.0/4 ``` For IPv6, the list of disallowed networks could be : ``` ::1/128,fc00::/7,fe80::/10 ``` These 2 lists have been built based on https://en.wikipedia.org/wiki/Reserved_IP_addresses Is this the best way to handle outgoing traffic ? @kradalby, @juanfont any thoughts on this ?
Author
Owner

@mirkosemler commented on GitHub (Aug 9, 2022):

I disagree on the federation. the oidc section has allowed domains, allowed users and a domain hint. if we have a list of member domains the oidc system (i.e. keycloak ) cloud provide access for a defined list of joinable domains and headscale could distinguish them according to the list of member domains, the rest would default to being shared access.

the user federation on keycloak implies the consumption of ldap or AD, besides also providing the ability to derive login grants from multiple identity providers to one source, in my case headscale. have skipped the part where i connect my google account, because its already implemented in the adroid client, so i know it works, but it strongly seems to me that headscale does not care about multiple origins providing oidc scopes (address, email, phone, profile, ...) it accepts the 4 default values defined in config.yml.

keyclaok should be able to do the federation.

if headscale is able to provide autogroup:shared and autogroup:member by definition of a member domain list for use with full username@domain.tld list we approach feature parity with tailscale ACLs by having a more powerful (less text) definition of access rules. headscale already provides a great feature set for identity aware distributed firewalling towards zero trust by deriving its namespaces from oidc. distinguishing the domains in the namespaces by shared/member list is a premium feature and it should be fairly easy.

please consider.

@mirkosemler commented on GitHub (Aug 9, 2022): I disagree on the federation. the oidc section has allowed domains, allowed users and a domain hint. if we have a list of member domains the oidc system (i.e. keycloak ) cloud provide access for a defined list of joinable domains and headscale could distinguish them according to the list of member domains, the rest would default to being shared access. the user federation on keycloak implies the consumption of ldap or AD, besides also providing the ability to derive login grants from multiple identity providers to one source, in my case headscale. have skipped the part where i connect my google account, because its already implemented in the adroid client, so i know it works, but it strongly seems to me that headscale does not care about multiple origins providing oidc scopes (address, email, phone, profile, ...) it accepts the 4 default values defined in config.yml. keyclaok should be able to do the federation. if headscale is able to provide autogroup:shared and autogroup:member by definition of a member domain list for use with full username@domain.tld list we approach feature parity with tailscale ACLs by having a more powerful (less text) definition of access rules. headscale already provides a great feature set for identity aware distributed firewalling towards zero trust by deriving its namespaces from oidc. distinguishing the domains in the namespaces by shared/member list is a premium feature and it should be fairly easy. please consider.
Author
Owner

@restanrm commented on GitHub (Aug 11, 2022):

I guess that we have different views on federation. For me it means merging 2 different instances of headscale. I have this definition because currently an headscale network is the equivalent of a tailnet. You cannot have 2 different sets of ACL's on a single headscale instance.

Although I'm not ok with the definition of the federation and do not fully understand what you mean because I don't know keycloak (and oidc) well, I can see something interesting. If I understand what you mean (@mirkosemler) the allowed_domains would not refuse users from other domains in this kind of configuration but just handle them as shared users ?

Improving on this we could also use the OIDC groups to be mapped in the ACL's in order to reduce the “text definition” and have dynamic configuration base on the authentication provider. This is something that I would like very much, it's quite painful to edit the ACL's each time we want to add (or remove) someone to a team.

@restanrm commented on GitHub (Aug 11, 2022): I guess that we have different views on federation. For me it means merging 2 different instances of headscale. I have this definition because currently an headscale network is the equivalent of a tailnet. You cannot have 2 different sets of ACL's on a single headscale instance. Although I'm not ok with the definition of the federation and do not fully understand what you mean because I don't know keycloak (and oidc) well, I can see something interesting. If I understand what you mean (@mirkosemler) the `allowed_domains` would not refuse users from other domains in this kind of configuration but just handle them as shared users ? Improving on this we could also use the OIDC groups to be mapped in the ACL's in order to reduce the “text definition” and have dynamic configuration base on the authentication provider. This is something that I would like very much, it's quite painful to edit the ACL's each time we want to add (or remove) someone to a team.
Author
Owner

@TakahashiAmaki commented on GitHub (Sep 9, 2022):

currently, we cloud not use api to update acl rules online, so i thought autogroup:members/autogroup:self for each new namespace could see their own devices is necessary

@TakahashiAmaki commented on GitHub (Sep 9, 2022): currently, we cloud not use api to update acl rules online, so i thought `autogroup:members`/`autogroup:self` for each new namespace could see their own devices is necessary
Author
Owner

@linsomniac commented on GitHub (Feb 26, 2023):

What I'm looking for is "src: group:devs; dst: autogroup:self", where in this case "autogroup:self" means "the same user (used to be namespace) as the src". Meaning: Users in group "devs" are connecting to their own boxes. IOW, If have have alice, bob, and charlie in devs, the rule would match src=alice;dst=alice, src=bob;dst=bob, etc...

@linsomniac commented on GitHub (Feb 26, 2023): What I'm looking for is "src: group:devs; dst: autogroup:self", where in this case "autogroup:self" means "the same user (used to be namespace) as the src". Meaning: Users in group "devs" are connecting to their own boxes. IOW, If have have alice, bob, and charlie in devs, the rule would match src=alice;dst=alice, src=bob;dst=bob, etc...
Author
Owner

@linsomniac commented on GitHub (Feb 26, 2023):

TL;DR: I propose a definition of "autogroup:self" and ask for review of proposed solution. @kradalby and @juanfont are likely reviewers.

I've taken a look at the code, and I think it's becoming a little clearer for me.

Aside: After some research I believe that "autogroup:members" is the equivalent of a group with all users in it, but with all tagged nodes removed. I'm not dealing with that here however. BUT, I do believe it means that headscale needs to not consider tagged nodes a member of any group. I believe headscale does this as well.

First, we need to define what "autogroup:self:ports" means in the dst. I'm going to propose, after a shockingly large amount of time thinking it over, that it follows these rules:

  1. For every element "X" in "src"...
  2. If "X" is a group, it is expanded to every user in the group and injected into the iteration in 1 above.
  3. A new ACL is created: src: X; dst: X:ports (with the same proto/action as the current rule.

Yes, this could lead to some nonsensical rules, in particular if a host is listed in the source. Note: It doesn't look like subnets can be listed in the source. That seems reasonable.

A reasonable question is: Does this match tailscale's definition? I have no idea, I can't find it well documented. Do we have to match their implementation? Not necessarily. It's hard to match it when I can't find it documented. We could take a stab at it and fix it when discrepancies are found, though that would break some users. We could call it something else ("autogroup:reflect"? "autogroup:reverseuno"?). The only examples I've seen are of it being used with "autogroup:members", so I have no idea if it's usable with anything else.

I believe, to implement "autogroup:self", this is what needs to happen:

  1. In acls.go, move the "srcIPs :=" block (line 165) down below the "destPorts :=" block (down to after current line 201).
  2. In generateACLPolicyDest() return an additional value of "selfPorts" which is set to the "ports" part of "autogroup:self:ports" if an autogroup:self is found in the destination list.
  3. While iterating acl.Sources, if "selfPorts" above is set (indicating autogroup:self):
  4. -- If "src" is a group, iterate over the users that are a member of it.
  5. -- Append to "rules" a rule with SrcIPs the generateACLPolicySrc expansion of this source, and DstPorts this same expansion but with "ports" added to it.

To clarify:

  1. The final rules append would still happen, we would just be injecting some individual rules as well.
  2. 7 above would either be operating on each of the users in the group specified by "src", or "src" itself if it is not a group.
@linsomniac commented on GitHub (Feb 26, 2023): TL;DR: I propose a definition of "autogroup:self" and ask for review of proposed solution. @kradalby and @juanfont are likely reviewers. I've taken a look at the code, and I think it's becoming a little clearer for me. Aside: After some research I believe that "autogroup:members" is the equivalent of a group with all users in it, but with all tagged nodes removed. I'm not dealing with that here however. BUT, I do believe it means that headscale needs to not consider tagged nodes a member of any group. I believe headscale does this as well. First, we need to define what "autogroup:self:ports" means in the dst. I'm going to propose, after a shockingly large amount of time thinking it over, that it follows these rules: 1. For every element "X" in "src"... 2. If "X" is a group, it is expanded to every user in the group and injected into the iteration in 1 above. 3. A new ACL is created: src: X; dst: X:ports (with the same proto/action as the current rule. Yes, this could lead to some nonsensical rules, in particular if a host is listed in the source. Note: It doesn't look like subnets can be listed in the source. That seems reasonable. A reasonable question is: Does this match tailscale's definition? I have no idea, I can't find it well documented. Do we have to match their implementation? Not necessarily. It's hard to match it when I can't find it documented. We could take a stab at it and fix it when discrepancies are found, though that would break some users. We could call it something else ("autogroup:reflect"? "autogroup:reverseuno"?). The only examples I've seen are of it being used with "autogroup:members", so I have no idea if it's usable with anything else. I believe, to implement "autogroup:self", this is what needs to happen: 1. In acls.go, move the "srcIPs :=" block (line 165) down below the "destPorts :=" block (down to after current line 201). 2. In generateACLPolicyDest() return an additional value of "selfPorts" which is set to the "ports" part of "autogroup:self:ports" if an autogroup:self is found in the destination list. 5. While iterating acl.Sources, if "selfPorts" above is set (indicating autogroup:self): 6. -- If "src" is a group, iterate over the users that are a member of it. 7. -- Append to "rules" a rule with SrcIPs the generateACLPolicySrc expansion of this source, and DstPorts this same expansion but with "ports" added to it. To clarify: 1. The final rules append would still happen, we would just be injecting some individual rules as well. 2. 7 above would either be operating on each of the users in the group specified by "src", or "src" itself if it is not a group.
Author
Owner

@anuragbhatia commented on GitHub (May 23, 2023):

autogroup:internet will be good to have. I don't see a way to "deny" traffic. Tried both action "action": "reject" and "action": "deny" but syntax does not support it.

The problem this creates is similar to use case described here: I want to give access to exit nodes for internet to some users without exposing internal IPs.

In regular firewall I will do something like:

rule1: deny traffic to RFC1918 private IPs for tag: family
rule2: allow traffic to 0.0.0.0/0, ::/0 to tag:family

but since rule1 is not possible, I am using following as workaround:

    // Family can access internet via exit nodes but not private IPs
    {
      "action": "accept",
      "src": ["group:family"],
      "dst": [ "0.0.0.0/8:*","2.0.0.0/8:*","3.0.0.0/8:*","4.0.0.0/6:*","8.0.0.0/7:*","11.0.0.0/8:*", 
                "12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/2:*","128.0.0.0/3:*",
                "160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*",
                "172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*",
                "192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*",
                "193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*" ]      
    },

  // Family can access home server on 80/443
    {
      "action": "accept",
      "src": ["group:family"],
      "dst": ["172.16.16.4/32:80","172.16.16.4/32:443"],
      "proto": "tcp"
    }

The list of IPs is certainly long and ugly. Plus don't see it supporting IPv6 in this syntax.

@anuragbhatia commented on GitHub (May 23, 2023): autogroup:internet will be good to have. I don't see a way to "deny" traffic. Tried both action "action": "reject" and "action": "deny" but syntax does not support it. The problem this creates is similar to use case described here: I want to give access to exit nodes for internet to some users without exposing internal IPs. In regular firewall I will do something like: rule1: deny traffic to RFC1918 private IPs for tag: family rule2: allow traffic to 0.0.0.0/0, ::/0 to tag:family but since rule1 is not possible, I am using following as workaround: ``` // Family can access internet via exit nodes but not private IPs { "action": "accept", "src": ["group:family"], "dst": [ "0.0.0.0/8:*","2.0.0.0/8:*","3.0.0.0/8:*","4.0.0.0/6:*","8.0.0.0/7:*","11.0.0.0/8:*", "12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/2:*","128.0.0.0/3:*", "160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*", "172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*", "192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*", "193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*" ] }, // Family can access home server on 80/443 { "action": "accept", "src": ["group:family"], "dst": ["172.16.16.4/32:80","172.16.16.4/32:443"], "proto": "tcp" } ``` The list of IPs is certainly long and ugly. Plus don't see it supporting IPv6 in this syntax.
Author
Owner

@RUzOfuz5m commented on GitHub (May 26, 2023):

@anuragbhatia Thank you for that!

Your dest has "64.0.0.0/2:*" which includes 100.64.X.X. This gave access to all my nodes as I kept the default range (100.64.0.0). I changed mine to exclude this range and that fixed the problem.

    {
      "action": "accept",
      "src": ["group:client"],
      "dst": [
              "group:exit:0",       // Allow to go through exit nodes without accessing any ports on the hosts 
              "0.0.0.0/5:*","8.0.0.0/7:*","11.0.0.0/8:*","12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/3:*","96.0.0.0/6:*","100.0.0.0/10:*","100.128.0.0/9:*","101.0.0.0/8:*","102.0.0.0/7:*","104.0.0.0/5:*","112.0.0.0/5:*","120.0.0.0/6:*","124.0.0.0/7:*","126.0.0.0/8:*","128.0.0.0/3:*","160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*","172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*","192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*","193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*",
             ]
    },

I used the following commands to exclude some popular internal ranges.

netmask -c 0.0.0.0:9.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#10.0.0.0/8
netmask -c 11.0.0.0:100.63.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#100.64.0.0/10
netmask -c 100.128.0.0:126.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#127.0.0.0/8
netmask -c 128.0.0.0:172.15.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#172.16.0.0/12
netmask -c 172.32.0.0:192.167.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#192.168.0.0/16 
netmask -c 192.169.0.0:223.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
cat  ./acl
@RUzOfuz5m commented on GitHub (May 26, 2023): @anuragbhatia Thank you for that! Your dest has "64.0.0.0/2:*" which includes 100.64.X.X. This gave access to all my nodes as I kept the default range (100.64.0.0). I changed mine to exclude this range and that fixed the problem. ``` { "action": "accept", "src": ["group:client"], "dst": [ "group:exit:0", // Allow to go through exit nodes without accessing any ports on the hosts "0.0.0.0/5:*","8.0.0.0/7:*","11.0.0.0/8:*","12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/3:*","96.0.0.0/6:*","100.0.0.0/10:*","100.128.0.0/9:*","101.0.0.0/8:*","102.0.0.0/7:*","104.0.0.0/5:*","112.0.0.0/5:*","120.0.0.0/6:*","124.0.0.0/7:*","126.0.0.0/8:*","128.0.0.0/3:*","160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*","172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*","192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*","193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*", ] }, ``` I used the following commands to exclude some popular internal ranges. ``` netmask -c 0.0.0.0:9.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl #10.0.0.0/8 netmask -c 11.0.0.0:100.63.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl #100.64.0.0/10 netmask -c 100.128.0.0:126.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl #127.0.0.0/8 netmask -c 128.0.0.0:172.15.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl #172.16.0.0/12 netmask -c 172.32.0.0:192.167.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl #192.168.0.0/16 netmask -c 192.169.0.0:223.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl cat ./acl ```
Author
Owner

@julianq commented on GitHub (Jun 30, 2023):

@anuragbhatia @RUzOfuz5m

Amazing. GOAT status for both of you.

@julianq commented on GitHub (Jun 30, 2023): @anuragbhatia @RUzOfuz5m Amazing. GOAT status for both of you.
Author
Owner

@wernerhoffman commented on GitHub (Jul 8, 2023):

Hey, thanks for the effort!
Could anyone please explain to me how those netmask commands are used?
I know this is not a support forum, but discord won't allow me an account.

As I am understanding their output can be used in place of autogroup:internet (which is quite handy because this is only supported by tailscale, not headscale).
Because this is crucial to my network's functionality I would like to be sure I understand those commands correctly, so why do you use the commands you use?

How to I instruct netmask (1) to generate all ranges except for some (RFC1918) IPs?

@wernerhoffman commented on GitHub (Jul 8, 2023): Hey, thanks for the effort! Could anyone please explain to me how those `netmask` commands are used? I know this is not a support forum, but discord won't allow me an account. As I am understanding their output can be used in place of `autogroup:internet` (which is quite handy because this is only supported by tailscale, not headscale). Because this is crucial to my network's functionality I would like to be sure I understand those commands correctly, so why do you use the commands you use? How to I instruct `netmask (1)` to generate all ranges _except for some_ (RFC1918) IPs?
Author
Owner

@wernerhoffman commented on GitHub (Jul 9, 2023):

Ah, I understood the structure of the command. Here an explanation for all those that come after me:

Starting with 0.0.0.0 you give netmask a range until the network you want to exclude starts. In this case we give netmask the range 0.0.0.0:9.255.255.255 because 9.255.255.255 is the last IP coming before being in 10.0.0.0/8 which is the first network we want to exclude.
The first IP after 10.0.0.0/8 network is 11.0.0.0. This is why the next range starts with that.
From there it's the same procedure for each network we want to exclude.

From 223.255.255.255 forward all IPs are reserved.

@wernerhoffman commented on GitHub (Jul 9, 2023): Ah, I understood the structure of the command. Here an explanation for all those that come after me: Starting with `0.0.0.0` you give `netmask` a range until the network you want to exclude starts. In this case we give `netmask` the range `0.0.0.0:9.255.255.255` because 9.255.255.255 is the last IP coming before being in `10.0.0.0/8` which is the first network we want to exclude. The first IP after `10.0.0.0/8` network is `11.0.0.0`. This is why the next range starts with that. From there it's the same procedure for each network we want to exclude. From 223.255.255.255 forward [all IPs are reserved](https://en.wikipedia.org/wiki/Reserved_IP_addresses).
Author
Owner

@github-actions[bot] commented on GitHub (Dec 21, 2023):

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

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

@comminutus commented on GitHub (Dec 27, 2023):

how's this coming?

@comminutus commented on GitHub (Dec 27, 2023): how's this coming?
Author
Owner

@almereyda commented on GitHub (Mar 27, 2024):

Additionally to the autogroup:* items used in acls, we also find an autogroup:nonroot in ssh, which does not seem to be respected in v0.23.0-alpha5, despite it is used in acls-test.go.

The current workaround to specify the accepted user names manually.

@almereyda commented on GitHub (Mar 27, 2024): Additionally to the `autogroup:*` items used in `acls`, we also find an `autogroup:nonroot` in `ssh`, which does not seem to be respected in v0.23.0-alpha5, despite it is used in `acls-test.go`. The current workaround to specify the accepted user names manually.
Author
Owner

@Hypnotist1148 commented on GitHub (Jun 10, 2024):

Any plans to have autogroups:self and autogroup:member implemented?
Would be useful to define a rule like this { "action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self:*"] } to have each user automatically have access to their own clients.

Edit: Here the source of the snipped I've provided: https://tailscale.com/kb/1192/acl-samples#starter-plan-acl

@Hypnotist1148 commented on GitHub (Jun 10, 2024): Any plans to have `autogroups:self` and `autogroup:member` implemented? Would be useful to define a rule like this `{ "action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self:*"] }` to have each user automatically have access to their own clients. Edit: Here the source of the snipped I've provided: https://tailscale.com/kb/1192/acl-samples#starter-plan-acl
Author
Owner

@aradng commented on GitHub (Aug 17, 2024):

jst fyi, autogroup:nonroot is still not supported in 0.23.0-beta1

@aradng commented on GitHub (Aug 17, 2024): jst fyi, autogroup:nonroot is still not supported in 0.23.0-beta1
Author
Owner

@vinhjaxt commented on GitHub (Sep 8, 2024):

I added acl autogroup self and autogroup member into headscale.

https://github.com/juanfont/headscale/compare/main...vinhjaxt:headscale:main

My manual test just works

@vinhjaxt commented on GitHub (Sep 8, 2024): I added acl autogroup self and autogroup member into headscale. https://github.com/juanfont/headscale/compare/main...vinhjaxt:headscale:main My manual test just works
Author
Owner

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

@vinhjaxt could you submit a PR for it please? 🙏

@Snuupy commented on GitHub (Sep 12, 2024): @vinhjaxt could you submit a PR for it please? 🙏
Author
Owner

@Haarolean commented on GitHub (Oct 16, 2024):

@vinhjaxt would you please submit a PR to the upstream repo with the patch you mentioned? I bet many of us would benefit from it without a need to build a custom image ourselves :-)

@Haarolean commented on GitHub (Oct 16, 2024): @vinhjaxt would you please submit a PR to the upstream repo with the patch you mentioned? I bet many of us would benefit from it without a need to build a custom image ourselves :-)
Author
Owner

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

While I have not looked too closely at @vinhjaxt code, we are positive to accepting it, but it will require rigorous testing.

@kradalby commented on GitHub (Oct 16, 2024): While I have not looked too closely at @vinhjaxt code, we are positive to accepting it, but it will require rigorous testing.
Author
Owner

@shokeen12 commented on GitHub (Feb 4, 2025):

I'm encountering two issues while using the autogroup patch by @vinhjaxt

  1. Exit Node Visibility Issue
    When isolating each user with the following ACL, it works correctly in restricting access. However, exit nodes belonging to other users are still displayed, even though they are not accessible.
    Expected behavior: Each user's exit node should only be visible to their own devices and not to other users.
  2. ACL File Parsing Issue
    When using a file instead of the API for ACL rules, the system fails to parse the ACL policy correctly.
    The policy I am using:

{
"acls": [
{ "action": "accept", "src": ["autogroup:self"], "dst": ["autogroup:self:"] },
{ "action": "accept", "src": ["autogroup:self"], "dst": ["autogroup:internet:
"] }
],
"autoApprovers": {
"exitNode": ["autogroup:self"]
}
}

@shokeen12 commented on GitHub (Feb 4, 2025): I'm encountering two issues while using the autogroup patch by @vinhjaxt 1. Exit Node Visibility Issue When isolating each user with the following ACL, it works correctly in restricting access. However, exit nodes belonging to other users are still displayed, even though they are not accessible. Expected behavior: Each user's exit node should only be visible to their own devices and not to other users. 2. ACL File Parsing Issue When using a file instead of the API for ACL rules, the system fails to parse the ACL policy correctly. The policy I am using: { "acls": [ { "action": "accept", "src": ["autogroup:self"], "dst": ["autogroup:self:*"] }, { "action": "accept", "src": ["autogroup:self"], "dst": ["autogroup:internet:*"] } ], "autoApprovers": { "exitNode": ["autogroup:self"] } }
Author
Owner

@HybridRCG commented on GitHub (Feb 28, 2025):

autogroup:internet will be good to have. I don't see a way to "deny" traffic. Tried both action "action": "reject" and "action": "deny" but syntax does not support it.

The problem this creates is similar to use case described here: I want to give access to exit nodes for internet to some users without exposing internal IPs.

In regular firewall I will do something like:

rule1: deny traffic to RFC1918 private IPs for tag: family rule2: allow traffic to 0.0.0.0/0, ::/0 to tag:family

but since rule1 is not possible, I am using following as workaround:

    // Family can access internet via exit nodes but not private IPs
    {
      "action": "accept",
      "src": ["group:family"],
      "dst": [ "0.0.0.0/8:*","2.0.0.0/8:*","3.0.0.0/8:*","4.0.0.0/6:*","8.0.0.0/7:*","11.0.0.0/8:*", 
                "12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/2:*","128.0.0.0/3:*",
                "160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*",
                "172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*",
                "192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*",
                "193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*" ]      
    },

  // Family can access home server on 80/443
    {
      "action": "accept",
      "src": ["group:family"],
      "dst": ["172.16.16.4/32:80","172.16.16.4/32:443"],
      "proto": "tcp"
    }

The list of IPs is certainly long and ugly. Plus don't see it supporting IPv6 in this syntax.

How is the

@anuragbhatia Thank you for that!

Your dest has "64.0.0.0/2:*" which includes 100.64.X.X. This gave access to all my nodes as I kept the default range (100.64.0.0). I changed mine to exclude this range and that fixed the problem.

    {
      "action": "accept",
      "src": ["group:client"],
      "dst": [
              "group:exit:0",       // Allow to go through exit nodes without accessing any ports on the hosts 
              "0.0.0.0/5:*","8.0.0.0/7:*","11.0.0.0/8:*","12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/3:*","96.0.0.0/6:*","100.0.0.0/10:*","100.128.0.0/9:*","101.0.0.0/8:*","102.0.0.0/7:*","104.0.0.0/5:*","112.0.0.0/5:*","120.0.0.0/6:*","124.0.0.0/7:*","126.0.0.0/8:*","128.0.0.0/3:*","160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*","172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*","192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*","193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*",
             ]
    },

I used the following commands to exclude some popular internal ranges.

netmask -c 0.0.0.0:9.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#10.0.0.0/8
netmask -c 11.0.0.0:100.63.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#100.64.0.0/10
netmask -c 100.128.0.0:126.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#127.0.0.0/8
netmask -c 128.0.0.0:172.15.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#172.16.0.0/12
netmask -c 172.32.0.0:192.167.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
#192.168.0.0/16 
netmask -c 192.169.0.0:223.255.255.255  | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl
cat  ./acl

Hi

  1. How is the "group:exit:0" defined.
  2. Is it possible to stop clinet form using the internet on the exit node. but let it use its own internet? Currently as i see it if i dont have a internet rule, it blocks all internet traffic on the client side, so outlook etc cant function..
@HybridRCG commented on GitHub (Feb 28, 2025): > autogroup:internet will be good to have. I don't see a way to "deny" traffic. Tried both action "action": "reject" and "action": "deny" but syntax does not support it. > > The problem this creates is similar to use case described here: I want to give access to exit nodes for internet to some users without exposing internal IPs. > > In regular firewall I will do something like: > > rule1: deny traffic to RFC1918 private IPs for tag: family rule2: allow traffic to 0.0.0.0/0, ::/0 to tag:family > > but since rule1 is not possible, I am using following as workaround: > > ``` > // Family can access internet via exit nodes but not private IPs > { > "action": "accept", > "src": ["group:family"], > "dst": [ "0.0.0.0/8:*","2.0.0.0/8:*","3.0.0.0/8:*","4.0.0.0/6:*","8.0.0.0/7:*","11.0.0.0/8:*", > "12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/2:*","128.0.0.0/3:*", > "160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*", > "172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*", > "192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*", > "193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*" ] > }, > > // Family can access home server on 80/443 > { > "action": "accept", > "src": ["group:family"], > "dst": ["172.16.16.4/32:80","172.16.16.4/32:443"], > "proto": "tcp" > } > ``` > > The list of IPs is certainly long and ugly. Plus don't see it supporting IPv6 in this syntax. How is the > [@anuragbhatia](https://github.com/anuragbhatia) Thank you for that! > > Your dest has "64.0.0.0/2:*" which includes 100.64.X.X. This gave access to all my nodes as I kept the default range (100.64.0.0). I changed mine to exclude this range and that fixed the problem. > > ``` > { > "action": "accept", > "src": ["group:client"], > "dst": [ > "group:exit:0", // Allow to go through exit nodes without accessing any ports on the hosts > "0.0.0.0/5:*","8.0.0.0/7:*","11.0.0.0/8:*","12.0.0.0/6:*","16.0.0.0/4:*","32.0.0.0/3:*","64.0.0.0/3:*","96.0.0.0/6:*","100.0.0.0/10:*","100.128.0.0/9:*","101.0.0.0/8:*","102.0.0.0/7:*","104.0.0.0/5:*","112.0.0.0/5:*","120.0.0.0/6:*","124.0.0.0/7:*","126.0.0.0/8:*","128.0.0.0/3:*","160.0.0.0/5:*","168.0.0.0/6:*","172.0.0.0/12:*","172.32.0.0/11:*","172.64.0.0/10:*","172.128.0.0/9:*","173.0.0.0/8:*","174.0.0.0/7:*","176.0.0.0/4:*","192.0.0.0/9:*","192.128.0.0/11:*","192.160.0.0/13:*","192.169.0.0/16:*","192.170.0.0/15:*","192.172.0.0/14:*","192.176.0.0/12:*","192.192.0.0/10:*","193.0.0.0/8:*","194.0.0.0/7:*","196.0.0.0/6:*","200.0.0.0/5:*","208.0.0.0/4:*", > ] > }, > ``` > > I used the following commands to exclude some popular internal ranges. > > ``` > netmask -c 0.0.0.0:9.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > #10.0.0.0/8 > netmask -c 11.0.0.0:100.63.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > #100.64.0.0/10 > netmask -c 100.128.0.0:126.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > #127.0.0.0/8 > netmask -c 128.0.0.0:172.15.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > #172.16.0.0/12 > netmask -c 172.32.0.0:192.167.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > #192.168.0.0/16 > netmask -c 192.169.0.0:223.255.255.255 | awk '{print "\""$1":*\""}' | sed -z 's/\n/,/g' >> ./acl > cat ./acl > ``` Hi 1. How is the "group:exit:0" defined. 2. Is it possible to stop clinet form using the internet on the exit node. but let it use its own internet? Currently as i see it if i dont have a internet rule, it blocks all internet traffic on the client side, so outlook etc cant function..
Author
Owner

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

Maybe we can implement this bit by bit and start with some easy steps like autogroup:self ?

@SuperSandro2000 commented on GitHub (Mar 1, 2025): Maybe we can implement this bit by bit and start with some easy steps like autogroup:self ?
Author
Owner

@kradalby commented on GitHub (Mar 2, 2025):

I think that make sense, I have a tracking bug for my current goal of reimplementing the policy (as v2) #2416, currently, I consider it out of scope to add new ones, but when it is in, I dont see why we cant start looking at it.

@kradalby commented on GitHub (Mar 2, 2025): I think that make sense, I have a tracking bug for my current goal of reimplementing the policy (as v2) #2416, currently, I consider it out of scope to add new ones, but when it is in, I dont see why we cant start looking at it.
Author
Owner

@adipierro commented on GitHub (Mar 3, 2025):

Maybe we can implement this bit by bit and start with some easy steps like autogroup:self ?

Unfortunately, implementing autogroup:self is not that easy, and I consider proper and efficient implementation as a quite complicated process, especially while policy is in its current state -- it might require a lot of CPU time in large environments.

@adipierro commented on GitHub (Mar 3, 2025): > Maybe we can implement this bit by bit and start with some easy steps like autogroup:self ? Unfortunately, implementing `autogroup:self` is not that easy, and I consider proper and efficient implementation as a quite complicated process, especially while `policy` is in its current state -- it might require a lot of CPU time in large environments.
Author
Owner

@kradalby commented on GitHub (Mar 3, 2025):

Luckily we can start with a naive and working one as headscale is targeted small environments, so we don’t have to do premature optimalisations

@kradalby commented on GitHub (Mar 3, 2025): Luckily we can start with a naive and working one as headscale is targeted small environments, so we don’t have to do premature optimalisations
Author
Owner

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

I wrote up an overview for this feature in https://github.com/juanfont/headscale/issues/2618 with the current state and I will close this issue as part of letting that take over. As I see it, after @vdovhanych implemented member and tagged as part of the new policy based on @gabe565 and @vinhjaxt code. The only remaining is autogroup:self.

@kradalby commented on GitHub (May 21, 2025): I wrote up an overview for this feature in https://github.com/juanfont/headscale/issues/2618 with the current state and I will close this issue as part of letting that take over. As I see it, after @vdovhanych implemented `member` and `tagged` as part of the new policy based on @gabe565 and @vinhjaxt code. The only remaining is `autogroup:self`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#280