[Feature] Template-based ACL generation #860

Closed
opened 2025-12-29 02:24:57 +01:00 by adam · 2 comments
Owner

Originally created by @mpldr on GitHub (Nov 21, 2024).

Use case

With OIDC-based authentication, new users can be set up pretty easily, however, this does not extend to ACLs. It would be great to have a way to instead of a hujson file, supply a template that generates it based on the current user table. This would allow onboarding a new user easily by simply telling them to authenticate, and everything else would be done automatically based on the template. This has the added advantage of making it possible to share template-ACLs without requiring the user to edit them in detail.

Description

Taking the example from headscale's docs, it could be written as follows to allow dynamically onboard users.

{
  "groups": {
    "group:boss": ["boss"],
    "group:dev": [
    {{ for $user := range .Users }}
    {{ if -eq $user.group "dev" }}
    "{{$user.name}}",
    {{ end }}
    {{ end }}
    ],
    "group:admin": [
    {{ for $user := range .Users }}
    {{ if -eq $user.group "admin" }}
    "{{$user.name}}",
    {{ end }}
    {{ end }}
    ],
    "group:intern": [
    {{ for $user := range .Users }}
    {{ if -eq $user.group "intern" }}
    "{{$user.name}}",
    {{ end }}
    {{ end }}

    ]
  },
  "tagOwners": {
    "tag:prod-databases": ["group:admin"],
    "tag:prod-app-servers": ["group:admin"],
    "tag:internal": ["group:boss"],
    "tag:dev-databases": ["group:admin", "group:dev"],
    "tag:dev-app-servers": ["group:admin", "group:dev"]
  },
  "hosts": {
    "postgresql.internal": "10.20.0.2/32",
    "webservers.internal": "10.20.10.1/29"
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:boss"],
      "dst": [
        "tag:prod-databases:*",
        "tag:prod-app-servers:*",
        "tag:internal:*",
        "tag:dev-databases:*",
        "tag:dev-app-servers:*"
      ]
    },

    {
      "action": "accept",
      "src": ["group:admin"],
      "proto": "tcp",
      "dst": [
        "tag:prod-databases:22",
        "tag:prod-app-servers:22",
        "tag:internal:22",
        "tag:dev-databases:22",
        "tag:dev-app-servers:22"
      ]
    },

    {
      "action": "accept",
      "src": ["group:admin"],
      "proto": "icmp",
      "dst": [
        "tag:prod-databases:*",
        "tag:prod-app-servers:*",
        "tag:internal:*",
        "tag:dev-databases:*",
        "tag:dev-app-servers:*"
      ]
    },

    {
      "action": "accept",
      "src": ["group:dev"],
      "dst": [
        "tag:dev-databases:*",
        "tag:dev-app-servers:*",
        "tag:prod-app-servers:80,443"
      ]
    },

    {
      "action": "accept",
      "src": ["group:dev"],
      "dst": ["10.20.0.0/16:443,5432", "router.internal:0"]
    },

    {
      "action": "accept",
      "src": ["tag:dev-app-servers"],
      "proto": "tcp",
      "dst": ["tag:dev-databases:5432"]
    },
    {
      "action": "accept",
      "src": ["tag:prod-app-servers"],
      "dst": ["tag:prod-databases:5432"]
    },

    {
      "action": "accept",
      "src": ["group:intern"],
      "dst": ["tag:dev-app-servers:80,443"]
    },

    {{ for $user := range .Users }}
    { "action": "accept", "src": ["{{$user.name}}"], "dst": ["{{$user.name}}:*"] },
    {{ end }}
  ]
}

This specific example would, of course, require deriving a group from an OIDC claim, which is beyond the scope of this issue, the basic functionality of:

    {{ for $user := range .Users }}
    { "action": "accept", "src": ["{{$user.name}}"], "dst": ["{{$user.name}}:*"] },
    {{ end }}

Would already be a huge stepping stone in that regard, though.

Contribution

  • I can write the design doc for this feature
  • I can contribute this feature

How can it be implemented?

Go has a powerful templating language already, though the use of curly braces may make the generation of hujson more difficult. I would push this issue off until a design document or general feedback has been provided, though.

