headscale/acls_test.go
2022-02-17 09:30:08 +01:00

387 lines
12 KiB
Go

package headscale
import (
"errors"
"gopkg.in/check.v1"
"gorm.io/datatypes"
"inet.af/netaddr"
)
func (s *Suite) TestWrongPath(c *check.C) {
err := app.LoadACLPolicy("asdfg")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestBrokenHuJson(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/broken.hujson")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestInvalidPolicyHuson(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/invalid.hujson")
c.Assert(err, check.NotNil)
c.Assert(err, check.Equals, errEmptyPolicy)
}
func (s *Suite) TestParseHosts(c *check.C) {
var hosts Hosts
err := hosts.UnmarshalJSON(
[]byte(
`{"example-host-1": "100.100.100.100","example-host-2": "100.100.101.100/24"}`,
),
)
c.Assert(hosts, check.NotNil)
c.Assert(err, check.IsNil)
}
func (s *Suite) TestParseInvalidCIDR(c *check.C) {
var hosts Hosts
err := hosts.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100/42"}`))
c.Assert(hosts, check.IsNil)
c.Assert(err, check.NotNil)
}
func (s *Suite) TestRuleInvalidGeneration(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_invalid.hujson")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestBasicRule(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_1.hujson")
c.Assert(err, check.IsNil)
rules, err := app.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
}
func (s *Suite) TestInvalidAction(c *check.C) {
app.aclPolicy = &ACLPolicy{
ACLs: []ACL{
{Action: "invalidAction", Users: []string{"*"}, Ports: []string{"*:*"}},
},
}
err := app.UpdateACLRules()
c.Assert(errors.Is(err, errInvalidAction), check.Equals, true)
}
func (s *Suite) TestInvalidGroupInGroup(c *check.C) {
// this ACL is wrong because the group in users 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{"*:*"}},
},
}
err := app.UpdateACLRules()
c.Assert(errors.Is(err, errInvalidGroup), check.Equals, true)
}
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{"*:*"}},
},
}
err := app.UpdateACLRules()
c.Assert(errors.Is(err, errInvalidTag), check.Equals, true)
}
// 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) {
namespace, err := app.CreateNamespace("foo")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("foo", "testmachine")
c.Assert(err, check.NotNil)
b := []byte("{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:test\"]}")
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(b),
}
app.db.Save(&machine)
app.aclPolicy = &ACLPolicy{
Groups: Groups{"group:test": []string{"foo", "foobar"}},
TagOwners: TagOwners{"tag:test": []string{"bar", "group:test"}},
ACLs: []ACL{
{Action: "accept", Users: []string{"tag:test"}, Ports: []string{"*:*"}},
},
}
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
c.Assert(app.aclRules, check.HasLen, 1)
c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1)
c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1")
}
// 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) {
namespace, err := app.CreateNamespace("foo")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("foo", "testmachine")
c.Assert(err, check.NotNil)
b := []byte("{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:test\"]}")
machine := Machine{
ID: 1,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(b),
}
app.db.Save(&machine)
app.aclPolicy = &ACLPolicy{
Groups: Groups{"group:test": []string{"foo", "foobar"}},
TagOwners: TagOwners{"tag:test": []string{"bar", "group:test"}},
ACLs: []ACL{
{Action: "accept", Users: []string{"*"}, Ports: []string{"tag:test:*"}},
},
}
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
c.Assert(app.aclRules, check.HasLen, 1)
c.Assert(app.aclRules[0].DstPorts, check.HasLen, 1)
c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1")
}
// need a test with:
// tag on a host that isn't owned by a tag owners. So the namespace
// of the host should be valid
func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
namespace, err := app.CreateNamespace("foo")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("foo", "testmachine")
c.Assert(err, check.NotNil)
b := []byte("{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:foo\"]}")
machine := Machine{
ID: 1,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(b),
}
app.db.Save(&machine)
app.aclPolicy = &ACLPolicy{
TagOwners: TagOwners{"tag:test": []string{"foo"}},
ACLs: []ACL{
{Action: "accept", Users: []string{"foo"}, Ports: []string{"*:*"}},
},
}
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
c.Assert(app.aclRules, check.HasLen, 1)
c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1)
c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1")
}
// tag on a host is owned by a tag owner, the tag is valid.
// an ACL rule is matching the tag to a namespace. It should not be valid since the
// host should be tied to the tag now.
func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
namespace, err := app.CreateNamespace("foo")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("foo", "webserver")
c.Assert(err, check.NotNil)
b := []byte("{\"OS\":\"centos\",\"Hostname\":\"webserver\",\"RequestTags\":[\"tag:webapp\"]}")
machine := Machine{
ID: 1,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "webserver",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(b),
}
app.db.Save(&machine)
_, err = app.GetMachine("foo", "user")
b = []byte("{\"OS\":\"debian\",\"Hostname\":\"user\"}")
c.Assert(err, check.NotNil)
machine = Machine{
ID: 2,
MachineKey: "foo2",
NodeKey: "bar2",
DiscoKey: "faab",
Name: "user",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(b),
}
app.db.Save(&machine)
app.aclPolicy = &ACLPolicy{
TagOwners: TagOwners{"tag:webapp": []string{"foo"}},
ACLs: []ACL{
{Action: "accept", Users: []string{"foo"}, Ports: []string{"tag:webapp:80,443"}},
},
}
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
c.Logf("Rules: %v", app.aclRules)
c.Assert(app.aclRules, check.HasLen, 1)
c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 0)
}
func (s *Suite) TestPortRange(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_range.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, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(5400))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
}
func (s *Suite) TestPortWildcard(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.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, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert(rules[0].SrcIPs, check.HasLen, 1)
c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
}
func (s *Suite) TestPortNamespace(c *check.C) {
namespace, err := app.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("testnamespace", "testmachine")
c.Assert(err, check.NotNil)
ips, _ := app.getAvailableIPs()
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips,
AuthKeyID: uint(pak.ID),
}
app.db.Save(&machine)
err = app.LoadACLPolicy(
"./tests/acls/acl_policy_basic_namespace_as_user.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, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert(rules[0].SrcIPs, check.HasLen, 1)
c.Assert(rules[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
c.Assert(len(ips), check.Equals, 1)
c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String())
}
func (s *Suite) TestPortGroup(c *check.C) {
namespace, err := app.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("testnamespace", "testmachine")
c.Assert(err, check.NotNil)
ips, _ := app.getAvailableIPs()
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips,
AuthKeyID: uint(pak.ID),
}
app.db.Save(&machine)
err = app.LoadACLPolicy("./tests/acls/acl_policy_basic_groups.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, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert(rules[0].SrcIPs, check.HasLen, 1)
c.Assert(rules[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
c.Assert(len(ips), check.Equals, 1)
c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String())
}