mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Add YAML support to ACLs
This commit is contained in:
		
							parent
							
								
									67d6c8f946
								
							
						
					
					
						commit
						8a3a0b6403
					
				
							
								
								
									
										40
									
								
								acls.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								acls.go
									
									
									
									
									
								
							@ -5,11 +5,13 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"github.com/tailscale/hujson"
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
	"inet.af/netaddr"
 | 
			
		||||
	"tailscale.com/tailcfg"
 | 
			
		||||
)
 | 
			
		||||
@ -53,16 +55,36 @@ func (h *Headscale) LoadACLPolicy(path string) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ast, err := hujson.Parse(policyBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	ast.Standardize()
 | 
			
		||||
	policyBytes = ast.Pack()
 | 
			
		||||
	err = json.Unmarshal(policyBytes, &policy)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	switch filepath.Ext(path) {
 | 
			
		||||
	case ".yml", ".yaml":
 | 
			
		||||
		log.Debug().
 | 
			
		||||
			Str("path", path).
 | 
			
		||||
			Bytes("file", policyBytes).
 | 
			
		||||
			Msg("Loading ACLs from YAML")
 | 
			
		||||
 | 
			
		||||
		err := yaml.Unmarshal(policyBytes, &policy)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Trace().
 | 
			
		||||
			Interface("policy", policy).
 | 
			
		||||
			Msg("Loaded policy from YAML")
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		ast, err := hujson.Parse(policyBytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ast.Standardize()
 | 
			
		||||
		policyBytes = ast.Pack()
 | 
			
		||||
		err = json.Unmarshal(policyBytes, &policy)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if policy.IsZero() {
 | 
			
		||||
		return errEmptyPolicy
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -5,23 +5,24 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/tailscale/hujson"
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
	"inet.af/netaddr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ACLPolicy represents a Tailscale ACL Policy.
 | 
			
		||||
type ACLPolicy struct {
 | 
			
		||||
	Groups    Groups    `json:"Groups"`
 | 
			
		||||
	Hosts     Hosts     `json:"Hosts"`
 | 
			
		||||
	TagOwners TagOwners `json:"TagOwners"`
 | 
			
		||||
	ACLs      []ACL     `json:"ACLs"`
 | 
			
		||||
	Tests     []ACLTest `json:"Tests"`
 | 
			
		||||
	Groups    Groups    `json:"Groups"    yaml:"Groups"`
 | 
			
		||||
	Hosts     Hosts     `json:"Hosts"     yaml:"Hosts"`
 | 
			
		||||
	TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"`
 | 
			
		||||
	ACLs      []ACL     `json:"ACLs"      yaml:"ACLs"`
 | 
			
		||||
	Tests     []ACLTest `json:"Tests"     yaml:"Tests"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ACL is a basic rule for the ACL Policy.
 | 
			
		||||
type ACL struct {
 | 
			
		||||
	Action string   `json:"Action"`
 | 
			
		||||
	Users  []string `json:"Users"`
 | 
			
		||||
	Ports  []string `json:"Ports"`
 | 
			
		||||
	Action string   `json:"Action" yaml:"Action"`
 | 
			
		||||
	Users  []string `json:"Users"  yaml:"Users"`
 | 
			
		||||
	Ports  []string `json:"Ports"  yaml:"Ports"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Groups references a series of alias in the ACL rules.
 | 
			
		||||
@ -35,9 +36,9 @@ type TagOwners map[string][]string
 | 
			
		||||
 | 
			
		||||
// ACLTest is not implemented, but should be use to check if a certain rule is allowed.
 | 
			
		||||
type ACLTest struct {
 | 
			
		||||
	User  string   `json:"User"`
 | 
			
		||||
	Allow []string `json:"Allow"`
 | 
			
		||||
	Deny  []string `json:"Deny,omitempty"`
 | 
			
		||||
	User  string   `json:"User"           yaml:"User"`
 | 
			
		||||
	Allow []string `json:"Allow"          yaml:"Allow"`
 | 
			
		||||
	Deny  []string `json:"Deny,omitempty" yaml:"Deny,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
 | 
			
		||||
@ -69,6 +70,27 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalYAML allows to parse the Hosts directly into netaddr objects.
 | 
			
		||||
func (hosts *Hosts) UnmarshalYAML(data []byte) error {
 | 
			
		||||
	newHosts := Hosts{}
 | 
			
		||||
	hostIPPrefixMap := make(map[string]string)
 | 
			
		||||
 | 
			
		||||
	err := yaml.Unmarshal(data, &hostIPPrefixMap)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for host, prefixStr := range hostIPPrefixMap {
 | 
			
		||||
		prefix, err := netaddr.ParseIPPrefix(prefixStr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		newHosts[host] = prefix
 | 
			
		||||
	}
 | 
			
		||||
	*hosts = newHosts
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsZero is perhaps a bit naive here.
 | 
			
		||||
func (policy ACLPolicy) IsZero() bool {
 | 
			
		||||
	if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user