1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-09-11 17:53:10 +02:00

Allow setting ForcedTags using the acls.hujson config file

This patch introduces a declarative way to set ForcedTags for machines.

If a "forcedTags" field is set in the acls.hujson file, the ForcedTags
of machines will be overriden in the database based on the tags defined
in the config file.

```json
{
    "hosts": {
        "a": "fd7a:115c:a1e0::1",
        "b": "fd7a:115c:a1e0::2"
    },
    "forcedTags": {
        "tag:some-tag": [
            "a",
            "tag:some-other-tag"
        ],
        "tag:some-other-tag": [
            "b"
        ]
    }
}
```
This commit is contained in:
networkException 2023-06-17 19:13:45 +02:00
parent b01f1f1867
commit 1dd7f193c9
No known key found for this signature in database
GPG Key ID: E3877443AE684391
2 changed files with 77 additions and 0 deletions

View File

@ -119,9 +119,82 @@ func (h *Headscale) LoadACLPolicyFromBytes(acl []byte, format string) error {
h.aclPolicy = &policy
machines, err := h.ListMachines()
if err != nil {
return err
}
if h.aclPolicy.ForcedTags != nil {
forcedTagsByIp := make(map[netip.Addr][]string)
for tag, _ := range h.aclPolicy.ForcedTags {
var expandedHosts []string
err := expandNestedTagsToHosts(h.aclPolicy.ForcedTags, tag, &expandedHosts, 0)
if err != nil {
return err
}
for _, expandedHost := range expandedHosts {
ipForExpandedHost := h.aclPolicy.Hosts[expandedHost].Addr()
forcedTags, _ := forcedTagsByIp[ipForExpandedHost]
forcedTagsByIp[ipForExpandedHost] = append(forcedTags, tag)
}
}
for _, machine := range machines {
machine, err := h.GetMachineByID(machine.ID)
if err != nil {
return err
}
machine.ForcedTags = []string{}
for _, ip := range machine.IPAddresses {
forcedTags, ok := forcedTagsByIp[ip]
if ok {
log.Info().
Str("machine", machine.String()).
Strs("forcedTags", forcedTags).
Msg("Setting forced tags")
machine.ForcedTags = forcedTags
}
}
if err := h.db.Save(machine).Error; err != nil {
return fmt.Errorf("failed to update tags for machine in the database: %w", err)
}
}
h.setLastStateChangeToNow()
}
return h.UpdateACLRules()
}
func expandNestedTagsToHosts(forcedTags ForcedTags, tag string, into *[]string, depth int) error {
if depth > 5 {
log.Error().
Msgf("Recursed too deeply trying to expand %s, expanded %v so far", tag, *into)
return fmt.Errorf("Recursed too deeply")
}
for _, hostOrTag := range forcedTags[tag] {
if !strings.HasPrefix(hostOrTag, "tag:") {
*into = append(*into, hostOrTag)
} else {
expandNestedTagsToHosts(forcedTags, hostOrTag, into, depth + 1)
}
}
return nil
}
func (h *Headscale) UpdateACLRules() error {
machines, err := h.ListMachines()
if err != nil {

View File

@ -14,6 +14,7 @@ type ACLPolicy struct {
Groups Groups `json:"groups" yaml:"groups"`
Hosts Hosts `json:"hosts" yaml:"hosts"`
TagOwners TagOwners `json:"tagOwners" yaml:"tagOwners"`
ForcedTags ForcedTags `json:"forcedTags" yaml:"forcedTags"`
ACLs []ACL `json:"acls" yaml:"acls"`
Tests []ACLTest `json:"tests" yaml:"tests"`
AutoApprovers AutoApprovers `json:"autoApprovers" yaml:"autoApprovers"`
@ -28,6 +29,9 @@ type ACL struct {
Destinations []string `json:"dst" yaml:"dst"`
}
// ForcedTags specifies which tags are applied to which hosts by the server
type ForcedTags map[string][]string
// Groups references a series of alias in the ACL rules.
type Groups map[string][]string