Merge branch 'main' into test-126
This commit is contained in:
commit
a058f17946
21 changed files with 349 additions and 164 deletions
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## 0.16.0 (2022-xx-xx)
|
## 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
|
### Changes
|
||||||
|
|
||||||
- **Drop** armhf (32-bit ARM) support. [#609](https://github.com/juanfont/headscale/pull/609)
|
- **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
|
- 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)
|
- 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)
|
- 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 -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)
|
## 0.15.0 (2022-03-20)
|
||||||
|
|
||||||
|
|
109
acls.go
109
acls.go
|
@ -23,6 +23,7 @@ const (
|
||||||
errInvalidGroup = Error("invalid group")
|
errInvalidGroup = Error("invalid group")
|
||||||
errInvalidTag = Error("invalid tag")
|
errInvalidTag = Error("invalid tag")
|
||||||
errInvalidPortFormat = Error("invalid port format")
|
errInvalidPortFormat = Error("invalid port format")
|
||||||
|
errWildcardIsNeeded = Error("wildcard as port is required for the protocol")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -36,6 +37,23 @@ const (
|
||||||
expectedTokenItems = 2
|
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.
|
// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules.
|
||||||
func (h *Headscale) LoadACLPolicy(path string) error {
|
func (h *Headscale) LoadACLPolicy(path string) error {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
|
@ -123,23 +141,31 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIPs := []string{}
|
srcIPs := []string{}
|
||||||
for innerIndex, user := range acl.Users {
|
for innerIndex, src := range acl.Sources {
|
||||||
srcs, err := h.generateACLPolicySrcIP(machines, *h.aclPolicy, user)
|
srcs, err := h.generateACLPolicySrcIP(machines, *h.aclPolicy, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Msgf("Error parsing ACL %d, User %d", index, innerIndex)
|
Msgf("Error parsing ACL %d, Source %d", index, innerIndex)
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
srcIPs = append(srcIPs, srcs...)
|
srcIPs = append(srcIPs, srcs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
destPorts := []tailcfg.NetPortRange{}
|
protocols, needsWildcard, err := parseProtocol(acl.Protocol)
|
||||||
for innerIndex, ports := range acl.Ports {
|
|
||||||
dests, err := h.generateACLPolicyDestPorts(machines, *h.aclPolicy, ports)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Msgf("Error parsing ACL %d, Port %d", index, innerIndex)
|
Msgf("Error parsing ACL %d. protocol unknown %s", index, acl.Protocol)
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
destPorts := []tailcfg.NetPortRange{}
|
||||||
|
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, Destination %d", index, innerIndex)
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -149,6 +175,7 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
|
||||||
rules = append(rules, tailcfg.FilterRule{
|
rules = append(rules, tailcfg.FilterRule{
|
||||||
SrcIPs: srcIPs,
|
SrcIPs: srcIPs,
|
||||||
DstPorts: destPorts,
|
DstPorts: destPorts,
|
||||||
|
IPProto: protocols,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,17 +185,18 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
|
||||||
func (h *Headscale) generateACLPolicySrcIP(
|
func (h *Headscale) generateACLPolicySrcIP(
|
||||||
machines []Machine,
|
machines []Machine,
|
||||||
aclPolicy ACLPolicy,
|
aclPolicy ACLPolicy,
|
||||||
u string,
|
src string,
|
||||||
) ([]string, error) {
|
) ([]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,
|
machines []Machine,
|
||||||
aclPolicy ACLPolicy,
|
aclPolicy ACLPolicy,
|
||||||
d string,
|
dest string,
|
||||||
|
needsWildcard bool,
|
||||||
) ([]tailcfg.NetPortRange, error) {
|
) ([]tailcfg.NetPortRange, error) {
|
||||||
tokens := strings.Split(d, ":")
|
tokens := strings.Split(dest, ":")
|
||||||
if len(tokens) < expectedTokenItems || len(tokens) > 3 {
|
if len(tokens) < expectedTokenItems || len(tokens) > 3 {
|
||||||
return nil, errInvalidPortFormat
|
return nil, errInvalidPortFormat
|
||||||
}
|
}
|
||||||
|
@ -195,7 +223,7 @@ func (h *Headscale) generateACLPolicyDestPorts(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ports, err := expandPorts(tokens[len(tokens)-1])
|
ports, err := expandPorts(tokens[len(tokens)-1], needsWildcard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -214,6 +242,54 @@ func (h *Headscale) generateACLPolicyDestPorts(
|
||||||
return dests, nil
|
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
|
// expandalias has an input of either
|
||||||
// - a namespace
|
// - a namespace
|
||||||
// - a group
|
// - a group
|
||||||
|
@ -268,6 +344,7 @@ func expandAlias(
|
||||||
alias,
|
alias,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
} else {
|
} else {
|
||||||
return ips, err
|
return ips, err
|
||||||
|
@ -359,13 +436,17 @@ func excludeCorrectlyTaggedNodes(
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) {
|
func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) {
|
||||||
if portsStr == "*" {
|
if portsStr == "*" {
|
||||||
return &[]tailcfg.PortRange{
|
return &[]tailcfg.PortRange{
|
||||||
{First: portRangeBegin, Last: portRangeEnd},
|
{First: portRangeBegin, Last: portRangeEnd},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if needsWildcard {
|
||||||
|
return nil, errWildcardIsNeeded
|
||||||
|
}
|
||||||
|
|
||||||
ports := []tailcfg.PortRange{}
|
ports := []tailcfg.PortRange{}
|
||||||
for _, portStr := range strings.Split(portsStr, ",") {
|
for _, portStr := range strings.Split(portsStr, ",") {
|
||||||
rang := strings.Split(portStr, "-")
|
rang := strings.Split(portStr, "-")
|
||||||
|
|
75
acls_test.go
75
acls_test.go
|
@ -62,7 +62,7 @@ func (s *Suite) TestBasicRule(c *check.C) {
|
||||||
func (s *Suite) TestInvalidAction(c *check.C) {
|
func (s *Suite) TestInvalidAction(c *check.C) {
|
||||||
app.aclPolicy = &ACLPolicy{
|
app.aclPolicy = &ACLPolicy{
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "invalidAction", Users: []string{"*"}, Ports: []string{"*:*"}},
|
{Action: "invalidAction", Sources: []string{"*"}, Destinations: []string{"*:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := app.UpdateACLRules()
|
err := app.UpdateACLRules()
|
||||||
|
@ -70,14 +70,14 @@ func (s *Suite) TestInvalidAction(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestInvalidGroupInGroup(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{
|
app.aclPolicy = &ACLPolicy{
|
||||||
Groups: Groups{
|
Groups: Groups{
|
||||||
"group:test": []string{"foo"},
|
"group:test": []string{"foo"},
|
||||||
"group:error": []string{"foo", "group:test"},
|
"group:error": []string{"foo", "group:test"},
|
||||||
},
|
},
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"group:error"}, Ports: []string{"*:*"}},
|
{Action: "accept", Sources: []string{"group:error"}, Destinations: []string{"*:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := app.UpdateACLRules()
|
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
|
// this ACL is wrong because no tagOwners own the requested tag for the server
|
||||||
app.aclPolicy = &ACLPolicy{
|
app.aclPolicy = &ACLPolicy{
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"tag:foo"}, Ports: []string{"*:*"}},
|
{Action: "accept", Sources: []string{"tag:foo"}, Destinations: []string{"*:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := app.UpdateACLRules()
|
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
|
// 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.
|
// 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.
|
// the tag is matched in the Sources section.
|
||||||
func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
|
func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) {
|
||||||
namespace, err := app.CreateNamespace("user1")
|
namespace, err := app.CreateNamespace("user1")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
|
||||||
Groups: Groups{"group:test": []string{"user1", "user2"}},
|
Groups: Groups{"group:test": []string{"user1", "user2"}},
|
||||||
TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
|
TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"tag:test"}, Ports: []string{"*:*"}},
|
{Action: "accept", Sources: []string{"tag:test"}, Destinations: []string{"*:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = app.UpdateACLRules()
|
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
|
// 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.
|
// 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.
|
// the tag is matched in the Destinations section.
|
||||||
func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
|
func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) {
|
||||||
namespace, err := app.CreateNamespace("user1")
|
namespace, err := app.CreateNamespace("user1")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
|
||||||
Groups: Groups{"group:test": []string{"user1", "user2"}},
|
Groups: Groups{"group:test": []string{"user1", "user2"}},
|
||||||
TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
|
TagOwners: TagOwners{"tag:test": []string{"user3", "group:test"}},
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"*"}, Ports: []string{"tag:test:*"}},
|
{Action: "accept", Sources: []string{"*"}, Destinations: []string{"tag:test:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = app.UpdateACLRules()
|
err = app.UpdateACLRules()
|
||||||
|
@ -222,7 +222,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
|
||||||
app.aclPolicy = &ACLPolicy{
|
app.aclPolicy = &ACLPolicy{
|
||||||
TagOwners: TagOwners{"tag:test": []string{"user1"}},
|
TagOwners: TagOwners{"tag:test": []string{"user1"}},
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"user1"}, Ports: []string{"*:*"}},
|
{Action: "accept", Sources: []string{"user1"}, Destinations: []string{"*:*"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = app.UpdateACLRules()
|
err = app.UpdateACLRules()
|
||||||
|
@ -288,8 +288,8 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{
|
{
|
||||||
Action: "accept",
|
Action: "accept",
|
||||||
Users: []string{"user1"},
|
Sources: []string{"user1"},
|
||||||
Ports: []string{"tag:webapp:80,443"},
|
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))
|
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) {
|
func (s *Suite) TestPortWildcard(c *check.C) {
|
||||||
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson")
|
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
@ -629,6 +643,7 @@ func Test_expandTagOwners(t *testing.T) {
|
||||||
func Test_expandPorts(t *testing.T) {
|
func Test_expandPorts(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
portsStr string
|
portsStr string
|
||||||
|
needsWildcard bool
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -638,15 +653,29 @@ func Test_expandPorts(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "wildcard",
|
name: "wildcard",
|
||||||
args: args{portsStr: "*"},
|
args: args{portsStr: "*", needsWildcard: true},
|
||||||
want: &[]tailcfg.PortRange{
|
want: &[]tailcfg.PortRange{
|
||||||
{First: portRangeBegin, Last: portRangeEnd},
|
{First: portRangeBegin, Last: portRangeEnd},
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "two ports",
|
name: "needs wildcard but does not require it",
|
||||||
args: args{portsStr: "80,443"},
|
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{
|
want: &[]tailcfg.PortRange{
|
||||||
{First: 80, Last: 80},
|
{First: 80, Last: 80},
|
||||||
{First: 443, Last: 443},
|
{First: 443, Last: 443},
|
||||||
|
@ -655,7 +684,7 @@ func Test_expandPorts(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "a range and a port",
|
name: "a range and a port",
|
||||||
args: args{portsStr: "80-1024,443"},
|
args: args{portsStr: "80-1024,443", needsWildcard: false},
|
||||||
want: &[]tailcfg.PortRange{
|
want: &[]tailcfg.PortRange{
|
||||||
{First: 80, Last: 1024},
|
{First: 80, Last: 1024},
|
||||||
{First: 443, Last: 443},
|
{First: 443, Last: 443},
|
||||||
|
@ -664,38 +693,38 @@ func Test_expandPorts(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "out of bounds",
|
name: "out of bounds",
|
||||||
args: args{portsStr: "854038"},
|
args: args{portsStr: "854038", needsWildcard: false},
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong port",
|
name: "wrong port",
|
||||||
args: args{portsStr: "85a38"},
|
args: args{portsStr: "85a38", needsWildcard: false},
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong port in first",
|
name: "wrong port in first",
|
||||||
args: args{portsStr: "a-80"},
|
args: args{portsStr: "a-80", needsWildcard: false},
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong port in last",
|
name: "wrong port in last",
|
||||||
args: args{portsStr: "80-85a38"},
|
args: args{portsStr: "80-85a38", needsWildcard: false},
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong port format",
|
name: "wrong port format",
|
||||||
args: args{portsStr: "80-85a38-3"},
|
args: args{portsStr: "80-85a38-3", needsWildcard: false},
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
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 {
|
if (err != nil) != test.wantErr {
|
||||||
t.Errorf("expandPorts() error = %v, wantErr %v", err, test.wantErr)
|
t.Errorf("expandPorts() error = %v, wantErr %v", err, test.wantErr)
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,19 @@ import (
|
||||||
|
|
||||||
// ACLPolicy represents a Tailscale ACL Policy.
|
// ACLPolicy represents a Tailscale ACL Policy.
|
||||||
type ACLPolicy struct {
|
type ACLPolicy struct {
|
||||||
Groups Groups `json:"Groups" yaml:"Groups"`
|
Groups Groups `json:"groups" yaml:"groups"`
|
||||||
Hosts Hosts `json:"Hosts" yaml:"Hosts"`
|
Hosts Hosts `json:"hosts" yaml:"hosts"`
|
||||||
TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"`
|
TagOwners TagOwners `json:"tagOwners" yaml:"tagOwners"`
|
||||||
ACLs []ACL `json:"ACLs" yaml:"ACLs"`
|
ACLs []ACL `json:"acls" yaml:"acls"`
|
||||||
Tests []ACLTest `json:"Tests" yaml:"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" yaml:"Action"`
|
Action string `json:"action" yaml:"action"`
|
||||||
Users []string `json:"Users" yaml:"Users"`
|
Protocol string `json:"proto" yaml:"proto"`
|
||||||
Ports []string `json:"Ports" yaml:"Ports"`
|
Sources []string `json:"src" yaml:"src"`
|
||||||
|
Destinations []string `json:"dst" yaml:"dst"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Groups references a series of alias in the ACL rules.
|
// 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.
|
// 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" yaml:"User"`
|
Source string `json:"src" yaml:"src"`
|
||||||
Allow []string `json:"Allow" yaml:"Allow"`
|
Accept []string `json:"accept" yaml:"accept"`
|
||||||
Deny []string `json:"Deny,omitempty" yaml:"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.
|
||||||
|
|
1
api.go
1
api.go
|
@ -280,6 +280,7 @@ func (h *Headscale) getMapResponse(
|
||||||
UserProfiles: profiles,
|
UserProfiles: profiles,
|
||||||
Debug: &tailcfg.Debug{
|
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),
|
fs.FileMode(0o770),
|
||||||
)
|
)
|
||||||
c.Assert(viper.GetBool("logtail.enabled"), check.Equals, false)
|
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) {
|
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
|
# 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.
|
# disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
|
||||||
enabled: false
|
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
|
||||||
|
|
|
@ -55,6 +55,7 @@ type Config struct {
|
||||||
OIDC OIDCConfig
|
OIDC OIDCConfig
|
||||||
|
|
||||||
LogTail LogTailConfig
|
LogTail LogTailConfig
|
||||||
|
RandomizeClientPort bool
|
||||||
|
|
||||||
CLI CLIConfig
|
CLI CLIConfig
|
||||||
|
|
||||||
|
@ -157,6 +158,7 @@ func LoadConfig(path string, isFile bool) error {
|
||||||
viper.SetDefault("oidc.strip_email_domain", true)
|
viper.SetDefault("oidc.strip_email_domain", true)
|
||||||
|
|
||||||
viper.SetDefault("logtail.enabled", false)
|
viper.SetDefault("logtail.enabled", false)
|
||||||
|
viper.SetDefault("randomize_client_port", false)
|
||||||
|
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
return fmt.Errorf("fatal error reading config file: %w", err)
|
return fmt.Errorf("fatal error reading config file: %w", err)
|
||||||
|
@ -384,6 +386,7 @@ func GetHeadscaleConfig() (*Config, error) {
|
||||||
dnsConfig, baseDomain := GetDNSConfig()
|
dnsConfig, baseDomain := GetDNSConfig()
|
||||||
derpConfig := GetDERPConfig()
|
derpConfig := GetDERPConfig()
|
||||||
logConfig := GetLogTailConfig()
|
logConfig := GetLogTailConfig()
|
||||||
|
randomizeClientPort := viper.GetBool("randomize_client_port")
|
||||||
|
|
||||||
configuredPrefixes := viper.GetStringSlice("ip_prefixes")
|
configuredPrefixes := viper.GetStringSlice("ip_prefixes")
|
||||||
parsedPrefixes := make([]netaddr.IPPrefix, 0, len(configuredPrefixes)+1)
|
parsedPrefixes := make([]netaddr.IPPrefix, 0, len(configuredPrefixes)+1)
|
||||||
|
@ -490,6 +493,7 @@ func GetHeadscaleConfig() (*Config, error) {
|
||||||
},
|
},
|
||||||
|
|
||||||
LogTail: logConfig,
|
LogTail: logConfig,
|
||||||
|
RandomizeClientPort: randomizeClientPort,
|
||||||
|
|
||||||
CLI: CLIConfig{
|
CLI: CLIConfig{
|
||||||
Address: viper.GetString("cli.address"),
|
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.
|
Headscale server.
|
||||||
|
|
||||||
ACLs could be written either on [huJSON](https://github.com/tailscale/hujson)
|
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
|
When registering the servers we will need to add the flag
|
||||||
`--advertised-tags=tag:<tag1>,tag:<tag2>`, and the user (namespace) that is
|
`--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
|
// boss have access to all servers
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["group:boss"],
|
"src": ["group:boss"],
|
||||||
"ports": [
|
"dst": [
|
||||||
"tag:prod-databases:*",
|
"tag:prod-databases:*",
|
||||||
"tag:prod-app-servers:*",
|
"tag:prod-app-servers:*",
|
||||||
"tag:internal:*",
|
"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",
|
"action": "accept",
|
||||||
"users": ["group:admin"],
|
"src": ["group:admin"],
|
||||||
"ports": [
|
"proto": "tcp",
|
||||||
|
"dst": [
|
||||||
"tag:prod-databases:22",
|
"tag:prod-databases:22",
|
||||||
"tag:prod-app-servers:22",
|
"tag:prod-app-servers:22",
|
||||||
"tag:internal: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
|
// 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
|
// they can only view the applications servers in prod and have no access to databases servers in production
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["group:dev"],
|
"src": ["group:dev"],
|
||||||
"ports": [
|
"dst": [
|
||||||
"tag:dev-databases:*",
|
"tag:dev-databases:*",
|
||||||
"tag:dev-app-servers:*",
|
"tag:dev-app-servers:*",
|
||||||
"tag:prod-app-servers:80,443"
|
"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
|
// https://github.com/juanfont/headscale/issues/502
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["group:dev"],
|
"src": ["group:dev"],
|
||||||
"ports": ["10.20.0.0/16:443,5432", "router.internal:0"]
|
"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
|
// applications servers
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["tag:dev-app-servers"],
|
"src": ["tag:dev-app-servers"],
|
||||||
"ports": ["tag:dev-databases:5432"]
|
"proto": "tcp",
|
||||||
|
"dst": ["tag:dev-databases:5432"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["tag:prod-app-servers"],
|
"src": ["tag:prod-app-servers"],
|
||||||
"ports": ["tag:prod-databases:5432"]
|
"dst": ["tag:prod-databases:5432"]
|
||||||
},
|
},
|
||||||
|
|
||||||
// interns have access to dev-app-servers only in reading mode
|
// interns have access to dev-app-servers only in reading mode
|
||||||
{
|
{
|
||||||
"action": "accept",
|
"action": "accept",
|
||||||
"users": ["group:intern"],
|
"src": ["group:intern"],
|
||||||
"ports": ["tag:dev-app-servers:80,443"]
|
"dst": ["tag:dev-app-servers:80,443"]
|
||||||
},
|
},
|
||||||
|
|
||||||
// We still have to allow internal namespaces communications since nothing guarantees that each user have
|
// We still have to allow internal namespaces communications since nothing guarantees that each user have
|
||||||
// their own namespaces.
|
// their own namespaces.
|
||||||
{ "action": "accept", "users": ["boss"], "ports": ["boss:*"] },
|
{ "action": "accept", "src": ["boss"], "dst": ["boss:*"] },
|
||||||
{ "action": "accept", "users": ["dev1"], "ports": ["dev1:*"] },
|
{ "action": "accept", "src": ["dev1"], "dst": ["dev1:*"] },
|
||||||
{ "action": "accept", "users": ["dev2"], "ports": ["dev2:*"] },
|
{ "action": "accept", "src": ["dev2"], "dst": ["dev2:*"] },
|
||||||
{ "action": "accept", "users": ["admin1"], "ports": ["admin1:*"] },
|
{ "action": "accept", "src": ["admin1"], "dst": ["admin1:*"] },
|
||||||
{ "action": "accept", "users": ["intern1"], "ports": ["intern1:*"] }
|
{ "action": "accept", "src": ["intern1"], "dst": ["intern1:*"] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -43,4 +43,4 @@ tls_letsencrypt_cache_dir: /var/www/.cache
|
||||||
tls_letsencrypt_challenge_type: HTTP-01
|
tls_letsencrypt_challenge_type: HTTP-01
|
||||||
unix_socket: /var/run/headscale.sock
|
unix_socket: /var/run/headscale.sock
|
||||||
unix_socket_permission: "0o770"
|
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
|
tls_letsencrypt_challenge_type: HTTP-01
|
||||||
unix_socket: /var/run/headscale.sock
|
unix_socket: /var/run/headscale.sock
|
||||||
unix_socket_permission: "0o770"
|
unix_socket_permission: "0o770"
|
||||||
|
randomize_client_port: false
|
||||||
|
|
|
@ -188,8 +188,8 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
|
||||||
Hosts: map[string]netaddr.IPPrefix{},
|
Hosts: map[string]netaddr.IPPrefix{},
|
||||||
TagOwners: map[string][]string{},
|
TagOwners: map[string][]string{},
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
{Action: "accept", Users: []string{"admin"}, Ports: []string{"*:*"}},
|
{Action: "accept", Sources: []string{"admin"}, Destinations: []string{"*:*"}},
|
||||||
{Action: "accept", Users: []string{"test"}, Ports: []string{"test:*"}},
|
{Action: "accept", Sources: []string{"test"}, Destinations: []string{"test:*"}},
|
||||||
},
|
},
|
||||||
Tests: []ACLTest{},
|
Tests: []ACLTest{},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
// Declare static groups of users beyond those in the identity service.
|
// Declare static groups of users beyond those in the identity service.
|
||||||
"Groups": {
|
"groups": {
|
||||||
"group:example": [
|
"group:example": [
|
||||||
"user1@example.com",
|
"user1@example.com",
|
||||||
"user2@example.com",
|
"user2@example.com",
|
||||||
|
@ -11,12 +11,12 @@
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Declare hostname aliases to use in place of IP addresses or subnets.
|
// Declare hostname aliases to use in place of IP addresses or subnets.
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"example-host-1": "100.100.100.100",
|
"example-host-1": "100.100.100.100",
|
||||||
"example-host-2": "100.100.101.100/24",
|
"example-host-2": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
// Define who is allowed to use which tags.
|
// Define who is allowed to use which tags.
|
||||||
"TagOwners": {
|
"tagOwners": {
|
||||||
// Everyone in the montreal-admins or global-admins group are
|
// Everyone in the montreal-admins or global-admins group are
|
||||||
// allowed to tag servers as montreal-webserver.
|
// allowed to tag servers as montreal-webserver.
|
||||||
"tag:montreal-webserver": [
|
"tag:montreal-webserver": [
|
||||||
|
@ -29,17 +29,17 @@
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Access control lists.
|
// Access control lists.
|
||||||
"ACLs": [
|
"acls": [
|
||||||
// Engineering users, plus the president, can access port 22 (ssh)
|
// Engineering users, plus the president, can access port 22 (ssh)
|
||||||
// and port 3389 (remote desktop protocol) on all servers, and all
|
// and port 3389 (remote desktop protocol) on all servers, and all
|
||||||
// ports on git-server or ci-server.
|
// ports on git-server or ci-server.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:example2",
|
"group:example2",
|
||||||
"192.168.1.0/24"
|
"192.168.1.0/24"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"*:22,3389",
|
"*:22,3389",
|
||||||
"git-server:*",
|
"git-server:*",
|
||||||
"ci-server:*"
|
"ci-server:*"
|
||||||
|
@ -48,22 +48,22 @@
|
||||||
// Allow engineer users to access any port on a device tagged with
|
// Allow engineer users to access any port on a device tagged with
|
||||||
// tag:production.
|
// tag:production.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:example"
|
"group:example"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:production:*"
|
"tag:production:*"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
|
// Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
|
||||||
// on both networks.
|
// on both networks.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"example-host-2",
|
"example-host-2",
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"example-host-1:*",
|
"example-host-1:*",
|
||||||
"192.168.1.0/24:*"
|
"192.168.1.0/24:*"
|
||||||
],
|
],
|
||||||
|
@ -72,22 +72,22 @@
|
||||||
// Comment out this section if you want to define specific ACL
|
// Comment out this section if you want to define specific ACL
|
||||||
// restrictions above.
|
// restrictions above.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"*"
|
"*"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"*:*"
|
"*:*"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// All users in Montreal are allowed to access the Montreal web
|
// All users in Montreal are allowed to access the Montreal web
|
||||||
// servers.
|
// servers.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"example-host-1"
|
"example-host-1"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:montreal-webserver:80,443"
|
"tag:montreal-webserver:80,443"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -96,30 +96,30 @@
|
||||||
// In contrast, this doesn't grant API servers the right to initiate
|
// In contrast, this doesn't grant API servers the right to initiate
|
||||||
// any connections.
|
// any connections.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"tag:montreal-webserver"
|
"tag:montreal-webserver"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:api-server:443"
|
"tag:api-server:443"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// Declare tests to check functionality of ACL rules
|
// Declare tests to check functionality of ACL rules
|
||||||
"Tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"User": "user1@example.com",
|
"src": "user1@example.com",
|
||||||
"Allow": [
|
"accept": [
|
||||||
"example-host-1:22",
|
"example-host-1:22",
|
||||||
"example-host-2:80"
|
"example-host-2:80"
|
||||||
],
|
],
|
||||||
"Deny": [
|
"deny": [
|
||||||
"exapmle-host-2:100"
|
"exapmle-host-2:100"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"User": "user2@example.com",
|
"src": "user2@example.com",
|
||||||
"Allow": [
|
"accept": [
|
||||||
"100.60.3.4:22"
|
"100.60.3.4:22"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"host-1": "100.100.100.100",
|
"host-1": "100.100.100.100",
|
||||||
"subnet-1": "100.100.101.100/24",
|
"subnet-1": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ACLs": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"subnet-1",
|
"subnet-1",
|
||||||
"192.168.1.0/24"
|
"192.168.1.0/24"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"*:22,3389",
|
"*:22,3389",
|
||||||
"host-1:*",
|
"host-1:*",
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
// This ACL is used to test group expansion
|
// This ACL is used to test group expansion
|
||||||
|
|
||||||
{
|
{
|
||||||
"Groups": {
|
"groups": {
|
||||||
"group:example": [
|
"group:example": [
|
||||||
"testnamespace",
|
"testnamespace",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"host-1": "100.100.100.100",
|
"host-1": "100.100.100.100",
|
||||||
"subnet-1": "100.100.101.100/24",
|
"subnet-1": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ACLs": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:example",
|
"group:example",
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"host-1:*",
|
"host-1:*",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
// This ACL is used to test namespace expansion
|
// This ACL is used to test namespace expansion
|
||||||
|
|
||||||
{
|
{
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"host-1": "100.100.100.100",
|
"host-1": "100.100.100.100",
|
||||||
"subnet-1": "100.100.101.100/24",
|
"subnet-1": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ACLs": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"testnamespace",
|
"testnamespace",
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"host-1:*",
|
"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
|
// This ACL is used to test the port range expansion
|
||||||
|
|
||||||
{
|
{
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"host-1": "100.100.100.100",
|
"host-1": "100.100.100.100",
|
||||||
"subnet-1": "100.100.101.100/24",
|
"subnet-1": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ACLs": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"subnet-1",
|
"subnet-1",
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"host-1:5400-5500",
|
"host-1:5400-5500",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
// This ACL is used to test wildcards
|
// This ACL is used to test wildcards
|
||||||
|
|
||||||
{
|
{
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"host-1": "100.100.100.100",
|
"host-1": "100.100.100.100",
|
||||||
"subnet-1": "100.100.101.100/24",
|
"subnet-1": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ACLs": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"Action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"*",
|
"*",
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"host-1:*",
|
"host-1:*",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
---
|
---
|
||||||
Hosts:
|
hosts:
|
||||||
host-1: 100.100.100.100/32
|
host-1: 100.100.100.100/32
|
||||||
subnet-1: 100.100.101.100/24
|
subnet-1: 100.100.101.100/24
|
||||||
ACLs:
|
acls:
|
||||||
- Action: accept
|
- action: accept
|
||||||
Users:
|
src:
|
||||||
- "*"
|
- "*"
|
||||||
Ports:
|
dst:
|
||||||
- host-1:*
|
- host-1:*
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
{
|
{
|
||||||
// Declare static groups of users beyond those in the identity service.
|
// Declare static groups of users beyond those in the identity service.
|
||||||
"Groups": {
|
"groups": {
|
||||||
"group:example": [
|
"group:example": [
|
||||||
"user1@example.com",
|
"user1@example.com",
|
||||||
"user2@example.com",
|
"user2@example.com",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Declare hostname aliases to use in place of IP addresses or subnets.
|
// Declare hostname aliases to use in place of IP addresses or subnets.
|
||||||
"Hosts": {
|
"hosts": {
|
||||||
"example-host-1": "100.100.100.100",
|
"example-host-1": "100.100.100.100",
|
||||||
"example-host-2": "100.100.101.100/24",
|
"example-host-2": "100.100.101.100/24",
|
||||||
},
|
},
|
||||||
// Define who is allowed to use which tags.
|
// Define who is allowed to use which tags.
|
||||||
"TagOwners": {
|
"tagOwners": {
|
||||||
// Everyone in the montreal-admins or global-admins group are
|
// Everyone in the montreal-admins or global-admins group are
|
||||||
// allowed to tag servers as montreal-webserver.
|
// allowed to tag servers as montreal-webserver.
|
||||||
"tag:montreal-webserver": [
|
"tag:montreal-webserver": [
|
||||||
|
@ -26,17 +26,17 @@
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Access control lists.
|
// Access control lists.
|
||||||
"ACLs": [
|
"acls": [
|
||||||
// Engineering users, plus the president, can access port 22 (ssh)
|
// Engineering users, plus the president, can access port 22 (ssh)
|
||||||
// and port 3389 (remote desktop protocol) on all servers, and all
|
// and port 3389 (remote desktop protocol) on all servers, and all
|
||||||
// ports on git-server or ci-server.
|
// ports on git-server or ci-server.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:engineering",
|
"group:engineering",
|
||||||
"president@example.com"
|
"president@example.com"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"*:22,3389",
|
"*:22,3389",
|
||||||
"git-server:*",
|
"git-server:*",
|
||||||
"ci-server:*"
|
"ci-server:*"
|
||||||
|
@ -45,23 +45,23 @@
|
||||||
// Allow engineer users to access any port on a device tagged with
|
// Allow engineer users to access any port on a device tagged with
|
||||||
// tag:production.
|
// tag:production.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:engineers"
|
"group:engineers"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:production:*"
|
"tag:production:*"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
|
// Allow servers in the my-subnet host and 192.168.1.0/24 to access hosts
|
||||||
// on both networks.
|
// on both networks.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"my-subnet",
|
"my-subnet",
|
||||||
"192.168.1.0/24"
|
"192.168.1.0/24"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"my-subnet:*",
|
"my-subnet:*",
|
||||||
"192.168.1.0/24:*"
|
"192.168.1.0/24:*"
|
||||||
],
|
],
|
||||||
|
@ -70,22 +70,22 @@
|
||||||
// Comment out this section if you want to define specific ACL
|
// Comment out this section if you want to define specific ACL
|
||||||
// restrictions above.
|
// restrictions above.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"*"
|
"*"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"*:*"
|
"*:*"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// All users in Montreal are allowed to access the Montreal web
|
// All users in Montreal are allowed to access the Montreal web
|
||||||
// servers.
|
// servers.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"group:montreal-users"
|
"group:montreal-users"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:montreal-webserver:80,443"
|
"tag:montreal-webserver:80,443"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -94,30 +94,30 @@
|
||||||
// In contrast, this doesn't grant API servers the right to initiate
|
// In contrast, this doesn't grant API servers the right to initiate
|
||||||
// any connections.
|
// any connections.
|
||||||
{
|
{
|
||||||
"Action": "accept",
|
"action": "accept",
|
||||||
"Users": [
|
"src": [
|
||||||
"tag:montreal-webserver"
|
"tag:montreal-webserver"
|
||||||
],
|
],
|
||||||
"Ports": [
|
"dst": [
|
||||||
"tag:api-server:443"
|
"tag:api-server:443"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// Declare tests to check functionality of ACL rules
|
// Declare tests to check functionality of ACL rules
|
||||||
"Tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"User": "user1@example.com",
|
"src": "user1@example.com",
|
||||||
"Allow": [
|
"accept": [
|
||||||
"example-host-1:22",
|
"example-host-1:22",
|
||||||
"example-host-2:80"
|
"example-host-2:80"
|
||||||
],
|
],
|
||||||
"Deny": [
|
"deny": [
|
||||||
"exapmle-host-2:100"
|
"exapmle-host-2:100"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"User": "user2@example.com",
|
"src": "user2@example.com",
|
||||||
"Allow": [
|
"accept": [
|
||||||
"100.60.3.4:22"
|
"100.60.3.4:22"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue