mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-11-04 10:01:05 +01:00 
			
		
		
		
	Merge branch 'main' into test-126
This commit is contained in:
		
						commit
						a058f17946
					
				@ -2,6 +2,10 @@
 | 
			
		||||
 | 
			
		||||
## 0.16.0 (2022-xx-xx)
 | 
			
		||||
 | 
			
		||||
### BREAKING
 | 
			
		||||
 | 
			
		||||
- Old ACL syntax is no longer supported ("users" & "ports" -> "src" & "dst"). Please check [the new syntax](https://tailscale.com/kb/1018/acls/).
 | 
			
		||||
 | 
			
		||||
### Changes
 | 
			
		||||
 | 
			
		||||
- **Drop** armhf (32-bit ARM) support. [#609](https://github.com/juanfont/headscale/pull/609)
 | 
			
		||||
@ -22,7 +26,9 @@
 | 
			
		||||
  - This change disables the logs by default
 | 
			
		||||
- Use [Prometheus]'s duration parser, supporting days (`d`), weeks (`w`) and years (`y`) [#598](https://github.com/juanfont/headscale/pull/598)
 | 
			
		||||
- Add support for reloading ACLs with SIGHUP [#601](https://github.com/juanfont/headscale/pull/601)
 | 
			
		||||
- Use new ACL syntax [#618](https://github.com/juanfont/headscale/pull/618)
 | 
			
		||||
- Add -c option to specify config file from command line [#285](https://github.com/juanfont/headscale/issues/285) [#612](https://github.com/juanfont/headscale/pull/601)
 | 
			
		||||
- Add configuration option to allow Tailscale clients to use a random WireGuard port. [kb/1181/firewalls](https://tailscale.com/kb/1181/firewalls) [#624](https://github.com/juanfont/headscale/pull/624)
 | 
			
		||||
 | 
			
		||||
## 0.15.0 (2022-03-20)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										107
									
								
								acls.go
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								acls.go
									
									
									
									
									
								
							@ -23,6 +23,7 @@ const (
 | 
			
		||||
	errInvalidGroup      = Error("invalid group")
 | 
			
		||||
	errInvalidTag        = Error("invalid tag")
 | 
			
		||||
	errInvalidPortFormat = Error("invalid port format")
 | 
			
		||||
	errWildcardIsNeeded  = Error("wildcard as port is required for the protocol")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -36,6 +37,23 @@ const (
 | 
			
		||||
	expectedTokenItems = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// For some reason golang.org/x/net/internal/iana is an internal package
 | 
			
		||||
const (
 | 
			
		||||
	protocolICMP     = 1   // Internet Control Message
 | 
			
		||||
	protocolIGMP     = 2   // Internet Group Management
 | 
			
		||||
	protocolIPv4     = 4   // IPv4 encapsulation
 | 
			
		||||
	protocolTCP      = 6   // Transmission Control
 | 
			
		||||
	protocolEGP      = 8   // Exterior Gateway Protocol
 | 
			
		||||
	protocolIGP      = 9   // any private interior gateway (used by Cisco for their IGRP)
 | 
			
		||||
	protocolUDP      = 17  // User Datagram
 | 
			
		||||
	protocolGRE      = 47  // Generic Routing Encapsulation
 | 
			
		||||
	protocolESP      = 50  // Encap Security Payload
 | 
			
		||||
	protocolAH       = 51  // Authentication Header
 | 
			
		||||
	protocolIPv6ICMP = 58  // ICMP for IPv6
 | 
			
		||||
	protocolSCTP     = 132 // Stream Control Transmission Protocol
 | 
			
		||||
	ProtocolFC       = 133 // Fibre Channel
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules.
 | 
			
		||||
func (h *Headscale) LoadACLPolicy(path string) error {
 | 
			
		||||
	log.Debug().
 | 
			
		||||
@ -123,23 +141,31 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		srcIPs := []string{}
 | 
			
		||||
		for innerIndex, user := range acl.Users {
 | 
			
		||||
			srcs, err := h.generateACLPolicySrcIP(machines, *h.aclPolicy, user)
 | 
			
		||||
		for innerIndex, src := range acl.Sources {
 | 
			
		||||
			srcs, err := h.generateACLPolicySrcIP(machines, *h.aclPolicy, src)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Msgf("Error parsing ACL %d, User %d", index, innerIndex)
 | 
			
		||||
					Msgf("Error parsing ACL %d, Source %d", index, innerIndex)
 | 
			
		||||
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			srcIPs = append(srcIPs, srcs...)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protocols, needsWildcard, err := parseProtocol(acl.Protocol)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error().
 | 
			
		||||
				Msgf("Error parsing ACL %d. protocol unknown %s", index, acl.Protocol)
 | 
			
		||||
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		destPorts := []tailcfg.NetPortRange{}
 | 
			
		||||
		for innerIndex, ports := range acl.Ports {
 | 
			
		||||
			dests, err := h.generateACLPolicyDestPorts(machines, *h.aclPolicy, ports)
 | 
			
		||||
		for innerIndex, dest := range acl.Destinations {
 | 
			
		||||
			dests, err := h.generateACLPolicyDest(machines, *h.aclPolicy, dest, needsWildcard)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error().
 | 
			
		||||
					Msgf("Error parsing ACL %d, Port %d", index, innerIndex)
 | 
			
		||||
					Msgf("Error parsing ACL %d, Destination %d", index, innerIndex)
 | 
			
		||||
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
@ -149,6 +175,7 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
 | 
			
		||||
		rules = append(rules, tailcfg.FilterRule{
 | 
			
		||||
			SrcIPs:   srcIPs,
 | 
			
		||||
			DstPorts: destPorts,
 | 
			
		||||
			IPProto:  protocols,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -158,17 +185,18 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
 | 
			
		||||
func (h *Headscale) generateACLPolicySrcIP(
 | 
			
		||||
	machines []Machine,
 | 
			
		||||
	aclPolicy ACLPolicy,
 | 
			
		||||
	u string,
 | 
			
		||||
	src string,
 | 
			
		||||
) ([]string, error) {
 | 
			
		||||
	return expandAlias(machines, aclPolicy, u, h.cfg.OIDC.StripEmaildomain)
 | 
			
		||||
	return expandAlias(machines, aclPolicy, src, h.cfg.OIDC.StripEmaildomain)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Headscale) generateACLPolicyDestPorts(
 | 
			
		||||
func (h *Headscale) generateACLPolicyDest(
 | 
			
		||||
	machines []Machine,
 | 
			
		||||
	aclPolicy ACLPolicy,
 | 
			
		||||
	d string,
 | 
			
		||||
	dest string,
 | 
			
		||||
	needsWildcard bool,
 | 
			
		||||
) ([]tailcfg.NetPortRange, error) {
 | 
			
		||||
	tokens := strings.Split(d, ":")
 | 
			
		||||
	tokens := strings.Split(dest, ":")
 | 
			
		||||
	if len(tokens) < expectedTokenItems || len(tokens) > 3 {
 | 
			
		||||
		return nil, errInvalidPortFormat
 | 
			
		||||
	}
 | 
			
		||||
@ -195,7 +223,7 @@ func (h *Headscale) generateACLPolicyDestPorts(
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ports, err := expandPorts(tokens[len(tokens)-1])
 | 
			
		||||
	ports, err := expandPorts(tokens[len(tokens)-1], needsWildcard)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@ -214,6 +242,54 @@ func (h *Headscale) generateACLPolicyDestPorts(
 | 
			
		||||
	return dests, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseProtocol reads the proto field of the ACL and generates a list of
 | 
			
		||||
// protocols that will be allowed, following the IANA IP protocol number
 | 
			
		||||
// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
 | 
			
		||||
//
 | 
			
		||||
// If the ACL proto field is empty, it allows ICMPv4, ICMPv6, TCP, and UDP,
 | 
			
		||||
// as per Tailscale behaviour (see tailcfg.FilterRule).
 | 
			
		||||
//
 | 
			
		||||
// Also returns a boolean indicating if the protocol
 | 
			
		||||
// requires all the destinations to use wildcard as port number (only TCP,
 | 
			
		||||
// UDP and SCTP support specifying ports).
 | 
			
		||||
func parseProtocol(protocol string) ([]int, bool, error) {
 | 
			
		||||
	switch protocol {
 | 
			
		||||
	case "":
 | 
			
		||||
		return []int{protocolICMP, protocolIPv6ICMP, protocolTCP, protocolUDP}, false, nil
 | 
			
		||||
	case "igmp":
 | 
			
		||||
		return []int{protocolIGMP}, true, nil
 | 
			
		||||
	case "ipv4", "ip-in-ip":
 | 
			
		||||
		return []int{protocolIPv4}, true, nil
 | 
			
		||||
	case "tcp":
 | 
			
		||||
		return []int{protocolTCP}, false, nil
 | 
			
		||||
	case "egp":
 | 
			
		||||
		return []int{protocolEGP}, true, nil
 | 
			
		||||
	case "igp":
 | 
			
		||||
		return []int{protocolIGP}, true, nil
 | 
			
		||||
	case "udp":
 | 
			
		||||
		return []int{protocolUDP}, false, nil
 | 
			
		||||
	case "gre":
 | 
			
		||||
		return []int{protocolGRE}, true, nil
 | 
			
		||||
	case "esp":
 | 
			
		||||
		return []int{protocolESP}, true, nil
 | 
			
		||||
	case "ah":
 | 
			
		||||
		return []int{protocolAH}, true, nil
 | 
			
		||||
	case "sctp":
 | 
			
		||||
		return []int{protocolSCTP}, false, nil
 | 
			
		||||
	case "icmp":
 | 
			
		||||
		return []int{protocolICMP, protocolIPv6ICMP}, true, nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		protocolNumber, err := strconv.Atoi(protocol)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, false, err
 | 
			
		||||
		}
 | 
			
		||||
		needsWildcard := protocolNumber != protocolTCP && protocolNumber != protocolUDP && protocolNumber != protocolSCTP
 | 
			
		||||
 | 
			
		||||
		return []int{protocolNumber}, needsWildcard, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// expandalias has an input of either
 | 
			
		||||
// - a namespace
 | 
			
		||||
// - a group
 | 
			
		||||
@ -268,6 +344,7 @@ func expandAlias(
 | 
			
		||||
						alias,
 | 
			
		||||
					)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return ips, nil
 | 
			
		||||
			} else {
 | 
			
		||||
				return ips, err
 | 
			
		||||
@ -359,13 +436,17 @@ func excludeCorrectlyTaggedNodes(
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) {
 | 
			
		||||
func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) {
 | 
			
		||||
	if portsStr == "*" {
 | 
			
		||||
		return &[]tailcfg.PortRange{
 | 
			
		||||
			{First: portRangeBegin, Last: portRangeEnd},
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if needsWildcard {
 | 
			
		||||
		return nil, errWildcardIsNeeded
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ports := []tailcfg.PortRange{}
 | 
			
		||||
	for _, portStr := range strings.Split(portsStr, ",") {
 | 
			
		||||
		rang := strings.Split(portStr, "-")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										79
									
								
								acls_test.go
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								acls_test.go
									
									
									
									
									
								
							@ -62,7 +62,7 @@ func (s *Suite) TestBasicRule(c *check.C) {
 | 
			
		||||
func (s *Suite) TestInvalidAction(c *check.C) {
 | 
			
		||||
	app.aclPolicy = &ACLPolicy{
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "invalidAction", Users: []string{"*"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "invalidAction", Sources: []string{"*"}, Destinations: []string{"*:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err := app.UpdateACLRules()
 | 
			
		||||
@ -70,14 +70,14 @@ func (s *Suite) TestInvalidAction(c *check.C) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) TestInvalidGroupInGroup(c *check.C) {
 | 
			
		||||
	// this ACL is wrong because the group in users sections doesn't exist
 | 
			
		||||
	// this ACL is wrong because the group in Sources sections doesn't exist
 | 
			
		||||
	app.aclPolicy = &ACLPolicy{
 | 
			
		||||
		Groups: Groups{
 | 
			
		||||
			"group:test":  []string{"foo"},
 | 
			
		||||
			"group:error": []string{"foo", "group:test"},
 | 
			
		||||
		},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"group:error"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"group:error"}, Destinations: []string{"*:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err := app.UpdateACLRules()
 | 
			
		||||
@ -88,7 +88,7 @@ func (s *Suite) TestInvalidTagOwners(c *check.C) {
 | 
			
		||||
	// this ACL is wrong because no tagOwners own the requested tag for the server
 | 
			
		||||
	app.aclPolicy = &ACLPolicy{
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"tag:foo"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"tag:foo"}, Destinations: []string{"*:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err := app.UpdateACLRules()
 | 
			
		||||
@ -97,8 +97,8 @@ func (s *Suite) TestInvalidTagOwners(c *check.C) {
 | 
			
		||||
 | 
			
		||||
// this test should validate that we can expand a group in a TagOWner section and
 | 
			
		||||
// match properly the IP's of the related hosts. The owner is valid and the tag is also valid.
 | 
			
		||||
// the tag is matched in the Users section.
 | 
			
		||||
func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
 | 
			
		||||
// the tag is matched in the Sources section.
 | 
			
		||||
func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) {
 | 
			
		||||
	namespace, err := app.CreateNamespace("user1")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
@ -131,7 +131,7 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
 | 
			
		||||
		Groups:    Groups{"group:test": []string{"user1", "user2"}},
 | 
			
		||||
		TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"tag:test"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"tag:test"}, Destinations: []string{"*:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err = app.UpdateACLRules()
 | 
			
		||||
@ -143,8 +143,8 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
 | 
			
		||||
 | 
			
		||||
// this test should validate that we can expand a group in a TagOWner section and
 | 
			
		||||
// match properly the IP's of the related hosts. The owner is valid and the tag is also valid.
 | 
			
		||||
// the tag is matched in the Ports section.
 | 
			
		||||
func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
 | 
			
		||||
// the tag is matched in the Destinations section.
 | 
			
		||||
func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) {
 | 
			
		||||
	namespace, err := app.CreateNamespace("user1")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
@ -177,7 +177,7 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
 | 
			
		||||
		Groups:    Groups{"group:test": []string{"user1", "user2"}},
 | 
			
		||||
		TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"*"}, Ports: []string{"tag:test:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"*"}, Destinations: []string{"tag:test:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err = app.UpdateACLRules()
 | 
			
		||||
@ -222,7 +222,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
 | 
			
		||||
	app.aclPolicy = &ACLPolicy{
 | 
			
		||||
		TagOwners: TagOwners{"tag:test": []string{"user1"}},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"user1"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"user1"}, Destinations: []string{"*:*"}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err = app.UpdateACLRules()
 | 
			
		||||
@ -287,9 +287,9 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
 | 
			
		||||
		TagOwners: TagOwners{"tag:webapp": []string{"user1"}},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{
 | 
			
		||||
				Action: "accept",
 | 
			
		||||
				Users:  []string{"user1"},
 | 
			
		||||
				Ports:  []string{"tag:webapp:80,443"},
 | 
			
		||||
				Action:       "accept",
 | 
			
		||||
				Sources:      []string{"user1"},
 | 
			
		||||
				Destinations: []string{"tag:webapp:80,443"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@ -321,6 +321,20 @@ func (s *Suite) TestPortRange(c *check.C) {
 | 
			
		||||
	c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) TestProtocolParsing(c *check.C) {
 | 
			
		||||
	err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_protocols.hujson")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	rules, err := app.generateACLRules()
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
	c.Assert(rules, check.NotNil)
 | 
			
		||||
 | 
			
		||||
	c.Assert(rules, check.HasLen, 3)
 | 
			
		||||
	c.Assert(rules[0].IPProto[0], check.Equals, protocolTCP)
 | 
			
		||||
	c.Assert(rules[1].IPProto[0], check.Equals, protocolUDP)
 | 
			
		||||
	c.Assert(rules[2].IPProto[1], check.Equals, protocolIPv6ICMP)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) TestPortWildcard(c *check.C) {
 | 
			
		||||
	err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
@ -628,7 +642,8 @@ func Test_expandTagOwners(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func Test_expandPorts(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		portsStr string
 | 
			
		||||
		portsStr      string
 | 
			
		||||
		needsWildcard bool
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
@ -638,15 +653,29 @@ func Test_expandPorts(t *testing.T) {
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "wildcard",
 | 
			
		||||
			args: args{portsStr: "*"},
 | 
			
		||||
			args: args{portsStr: "*", needsWildcard: true},
 | 
			
		||||
			want: &[]tailcfg.PortRange{
 | 
			
		||||
				{First: portRangeBegin, Last: portRangeEnd},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "two ports",
 | 
			
		||||
			args: args{portsStr: "80,443"},
 | 
			
		||||
			name: "needs wildcard but does not require it",
 | 
			
		||||
			args: args{portsStr: "*", needsWildcard: false},
 | 
			
		||||
			want: &[]tailcfg.PortRange{
 | 
			
		||||
				{First: portRangeBegin, Last: portRangeEnd},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "needs wildcard but gets port",
 | 
			
		||||
			args:    args{portsStr: "80,443", needsWildcard: true},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "two Destinations",
 | 
			
		||||
			args: args{portsStr: "80,443", needsWildcard: false},
 | 
			
		||||
			want: &[]tailcfg.PortRange{
 | 
			
		||||
				{First: 80, Last: 80},
 | 
			
		||||
				{First: 443, Last: 443},
 | 
			
		||||
@ -655,7 +684,7 @@ func Test_expandPorts(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "a range and a port",
 | 
			
		||||
			args: args{portsStr: "80-1024,443"},
 | 
			
		||||
			args: args{portsStr: "80-1024,443", needsWildcard: false},
 | 
			
		||||
			want: &[]tailcfg.PortRange{
 | 
			
		||||
				{First: 80, Last: 1024},
 | 
			
		||||
				{First: 443, Last: 443},
 | 
			
		||||
@ -664,38 +693,38 @@ func Test_expandPorts(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "out of bounds",
 | 
			
		||||
			args:    args{portsStr: "854038"},
 | 
			
		||||
			args:    args{portsStr: "854038", needsWildcard: false},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "wrong port",
 | 
			
		||||
			args:    args{portsStr: "85a38"},
 | 
			
		||||
			args:    args{portsStr: "85a38", needsWildcard: false},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "wrong port in first",
 | 
			
		||||
			args:    args{portsStr: "a-80"},
 | 
			
		||||
			args:    args{portsStr: "a-80", needsWildcard: false},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "wrong port in last",
 | 
			
		||||
			args:    args{portsStr: "80-85a38"},
 | 
			
		||||
			args:    args{portsStr: "80-85a38", needsWildcard: false},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "wrong port format",
 | 
			
		||||
			args:    args{portsStr: "80-85a38-3"},
 | 
			
		||||
			args:    args{portsStr: "80-85a38-3", needsWildcard: false},
 | 
			
		||||
			want:    nil,
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			got, err := expandPorts(test.args.portsStr)
 | 
			
		||||
			got, err := expandPorts(test.args.portsStr, test.args.needsWildcard)
 | 
			
		||||
			if (err != nil) != test.wantErr {
 | 
			
		||||
				t.Errorf("expandPorts() error = %v, wantErr %v", err, test.wantErr)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,18 +11,19 @@ import (
 | 
			
		||||
 | 
			
		||||
// ACLPolicy represents a Tailscale ACL Policy.
 | 
			
		||||
type ACLPolicy struct {
 | 
			
		||||
	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"`
 | 
			
		||||
	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" yaml:"Action"`
 | 
			
		||||
	Users  []string `json:"Users"  yaml:"Users"`
 | 
			
		||||
	Ports  []string `json:"Ports"  yaml:"Ports"`
 | 
			
		||||
	Action       string   `json:"action" yaml:"action"`
 | 
			
		||||
	Protocol     string   `json:"proto" yaml:"proto"`
 | 
			
		||||
	Sources      []string `json:"src"  yaml:"src"`
 | 
			
		||||
	Destinations []string `json:"dst"  yaml:"dst"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Groups references a series of alias in the ACL rules.
 | 
			
		||||
@ -36,9 +37,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"           yaml:"User"`
 | 
			
		||||
	Allow []string `json:"Allow"          yaml:"Allow"`
 | 
			
		||||
	Deny  []string `json:"Deny,omitempty" yaml:"Deny,omitempty"`
 | 
			
		||||
	Source string   `json:"src"           yaml:"src"`
 | 
			
		||||
	Accept []string `json:"accept"          yaml:"accept"`
 | 
			
		||||
	Deny   []string `json:"deny,omitempty" yaml:"deny,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								api.go
									
									
									
									
									
								
							@ -279,7 +279,8 @@ func (h *Headscale) getMapResponse(
 | 
			
		||||
		DERPMap:      h.DERPMap,
 | 
			
		||||
		UserProfiles: profiles,
 | 
			
		||||
		Debug: &tailcfg.Debug{
 | 
			
		||||
			DisableLogTail: !h.cfg.LogTail.Enabled,
 | 
			
		||||
			DisableLogTail:      !h.cfg.LogTail.Enabled,
 | 
			
		||||
			RandomizeClientPort: h.cfg.RandomizeClientPort,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -113,6 +113,7 @@ func (*Suite) TestConfigLoading(c *check.C) {
 | 
			
		||||
		fs.FileMode(0o770),
 | 
			
		||||
	)
 | 
			
		||||
	c.Assert(viper.GetBool("logtail.enabled"), check.Equals, false)
 | 
			
		||||
	c.Assert(viper.GetBool("randomize_client_port"), check.Equals, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*Suite) TestDNSConfigLoading(c *check.C) {
 | 
			
		||||
 | 
			
		||||
@ -244,3 +244,8 @@ logtail:
 | 
			
		||||
  # As there is currently no support for overriding the log server in headscale, this is
 | 
			
		||||
  # disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
 | 
			
		||||
  enabled: false
 | 
			
		||||
 | 
			
		||||
# Enabling this option makes devices prefer a random port for WireGuard traffic over the
 | 
			
		||||
# default static port 41641. This option is intended as a workaround for some buggy
 | 
			
		||||
# firewall devices. See https://tailscale.com/kb/1181/firewalls/ for more information.
 | 
			
		||||
randomize_client_port: false
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,8 @@ type Config struct {
 | 
			
		||||
 | 
			
		||||
	OIDC OIDCConfig
 | 
			
		||||
 | 
			
		||||
	LogTail LogTailConfig
 | 
			
		||||
	LogTail             LogTailConfig
 | 
			
		||||
	RandomizeClientPort bool
 | 
			
		||||
 | 
			
		||||
	CLI CLIConfig
 | 
			
		||||
 | 
			
		||||
@ -157,6 +158,7 @@ func LoadConfig(path string, isFile bool) error {
 | 
			
		||||
	viper.SetDefault("oidc.strip_email_domain", true)
 | 
			
		||||
 | 
			
		||||
	viper.SetDefault("logtail.enabled", false)
 | 
			
		||||
	viper.SetDefault("randomize_client_port", false)
 | 
			
		||||
 | 
			
		||||
	if err := viper.ReadInConfig(); err != nil {
 | 
			
		||||
		return fmt.Errorf("fatal error reading config file: %w", err)
 | 
			
		||||
@ -384,6 +386,7 @@ func GetHeadscaleConfig() (*Config, error) {
 | 
			
		||||
	dnsConfig, baseDomain := GetDNSConfig()
 | 
			
		||||
	derpConfig := GetDERPConfig()
 | 
			
		||||
	logConfig := GetLogTailConfig()
 | 
			
		||||
	randomizeClientPort := viper.GetBool("randomize_client_port")
 | 
			
		||||
 | 
			
		||||
	configuredPrefixes := viper.GetStringSlice("ip_prefixes")
 | 
			
		||||
	parsedPrefixes := make([]netaddr.IPPrefix, 0, len(configuredPrefixes)+1)
 | 
			
		||||
@ -489,7 +492,8 @@ func GetHeadscaleConfig() (*Config, error) {
 | 
			
		||||
			StripEmaildomain: viper.GetBool("oidc.strip_email_domain"),
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		LogTail: logConfig,
 | 
			
		||||
		LogTail:             logConfig,
 | 
			
		||||
		RandomizeClientPort: randomizeClientPort,
 | 
			
		||||
 | 
			
		||||
		CLI: CLIConfig{
 | 
			
		||||
			Address:  viper.GetString("cli.address"),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								docs/acls.md
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								docs/acls.md
									
									
									
									
									
								
							@ -33,7 +33,7 @@ Note: Namespaces will be created automatically when users authenticate with the
 | 
			
		||||
Headscale server.
 | 
			
		||||
 | 
			
		||||
ACLs could be written either on [huJSON](https://github.com/tailscale/hujson)
 | 
			
		||||
or Yaml. Check the [test ACLs](../tests/acls) for further information.
 | 
			
		||||
or YAML. Check the [test ACLs](../tests/acls) for further information.
 | 
			
		||||
 | 
			
		||||
When registering the servers we will need to add the flag
 | 
			
		||||
`--advertised-tags=tag:<tag1>,tag:<tag2>`, and the user (namespace) that is
 | 
			
		||||
@ -83,8 +83,8 @@ Here are the ACL's to implement the same permissions as above:
 | 
			
		||||
    // boss have access to all servers
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["group:boss"],
 | 
			
		||||
      "ports": [
 | 
			
		||||
      "src": ["group:boss"],
 | 
			
		||||
      "dst": [
 | 
			
		||||
        "tag:prod-databases:*",
 | 
			
		||||
        "tag:prod-app-servers:*",
 | 
			
		||||
        "tag:internal:*",
 | 
			
		||||
@ -93,11 +93,12 @@ Here are the ACL's to implement the same permissions as above:
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // admin have only access to administrative ports of the servers
 | 
			
		||||
    // admin have only access to administrative ports of the servers, in tcp/22
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["group:admin"],
 | 
			
		||||
      "ports": [
 | 
			
		||||
      "src": ["group:admin"],
 | 
			
		||||
      "proto": "tcp",
 | 
			
		||||
      "dst": [
 | 
			
		||||
        "tag:prod-databases:22",
 | 
			
		||||
        "tag:prod-app-servers:22",
 | 
			
		||||
        "tag:internal:22",
 | 
			
		||||
@ -106,12 +107,26 @@ Here are the ACL's to implement the same permissions as above:
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // we also allow admin to ping the servers
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "src": ["group:admin"],
 | 
			
		||||
      "proto": "icmp",
 | 
			
		||||
      "dst": [
 | 
			
		||||
        "tag:prod-databases:*",
 | 
			
		||||
        "tag:prod-app-servers:*",
 | 
			
		||||
        "tag:internal:*",
 | 
			
		||||
        "tag:dev-databases:*",
 | 
			
		||||
        "tag:dev-app-servers:*"
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // developers have access to databases servers and application servers on all ports
 | 
			
		||||
    // they can only view the applications servers in prod and have no access to databases servers in production
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["group:dev"],
 | 
			
		||||
      "ports": [
 | 
			
		||||
      "src": ["group:dev"],
 | 
			
		||||
      "dst": [
 | 
			
		||||
        "tag:dev-databases:*",
 | 
			
		||||
        "tag:dev-app-servers:*",
 | 
			
		||||
        "tag:prod-app-servers:80,443"
 | 
			
		||||
@ -124,37 +139,38 @@ Here are the ACL's to implement the same permissions as above:
 | 
			
		||||
    // https://github.com/juanfont/headscale/issues/502
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["group:dev"],
 | 
			
		||||
      "ports": ["10.20.0.0/16:443,5432", "router.internal:0"]
 | 
			
		||||
      "src": ["group:dev"],
 | 
			
		||||
      "dst": ["10.20.0.0/16:443,5432", "router.internal:0"]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // servers should be able to talk to database. Database should not be able to initiate connections to
 | 
			
		||||
    // servers should be able to talk to database in tcp/5432. Database should not be able to initiate connections to
 | 
			
		||||
    // applications servers
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["tag:dev-app-servers"],
 | 
			
		||||
      "ports": ["tag:dev-databases:5432"]
 | 
			
		||||
      "src": ["tag:dev-app-servers"],
 | 
			
		||||
      "proto": "tcp",
 | 
			
		||||
      "dst": ["tag:dev-databases:5432"]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["tag:prod-app-servers"],
 | 
			
		||||
      "ports": ["tag:prod-databases:5432"]
 | 
			
		||||
      "src": ["tag:prod-app-servers"],
 | 
			
		||||
      "dst": ["tag:prod-databases:5432"]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // interns have access to dev-app-servers only in reading mode
 | 
			
		||||
    {
 | 
			
		||||
      "action": "accept",
 | 
			
		||||
      "users": ["group:intern"],
 | 
			
		||||
      "ports": ["tag:dev-app-servers:80,443"]
 | 
			
		||||
      "src": ["group:intern"],
 | 
			
		||||
      "dst": ["tag:dev-app-servers:80,443"]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // We still have to allow internal namespaces communications since nothing guarantees that each user have
 | 
			
		||||
    // their own namespaces.
 | 
			
		||||
    { "action": "accept", "users": ["boss"], "ports": ["boss:*"] },
 | 
			
		||||
    { "action": "accept", "users": ["dev1"], "ports": ["dev1:*"] },
 | 
			
		||||
    { "action": "accept", "users": ["dev2"], "ports": ["dev2:*"] },
 | 
			
		||||
    { "action": "accept", "users": ["admin1"], "ports": ["admin1:*"] },
 | 
			
		||||
    { "action": "accept", "users": ["intern1"], "ports": ["intern1:*"] }
 | 
			
		||||
    { "action": "accept", "src": ["boss"], "dst": ["boss:*"] },
 | 
			
		||||
    { "action": "accept", "src": ["dev1"], "dst": ["dev1:*"] },
 | 
			
		||||
    { "action": "accept", "src": ["dev2"], "dst": ["dev2:*"] },
 | 
			
		||||
    { "action": "accept", "src": ["admin1"], "dst": ["admin1:*"] },
 | 
			
		||||
    { "action": "accept", "src": ["intern1"], "dst": ["intern1:*"] }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -43,4 +43,4 @@ tls_letsencrypt_cache_dir: /var/www/.cache
 | 
			
		||||
tls_letsencrypt_challenge_type: HTTP-01
 | 
			
		||||
unix_socket: /var/run/headscale.sock
 | 
			
		||||
unix_socket_permission: "0o770"
 | 
			
		||||
 | 
			
		||||
randomize_client_port: false
 | 
			
		||||
 | 
			
		||||
@ -43,4 +43,4 @@ tls_letsencrypt_cache_dir: /var/www/.cache
 | 
			
		||||
tls_letsencrypt_challenge_type: HTTP-01
 | 
			
		||||
unix_socket: /var/run/headscale.sock
 | 
			
		||||
unix_socket_permission: "0o770"
 | 
			
		||||
 | 
			
		||||
randomize_client_port: false
 | 
			
		||||
 | 
			
		||||
@ -188,8 +188,8 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
 | 
			
		||||
		Hosts:     map[string]netaddr.IPPrefix{},
 | 
			
		||||
		TagOwners: map[string][]string{},
 | 
			
		||||
		ACLs: []ACL{
 | 
			
		||||
			{Action: "accept", Users: []string{"admin"}, Ports: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Users: []string{"test"}, Ports: []string{"test:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"admin"}, Destinations: []string{"*:*"}},
 | 
			
		||||
			{Action: "accept", Sources: []string{"test"}, Destinations: []string{"test:*"}},
 | 
			
		||||
		},
 | 
			
		||||
		Tests: []ACLTest{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    // Declare static groups of users beyond those in the identity service.
 | 
			
		||||
    "Groups": {
 | 
			
		||||
    "groups": {
 | 
			
		||||
        "group:example": [
 | 
			
		||||
            "user1@example.com",
 | 
			
		||||
            "user2@example.com",
 | 
			
		||||
@ -11,12 +11,12 @@
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    // Declare hostname aliases to use in place of IP addresses or subnets.
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "example-host-1": "100.100.100.100",
 | 
			
		||||
        "example-host-2": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
    // Define who is allowed to use which tags.
 | 
			
		||||
    "TagOwners": {
 | 
			
		||||
    "tagOwners": {
 | 
			
		||||
        // Everyone in the montreal-admins or global-admins group are
 | 
			
		||||
        // allowed to tag servers as montreal-webserver.
 | 
			
		||||
        "tag:montreal-webserver": [
 | 
			
		||||
@ -29,17 +29,17 @@
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    // Access control lists.
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        // Engineering users, plus the president, can access port 22 (ssh)
 | 
			
		||||
        // and port 3389 (remote desktop protocol) on all servers, and all
 | 
			
		||||
        // ports on git-server or ci-server.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:example2",
 | 
			
		||||
                "192.168.1.0/24"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "*:22,3389",
 | 
			
		||||
                "git-server:*",
 | 
			
		||||
                "ci-server:*"
 | 
			
		||||
@ -48,22 +48,22 @@
 | 
			
		||||
        // Allow engineer users to access any port on a device tagged with
 | 
			
		||||
        // tag:production.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:example"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:production:*"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        // Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
 | 
			
		||||
        // on both networks.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "example-host-2", 
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "example-host-1:*",
 | 
			
		||||
                "192.168.1.0/24:*"
 | 
			
		||||
            ],
 | 
			
		||||
@ -72,22 +72,22 @@
 | 
			
		||||
        // Comment out this section if you want to define specific ACL
 | 
			
		||||
        // restrictions above.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "*:*"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        // All users in Montreal are allowed to access the Montreal web
 | 
			
		||||
        // servers.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "example-host-1"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:montreal-webserver:80,443"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
@ -96,30 +96,30 @@
 | 
			
		||||
        // In contrast, this doesn't grant API servers the right to initiate
 | 
			
		||||
        // any connections.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "tag:montreal-webserver"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:api-server:443"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    // Declare tests to check functionality of ACL rules
 | 
			
		||||
    "Tests": [
 | 
			
		||||
    "tests": [
 | 
			
		||||
        {
 | 
			
		||||
            "User": "user1@example.com",
 | 
			
		||||
            "Allow": [
 | 
			
		||||
            "src": "user1@example.com",
 | 
			
		||||
            "accept": [
 | 
			
		||||
                "example-host-1:22",
 | 
			
		||||
                "example-host-2:80"
 | 
			
		||||
            ],
 | 
			
		||||
            "Deny": [
 | 
			
		||||
            "deny": [
 | 
			
		||||
                "exapmle-host-2:100"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "User": "user2@example.com",
 | 
			
		||||
            "Allow": [
 | 
			
		||||
            "src": "user2@example.com",
 | 
			
		||||
            "accept": [
 | 
			
		||||
                "100.60.3.4:22"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -3,19 +3,19 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "subnet-1",
 | 
			
		||||
                "192.168.1.0/24"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "*:22,3389",
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,24 @@
 | 
			
		||||
// This ACL is used to test group expansion
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "Groups": {
 | 
			
		||||
    "groups": {
 | 
			
		||||
        "group:example": [
 | 
			
		||||
            "testnamespace",
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:example",
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,18 @@
 | 
			
		||||
// This ACL is used to test namespace expansion
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "testnamespace",
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										41
									
								
								tests/acls/acl_policy_basic_protocols.hujson
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/acls/acl_policy_basic_protocols.hujson
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
// This ACL is used to test wildcards
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*",
 | 
			
		||||
            ],
 | 
			
		||||
            "proto": "tcp",
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*",
 | 
			
		||||
            ],
 | 
			
		||||
            "proto": "udp",
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:53",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*",
 | 
			
		||||
            ],
 | 
			
		||||
            "proto": "icmp",
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
}
 | 
			
		||||
@ -1,18 +1,18 @@
 | 
			
		||||
// This ACL is used to test the port range expansion
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "subnet-1",
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:5400-5500",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,18 @@
 | 
			
		||||
// This ACL is used to test wildcards
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "host-1": "100.100.100.100",
 | 
			
		||||
        "subnet-1": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*",
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "host-1:*",
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
---
 | 
			
		||||
Hosts:
 | 
			
		||||
hosts:
 | 
			
		||||
  host-1: 100.100.100.100/32
 | 
			
		||||
  subnet-1: 100.100.101.100/24
 | 
			
		||||
ACLs:
 | 
			
		||||
  - Action: accept
 | 
			
		||||
    Users:
 | 
			
		||||
acls:
 | 
			
		||||
  - action: accept
 | 
			
		||||
    src:
 | 
			
		||||
      - "*"
 | 
			
		||||
    Ports:
 | 
			
		||||
    dst:
 | 
			
		||||
      - host-1:*
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,18 @@
 | 
			
		||||
{
 | 
			
		||||
    // Declare static groups of users beyond those in the identity service.
 | 
			
		||||
    "Groups": {
 | 
			
		||||
    "groups": {
 | 
			
		||||
        "group:example": [
 | 
			
		||||
            "user1@example.com",
 | 
			
		||||
            "user2@example.com",
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    // Declare hostname aliases to use in place of IP addresses or subnets.
 | 
			
		||||
    "Hosts": {
 | 
			
		||||
    "hosts": {
 | 
			
		||||
        "example-host-1": "100.100.100.100",
 | 
			
		||||
        "example-host-2": "100.100.101.100/24",
 | 
			
		||||
    },
 | 
			
		||||
    // Define who is allowed to use which tags.
 | 
			
		||||
    "TagOwners": {
 | 
			
		||||
    "tagOwners": {
 | 
			
		||||
        // Everyone in the montreal-admins or global-admins group are
 | 
			
		||||
        // allowed to tag servers as montreal-webserver.
 | 
			
		||||
        "tag:montreal-webserver": [
 | 
			
		||||
@ -26,17 +26,17 @@
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    // Access control lists.
 | 
			
		||||
    "ACLs": [
 | 
			
		||||
    "acls": [
 | 
			
		||||
        // Engineering users, plus the president, can access port 22 (ssh)
 | 
			
		||||
        // and port 3389 (remote desktop protocol) on all servers, and all
 | 
			
		||||
        // ports on git-server or ci-server.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:engineering",
 | 
			
		||||
                "president@example.com"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "*:22,3389",
 | 
			
		||||
                "git-server:*",
 | 
			
		||||
                "ci-server:*"
 | 
			
		||||
@ -45,23 +45,23 @@
 | 
			
		||||
        // Allow engineer users to access any port on a device tagged with
 | 
			
		||||
        // tag:production.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:engineers"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:production:*"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        // Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
 | 
			
		||||
        // on both networks.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "my-subnet",
 | 
			
		||||
                "192.168.1.0/24"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "my-subnet:*",
 | 
			
		||||
                "192.168.1.0/24:*"
 | 
			
		||||
            ],
 | 
			
		||||
@ -70,22 +70,22 @@
 | 
			
		||||
        // Comment out this section if you want to define specific ACL
 | 
			
		||||
        // restrictions above.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "*"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "*:*"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        // All users in Montreal are allowed to access the Montreal web
 | 
			
		||||
        // servers.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "group:montreal-users"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:montreal-webserver:80,443"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
@ -94,30 +94,30 @@
 | 
			
		||||
        // In contrast, this doesn't grant API servers the right to initiate
 | 
			
		||||
        // any connections.
 | 
			
		||||
        {
 | 
			
		||||
            "Action": "accept",
 | 
			
		||||
            "Users": [
 | 
			
		||||
            "action": "accept",
 | 
			
		||||
            "src": [
 | 
			
		||||
                "tag:montreal-webserver"
 | 
			
		||||
            ],
 | 
			
		||||
            "Ports": [
 | 
			
		||||
            "dst": [
 | 
			
		||||
                "tag:api-server:443"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    // Declare tests to check functionality of ACL rules
 | 
			
		||||
    "Tests": [
 | 
			
		||||
    "tests": [
 | 
			
		||||
        {
 | 
			
		||||
            "User": "user1@example.com",
 | 
			
		||||
            "Allow": [
 | 
			
		||||
            "src": "user1@example.com",
 | 
			
		||||
            "accept": [
 | 
			
		||||
                "example-host-1:22",
 | 
			
		||||
                "example-host-2:80"
 | 
			
		||||
            ],
 | 
			
		||||
            "Deny": [
 | 
			
		||||
            "deny": [
 | 
			
		||||
                "exapmle-host-2:100"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "User": "user2@example.com",
 | 
			
		||||
            "Allow": [
 | 
			
		||||
            "src": "user2@example.com",
 | 
			
		||||
            "accept": [
 | 
			
		||||
                "100.60.3.4:22"
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user