Merge branch 'main' into update-dependencies

This commit is contained in:
Kristoffer Dalby 2022-03-01 15:16:56 +01:00 committed by GitHub
commit 573008757d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 29 deletions

View file

@ -7,6 +7,10 @@
- Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357) - Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357)
- To limit access between nodes, use [ACLs](./docs/acls.md). - To limit access between nodes, use [ACLs](./docs/acls.md).
**Features**:
- Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359)
**Changes**: **Changes**:
- Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346)

40
acls.go
View file

@ -5,11 +5,13 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/tailscale/hujson" "github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -53,16 +55,36 @@ func (h *Headscale) LoadACLPolicy(path string) error {
return err return err
} }
ast, err := hujson.Parse(policyBytes) switch filepath.Ext(path) {
if err != nil { case ".yml", ".yaml":
return err log.Debug().
} Str("path", path).
ast.Standardize() Bytes("file", policyBytes).
policyBytes = ast.Pack() Msg("Loading ACLs from YAML")
err = json.Unmarshal(policyBytes, &policy)
if err != nil { err := yaml.Unmarshal(policyBytes, &policy)
return err 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() { if policy.IsZero() {
return errEmptyPolicy return errEmptyPolicy
} }

View file

@ -328,6 +328,22 @@ func (s *Suite) TestPortWildcard(c *check.C) {
c.Assert(rules[0].SrcIPs[0], check.Equals, "*") c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
} }
func (s *Suite) TestPortWildcardYAML(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.yaml")
c.Assert(err, check.IsNil)
rules, err := app.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(rules, check.HasLen, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert(rules[0].SrcIPs, check.HasLen, 1)
c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
}
func (s *Suite) TestPortNamespace(c *check.C) { func (s *Suite) TestPortNamespace(c *check.C) {
namespace, err := app.CreateNamespace("testnamespace") namespace, err := app.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)

View file

@ -5,23 +5,24 @@ import (
"strings" "strings"
"github.com/tailscale/hujson" "github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr" "inet.af/netaddr"
) )
// ACLPolicy represents a Tailscale ACL Policy. // ACLPolicy represents a Tailscale ACL Policy.
type ACLPolicy struct { type ACLPolicy struct {
Groups Groups `json:"Groups"` Groups Groups `json:"Groups" yaml:"Groups"`
Hosts Hosts `json:"Hosts"` Hosts Hosts `json:"Hosts" yaml:"Hosts"`
TagOwners TagOwners `json:"TagOwners"` TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"`
ACLs []ACL `json:"ACLs"` ACLs []ACL `json:"ACLs" yaml:"ACLs"`
Tests []ACLTest `json:"Tests"` Tests []ACLTest `json:"Tests" yaml:"Tests"`
} }
// ACL is a basic rule for the ACL Policy. // ACL is a basic rule for the ACL Policy.
type ACL struct { type ACL struct {
Action string `json:"Action"` Action string `json:"Action" yaml:"Action"`
Users []string `json:"Users"` Users []string `json:"Users" yaml:"Users"`
Ports []string `json:"Ports"` Ports []string `json:"Ports" yaml:"Ports"`
} }
// Groups references a series of alias in the ACL rules. // 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. // ACLTest is not implemented, but should be use to check if a certain rule is allowed.
type ACLTest struct { type ACLTest struct {
User string `json:"User"` User string `json:"User" yaml:"User"`
Allow []string `json:"Allow"` Allow []string `json:"Allow" yaml:"Allow"`
Deny []string `json:"Deny,omitempty"` Deny []string `json:"Deny,omitempty" yaml:"Deny,omitempty"`
} }
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects. // UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
@ -69,6 +70,27 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error {
return nil 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. // IsZero is perhaps a bit naive here.
func (policy ACLPolicy) IsZero() bool { func (policy ACLPolicy) IsZero() bool {
if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 {

View file

@ -132,7 +132,8 @@ tls_key_path: ""
log_level: info log_level: info
# Path to a file containg ACL policies. # Path to a file containg ACL policies.
# Recommended path: /etc/headscale/acl.hujson # ACLs can be defined as YAML or HUJSON.
# https://tailscale.com/kb/1018/acls/
acl_policy_path: "" acl_policy_path: ""
## DNS ## DNS

View file

@ -0,0 +1,10 @@
---
Hosts:
host-1: 100.100.100.100/32
subnet-1: 100.100.101.100/24
ACLs:
- Action: accept
Users:
- "*"
Ports:
- host-1:*

View file

@ -196,10 +196,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
var addressesSlices []string var addressesSlices []string
h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices) h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices)
log.Trace().
Strs("addresses", addressesSlices).
Msg("Got allocated ip addresses from databases")
var ips netaddr.IPSetBuilder var ips netaddr.IPSetBuilder
for _, slice := range addressesSlices { for _, slice := range addressesSlices {
var machineAddresses MachineAddresses var machineAddresses MachineAddresses
@ -216,10 +212,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
} }
} }
log.Trace().
Interface("addresses", ips).
Msg("Parsed ip addresses that has been allocated from databases")
ipSet, err := ips.IPSet() ipSet, err := ips.IPSet()
if err != nil { if err != nil {
return &netaddr.IPSet{}, fmt.Errorf( return &netaddr.IPSet{}, fmt.Errorf(