Originally created by @mpldr on GitHub (Nov 21, 2024). ### Use case With OIDC-based authentication, new users can be set up pretty easily, however, this does not extend to ACLs. It would be great to have a way to instead of a hujson file, supply a template that generates it based on the current user table. This would allow onboarding a new user easily by simply telling them to authenticate, and everything else would be done automatically based on the template. This has the added advantage of making it possible to share template-ACLs without requiring the user to edit them in detail. ### Description Taking the example from headscale's docs, it could be written as follows to allow dynamically onboard users. ``` { "groups": { "group:boss": ["boss"], "group:dev": [ {{ for $user := range .Users }} {{ if -eq $user.group "dev" }} "{{$user.name}}", {{ end }} {{ end }} ], "group:admin": [ {{ for $user := range .Users }} {{ if -eq $user.group "admin" }} "{{$user.name}}", {{ end }} {{ end }} ], "group:intern": [ {{ for $user := range .Users }} {{ if -eq $user.group "intern" }} "{{$user.name}}", {{ end }} {{ end }} ] }, "tagOwners": { "tag:prod-databases": ["group:admin"], "tag:prod-app-servers": ["group:admin"], "tag:internal": ["group:boss"], "tag:dev-databases": ["group:admin", "group:dev"], "tag:dev-app-servers": ["group:admin", "group:dev"] }, "hosts": { "postgresql.internal": "10.20.0.2/32", "webservers.internal": "10.20.10.1/29" }, "acls": [ { "action": "accept", "src": ["group:boss"], "dst": [ "tag:prod-databases:*", "tag:prod-app-servers:*", "tag:internal:*", "tag:dev-databases:*", "tag:dev-app-servers:*" ] }, { "action": "accept", "src": ["group:admin"], "proto": "tcp", "dst": [ "tag:prod-databases:22", "tag:prod-app-servers:22", "tag:internal:22", "tag:dev-databases:22", "tag:dev-app-servers:22" ] }, { "action": "accept", "src": ["group:admin"], "proto": "icmp", "dst": [ "tag:prod-databases:*", "tag:prod-app-servers:*", "tag:internal:*", "tag:dev-databases:*", "tag:dev-app-servers:*" ] }, { "action": "accept", "src": ["group:dev"], "dst": [ "tag:dev-databases:*", "tag:dev-app-servers:*", "tag:prod-app-servers:80,443" ] }, { "action": "accept", "src": ["group:dev"], "dst": ["10.20.0.0/16:443,5432", "router.internal:0"] }, { "action": "accept", "src": ["tag:dev-app-servers"], "proto": "tcp", "dst": ["tag:dev-databases:5432"] }, { "action": "accept", "src": ["tag:prod-app-servers"], "dst": ["tag:prod-databases:5432"] }, { "action": "accept", "src": ["group:intern"], "dst": ["tag:dev-app-servers:80,443"] }, {{ for $user := range .Users }} { "action": "accept", "src": ["{{$user.name}}"], "dst": ["{{$user.name}}:*"] }, {{ end }} ] } ``` This specific example would, of course, require deriving a group from an OIDC claim, which is beyond the scope of this issue, the basic functionality of: ``` {{ for $user := range .Users }} { "action": "accept", "src": ["{{$user.name}}"], "dst": ["{{$user.name}}:*"] }, {{ end }} ``` Would already be a huge stepping stone in that regard, though. ### Contribution - [ ] I can write the design doc for this feature - [X] I can contribute this feature ### How can it be implemented? Go has a powerful templating language already, though the use of curly braces may make the generation of hujson more difficult. I would push this issue off until a design document or general feedback has been provided, though.
adam added the enhancement label 2025-12-29 02:24:57 +01:00
adam closed this issue 2025-12-29 02:24:57 +01:00
Author
Owner

@ldesgoui commented on GitHub (Dec 8, 2024):

I completely disagree that text templating is an appropriate solution to this problem, it has caused in my experience a great amount of pain, that pain is shared and a lot of it is documented online. Programmatically generating JSON documents is trivial in all general programming languages.

@ldesgoui commented on GitHub (Dec 8, 2024): I completely disagree that text templating is an appropriate solution to this problem, it has caused in my experience a great amount of pain, that pain is shared and a lot of it is documented online. Programmatically generating JSON documents is trivial in all general programming languages.
Author
Owner

@kradalby commented on GitHub (Dec 10, 2024):

I too think this is not the way to go. As mentioned by @ldesgoui, there are plenty of ways to programmatically create json.

Gives me bad memories from working with Helm.

@kradalby commented on GitHub (Dec 10, 2024): I too think this is not the way to go. As mentioned by @ldesgoui, there are plenty of ways to programmatically create json. Gives me bad memories from working with Helm.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#860