headscale/machine_test.go

687 lines
17 KiB
Go
Raw Permalink Normal View History

2021-05-12 00:55:36 +00:00
package headscale
import (
"fmt"
"reflect"
"strconv"
"testing"
2021-11-21 13:40:19 +00:00
"time"
2021-05-12 00:55:36 +00:00
"gopkg.in/check.v1"
2022-01-16 13:16:59 +00:00
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
2021-05-12 00:55:36 +00:00
)
func (s *Suite) TestGetMachine(c *check.C) {
2021-11-15 16:16:04 +00:00
namespace, err := app.CreateNamespace("test")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
_, err = app.GetMachine("test", "testmachine")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.NotNil)
2021-11-15 16:16:04 +00:00
machine := &Machine{
2021-05-12 00:55:36 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
2021-11-15 16:16:04 +00:00
NamespaceID: namespace.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-05-12 00:55:36 +00:00
AuthKeyID: uint(pak.ID),
}
2021-11-15 16:16:04 +00:00
app.db.Save(machine)
2021-05-12 00:55:36 +00:00
2022-03-01 16:34:35 +00:00
_, err = app.GetMachine("test", "testmachine")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
2021-07-16 22:14:22 +00:00
}
func (s *Suite) TestGetMachineByID(c *check.C) {
2021-11-15 16:16:04 +00:00
namespace, err := app.CreateNamespace("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2021-05-12 00:55:36 +00:00
2021-11-15 16:16:04 +00:00
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
_, err = app.GetMachineByID(0)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.NotNil)
2021-11-15 16:16:04 +00:00
machine := Machine{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
2021-11-15 16:16:04 +00:00
NamespaceID: namespace.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(pak.ID),
}
2021-11-15 16:16:04 +00:00
app.db.Save(&machine)
2021-07-16 22:14:22 +00:00
2022-03-01 16:34:35 +00:00
_, err = app.GetMachineByID(0)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
}
func (s *Suite) TestGetMachineByNodeKeys(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachineByID(0)
c.Assert(err, check.NotNil)
nodeKey := key.NewNode()
oldNodeKey := key.NewNode()
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
app.db.Save(&machine)
_, err = app.GetMachineByNodeKeys(nodeKey.Public(), oldNodeKey.Public())
c.Assert(err, check.IsNil)
}
2021-07-16 22:14:22 +00:00
func (s *Suite) TestDeleteMachine(c *check.C) {
2021-11-15 16:16:04 +00:00
namespace, err := app.CreateNamespace("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
machine := Machine{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
2021-11-15 16:16:04 +00:00
NamespaceID: namespace.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(1),
}
2021-11-15 16:16:04 +00:00
app.db.Save(&machine)
err = app.DeleteMachine(&machine)
2021-07-16 22:29:14 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
_, err = app.GetMachine(namespace.Name, "testmachine")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.NotNil)
}
func (s *Suite) TestHardDeleteMachine(c *check.C) {
2021-11-15 16:16:04 +00:00
namespace, err := app.CreateNamespace("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
machine := Machine{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine3",
2021-11-15 16:16:04 +00:00
NamespaceID: namespace.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(1),
}
2021-11-15 16:16:04 +00:00
app.db.Save(&machine)
err = app.HardDeleteMachine(&machine)
2021-07-16 22:29:14 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
_, err = app.GetMachine(namespace.Name, "testmachine3")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.NotNil)
2021-05-12 00:55:36 +00:00
}
2022-02-25 09:26:34 +00:00
func (s *Suite) TestListPeers(c *check.C) {
2021-11-15 16:16:04 +00:00
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
_, err = app.GetMachineByID(0)
c.Assert(err, check.NotNil)
2021-11-15 16:16:04 +00:00
for index := 0; index <= 10; index++ {
machine := Machine{
ID: uint64(index),
MachineKey: "foo" + strconv.Itoa(index),
NodeKey: "bar" + strconv.Itoa(index),
DiscoKey: "faa" + strconv.Itoa(index),
Name: "testmachine" + strconv.Itoa(index),
NamespaceID: namespace.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2021-11-15 16:16:04 +00:00
app.db.Save(&machine)
}
2021-11-15 16:16:04 +00:00
machine0ByID, err := app.GetMachineByID(0)
c.Assert(err, check.IsNil)
2022-02-25 09:26:34 +00:00
peersOfMachine0, err := app.ListPeers(machine0ByID)
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
c.Assert(len(peersOfMachine0), check.Equals, 9)
c.Assert(peersOfMachine0[0].Name, check.Equals, "testmachine2")
c.Assert(peersOfMachine0[5].Name, check.Equals, "testmachine7")
c.Assert(peersOfMachine0[8].Name, check.Equals, "testmachine10")
}
2021-11-21 13:40:19 +00:00
func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
type base struct {
namespace *Namespace
key *PreAuthKey
}
stor := make([]base, 0)
for _, name := range []string{"test", "admin"} {
namespace, err := app.CreateNamespace(name)
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
stor = append(stor, base{namespace, pak})
}
_, err := app.GetMachineByID(0)
c.Assert(err, check.NotNil)
for index := 0; index <= 10; index++ {
machine := Machine{
2022-02-14 14:54:51 +00:00
ID: uint64(index),
MachineKey: "foo" + strconv.Itoa(index),
NodeKey: "bar" + strconv.Itoa(index),
DiscoKey: "faa" + strconv.Itoa(index),
IPAddresses: MachineAddresses{
netaddr.MustParseIP(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
},
Name: "testmachine" + strconv.Itoa(index),
NamespaceID: stor[index%2].namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(stor[index%2].key.ID),
}
app.db.Save(&machine)
}
app.aclPolicy = &ACLPolicy{
Groups: map[string][]string{
"group:test": {"admin"},
},
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:*"}},
},
Tests: []ACLTest{},
}
2022-02-03 19:00:41 +00:00
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
adminMachine, err := app.GetMachineByID(1)
c.Logf("Machine(%v), namespace: %v", adminMachine.Name, adminMachine.Namespace)
c.Assert(err, check.IsNil)
testMachine, err := app.GetMachineByID(2)
c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace)
c.Assert(err, check.IsNil)
2022-02-25 09:26:34 +00:00
machines, err := app.ListMachines()
c.Assert(err, check.IsNil)
2022-02-21 09:02:59 +00:00
peersOfTestMachine := getFilteredByACLPeers(machines, app.aclRules, testMachine)
peersOfAdminMachine := getFilteredByACLPeers(machines, app.aclRules, adminMachine)
c.Log(peersOfTestMachine)
c.Assert(len(peersOfTestMachine), check.Equals, 4)
c.Assert(peersOfTestMachine[0].Name, check.Equals, "testmachine4")
c.Assert(peersOfTestMachine[1].Name, check.Equals, "testmachine6")
c.Assert(peersOfTestMachine[3].Name, check.Equals, "testmachine10")
c.Log(peersOfAdminMachine)
c.Assert(len(peersOfAdminMachine), check.Equals, 9)
c.Assert(peersOfAdminMachine[0].Name, check.Equals, "testmachine2")
c.Assert(peersOfAdminMachine[2].Name, check.Equals, "testmachine4")
c.Assert(peersOfAdminMachine[5].Name, check.Equals, "testmachine7")
}
2021-11-21 13:40:19 +00:00
func (s *Suite) TestExpireMachine(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachine("test", "testmachine")
c.Assert(err, check.NotNil)
machine := &Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
Expiry: &time.Time{},
}
app.db.Save(machine)
machineFromDB, err := app.GetMachine("test", "testmachine")
c.Assert(err, check.IsNil)
c.Assert(machineFromDB.isExpired(), check.Equals, false)
app.ExpireMachine(machineFromDB)
c.Assert(machineFromDB.isExpired(), check.Equals, true)
}
2022-01-16 13:16:59 +00:00
func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
input := MachineAddresses([]netaddr.IP{
netaddr.MustParseIP("192.0.2.1"),
netaddr.MustParseIP("2001:db8::1"),
})
serialized, err := input.Value()
c.Assert(err, check.IsNil)
2022-01-30 08:35:10 +00:00
if serial, ok := serialized.(string); ok {
c.Assert(serial, check.Equals, "192.0.2.1,2001:db8::1")
}
2022-01-16 13:16:59 +00:00
var deserialized MachineAddresses
err = deserialized.Scan(serialized)
c.Assert(err, check.IsNil)
c.Assert(len(deserialized), check.Equals, len(input))
for i := range deserialized {
c.Assert(deserialized[i], check.Equals, input[i])
}
}
// nolint
func Test_getFilteredByACLPeers(t *testing.T) {
type args struct {
machines []Machine
rules []tailcfg.FilterRule
machine *Machine
}
tests := []struct {
2022-02-21 09:02:59 +00:00
name string
args args
want Machines
}{
{
name: "all hosts can talk to each other",
args: args{
machines: []Machine{ // list of all machines in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
2022-02-21 09:02:59 +00:00
{
SrcIPs: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"},
DstPorts: []tailcfg.NetPortRange{
{IP: "*"},
},
},
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "One host can talk to another, but not all hosts",
args: args{
machines: []Machine{ // list of all machines in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
2022-02-21 09:02:59 +00:00
{
SrcIPs: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
},
{
name: "host cannot directly talk to destination, but return path is authorized",
args: args{
machines: []Machine{ // list of all machines in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
2022-02-21 09:02:59 +00:00
{
SrcIPs: []string{"100.64.0.3"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "rules allows all hosts to reach one destination",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
},
},
{
name: "rules allows all hosts to reach one destination, destination can reach all hosts",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "rule allows all hosts to reach all destinations",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "*"},
},
},
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "without rule all communications are forbidden",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
2022-02-21 15:06:20 +00:00
got := getFilteredByACLPeers(
tt.args.machines,
tt.args.rules,
tt.args.machine,
)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("getFilteredByACLPeers() = %v, want %v", got, tt.want)
}
})
}
}