headscale/node_test.go

1267 lines
30 KiB
Go
Raw Permalink Normal View History

2021-05-12 00:55:36 +00:00
package headscale
import (
"fmt"
2022-09-01 22:04:31 +00:00
"net/netip"
"reflect"
"regexp"
"strconv"
"sync"
"testing"
2021-11-21 13:40:19 +00:00
"time"
2021-05-12 00:55:36 +00:00
"gopkg.in/check.v1"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
2021-05-12 00:55:36 +00:00
)
2023-05-01 14:53:23 +00:00
func (s *Suite) TestGetNode(c *check.C) {
user, err := app.CreateUser("test")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNode("test", "testnode")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.NotNil)
2023-05-01 14:53:23 +00:00
node := &Node{
2021-05-12 00:55:36 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-05-12 00:55:36 +00:00
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(node)
2021-05-12 00:55:36 +00:00
2023-05-01 14:53:23 +00:00
_, err = app.GetNode("test", "testnode")
2021-05-12 00:55:36 +00:00
c.Assert(err, check.IsNil)
2021-07-16 22:14:22 +00:00
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestGetNodeByID(c *check.C) {
user, err := app.CreateUser("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2021-05-12 00:55:36 +00:00
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByID(0)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.NotNil)
2023-05-01 14:53:23 +00:00
node := Node{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2021-07-16 22:14:22 +00:00
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByID(0)
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestGetNodeByNodeKey(c *check.C) {
user, err := app.CreateUser("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByID(0)
c.Assert(err, check.NotNil)
nodeKey := key.NewNode()
machineKey := key.NewMachine()
2023-05-01 14:53:23 +00:00
node := Node{
ID: 0,
MachineKey: MachinePublicKeyStripPrefix(machineKey.Public()),
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByNodeKey(nodeKey.Public())
c.Assert(err, check.IsNil)
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
user, err := app.CreateUser("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByID(0)
c.Assert(err, check.NotNil)
nodeKey := key.NewNode()
oldNodeKey := key.NewNode()
machineKey := key.NewMachine()
2023-05-01 14:53:23 +00:00
node := Node{
ID: 0,
MachineKey: MachinePublicKeyStripPrefix(machineKey.Public()),
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByAnyKey(machineKey.Public(), nodeKey.Public(), oldNodeKey.Public())
c.Assert(err, check.IsNil)
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestDeleteNode(c *check.C) {
user, err := app.CreateUser("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
node := Node{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(1),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2021-11-15 16:16:04 +00:00
2023-05-01 14:53:23 +00:00
err = app.DeleteNode(&node)
2021-07-16 22:29:14 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
2023-05-01 14:53:23 +00:00
_, err = app.GetNode(user.Name, "testnode")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.NotNil)
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestHardDeleteNode(c *check.C) {
user, err := app.CreateUser("test")
2021-07-16 22:14:22 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
node := Node{
2021-07-16 22:14:22 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode3",
UserID: user.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
2021-07-16 22:14:22 +00:00
AuthKeyID: uint(1),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2021-11-15 16:16:04 +00:00
2023-05-01 14:53:23 +00:00
err = app.HardDeleteNode(&node)
2021-07-16 22:29:14 +00:00
c.Assert(err, check.IsNil)
2021-11-15 16:16:04 +00:00
2023-05-01 14:53:23 +00:00
_, err = app.GetNode(user.Name, "testnode3")
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) {
user, err := app.CreateUser("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNodeByID(0)
c.Assert(err, check.NotNil)
2021-11-15 16:16:04 +00:00
for index := 0; index <= 10; index++ {
2023-05-01 14:53:23 +00:00
node := Node{
2021-11-15 16:16:04 +00:00
ID: uint64(index),
MachineKey: "foo" + strconv.Itoa(index),
NodeKey: "bar" + strconv.Itoa(index),
DiscoKey: "faa" + strconv.Itoa(index),
2023-05-01 14:53:23 +00:00
Hostname: "testnode" + strconv.Itoa(index),
UserID: user.ID,
2021-11-18 08:49:55 +00:00
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
}
2023-05-01 14:53:23 +00:00
node0ByID, err := app.GetNodeByID(0)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
peersOfNode0, err := app.ListPeers(node0ByID)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
c.Assert(len(peersOfNode0), check.Equals, 9)
c.Assert(peersOfNode0[0].Hostname, check.Equals, "testnode2")
c.Assert(peersOfNode0[5].Hostname, check.Equals, "testnode7")
c.Assert(peersOfNode0[8].Hostname, check.Equals, "testnode10")
}
2021-11-21 13:40:19 +00:00
func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
type base struct {
user *User
key *PreAuthKey
}
stor := make([]base, 0)
for _, name := range []string{"test", "admin"} {
user, err := app.CreateUser(name)
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
stor = append(stor, base{user, pak})
}
2023-05-01 14:53:23 +00:00
_, err := app.GetNodeByID(0)
c.Assert(err, check.NotNil)
for index := 0; index <= 10; index++ {
2023-05-01 14:53:23 +00:00
node := Node{
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),
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
2022-02-14 14:54:51 +00:00
},
2023-05-01 14:53:23 +00:00
Hostname: "testnode" + strconv.Itoa(index),
UserID: stor[index%2].user.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(stor[index%2].key.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
}
app.aclPolicy = &ACLPolicy{
Groups: map[string][]string{
"group:test": {"admin"},
},
2022-09-01 22:04:31 +00:00
Hosts: map[string]netip.Prefix{},
TagOwners: map[string][]string{},
ACLs: []ACL{
2022-08-04 08:47:00 +00:00
{
Action: "accept",
Sources: []string{"admin"},
Destinations: []string{"*:*"},
},
{
Action: "accept",
Sources: []string{"test"},
Destinations: []string{"test:*"},
},
},
Tests: []ACLTest{},
}
2022-02-03 19:00:41 +00:00
err = app.UpdateACLRules()
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
adminNode, err := app.GetNodeByID(1)
c.Logf("Node(%v), user: %v", adminNode.Hostname, adminNode.User)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
testNode, err := app.GetNodeByID(2)
c.Logf("Node(%v), user: %v", testNode.Hostname, testNode.User)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
nodes, err := app.ListNodes()
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
peersOfTestNode := app.filterNodesByACL(testNode, nodes)
peersOfAdminNode := app.filterNodesByACL(adminNode, nodes)
2023-05-01 14:53:23 +00:00
c.Log(peersOfTestNode)
c.Assert(len(peersOfTestNode), check.Equals, 9)
c.Assert(peersOfTestNode[0].Hostname, check.Equals, "testnode1")
c.Assert(peersOfTestNode[1].Hostname, check.Equals, "testnode3")
c.Assert(peersOfTestNode[3].Hostname, check.Equals, "testnode5")
2023-05-01 14:53:23 +00:00
c.Log(peersOfAdminNode)
c.Assert(len(peersOfAdminNode), check.Equals, 9)
c.Assert(peersOfAdminNode[0].Hostname, check.Equals, "testnode2")
c.Assert(peersOfAdminNode[2].Hostname, check.Equals, "testnode4")
c.Assert(peersOfAdminNode[5].Hostname, check.Equals, "testnode7")
}
2023-05-01 14:53:23 +00:00
func (s *Suite) TestExpireNode(c *check.C) {
user, err := app.CreateUser("test")
2021-11-21 13:40:19 +00:00
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
2021-11-21 13:40:19 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNode("test", "testnode")
2021-11-21 13:40:19 +00:00
c.Assert(err, check.NotNil)
2023-05-01 14:53:23 +00:00
node := &Node{
2021-11-21 13:40:19 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
2021-11-21 13:40:19 +00:00
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
Expiry: &time.Time{},
}
2023-05-01 14:53:23 +00:00
app.db.Save(node)
2021-11-21 13:40:19 +00:00
2023-05-01 14:53:23 +00:00
nodeFromDB, err := app.GetNode("test", "testnode")
2021-11-21 13:40:19 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
c.Assert(nodeFromDB, check.NotNil)
2021-11-21 13:40:19 +00:00
2023-05-01 14:53:23 +00:00
c.Assert(nodeFromDB.isExpired(), check.Equals, false)
2021-11-21 13:40:19 +00:00
2023-05-01 14:53:23 +00:00
err = app.ExpireNode(nodeFromDB)
2022-06-26 10:30:52 +00:00
c.Assert(err, check.IsNil)
2021-11-21 13:40:19 +00:00
2023-05-01 14:53:23 +00:00
c.Assert(nodeFromDB.isExpired(), check.Equals, true)
2021-11-21 13:40:19 +00:00
}
2022-01-16 13:16:59 +00:00
func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
2023-05-01 14:53:23 +00:00
input := NodeAddresses([]netip.Addr{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("192.0.2.1"),
netip.MustParseAddr("2001:db8::1"),
2022-01-16 13:16:59 +00:00
})
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
2023-05-01 14:53:23 +00:00
var deserialized NodeAddresses
2022-01-16 13:16:59 +00:00
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])
}
}
func (s *Suite) TestGenerateGivenName(c *check.C) {
user1, err := app.CreateUser("user-1")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user1.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNode("user-1", "testnode")
c.Assert(err, check.NotNil)
2023-05-01 14:53:23 +00:00
node := &Node{
ID: 0,
2023-05-01 14:53:23 +00:00
MachineKey: "node-key-1",
NodeKey: "node-key-1",
DiscoKey: "disco-key-1",
Hostname: "hostname-1",
GivenName: "hostname-1",
UserID: user1.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(node)
2023-05-01 14:53:23 +00:00
givenName, err := app.GenerateGivenName("node-key-2", "hostname-2")
comment := check.Commentf("Same user, unique nodes, unique hostnames, no conflict")
c.Assert(err, check.IsNil, comment)
c.Assert(givenName, check.Equals, "hostname-2", comment)
2023-05-01 14:53:23 +00:00
givenName, err = app.GenerateGivenName("node-key-1", "hostname-1")
comment = check.Commentf("Same user, same node, same hostname, no conflict")
c.Assert(err, check.IsNil, comment)
c.Assert(givenName, check.Equals, "hostname-1", comment)
2023-05-01 14:53:23 +00:00
givenName, err = app.GenerateGivenName("node-key-2", "hostname-1")
comment = check.Commentf("Same user, unique nodes, same hostname, conflict")
c.Assert(err, check.IsNil, comment)
2023-05-01 14:53:23 +00:00
c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", NodeGivenNameHashLength), comment)
2023-05-01 14:53:23 +00:00
givenName, err = app.GenerateGivenName("node-key-2", "hostname-1")
comment = check.Commentf("Unique users, unique nodes, same hostname, conflict")
c.Assert(err, check.IsNil, comment)
2023-05-01 14:53:23 +00:00
c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", NodeGivenNameHashLength), comment)
}
func (s *Suite) TestSetTags(c *check.C) {
user, err := app.CreateUser("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
_, err = app.GetNode("test", "testnode")
c.Assert(err, check.NotNil)
2023-05-01 14:53:23 +00:00
node := &Node{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
2023-05-01 14:53:23 +00:00
Hostname: "testnode",
UserID: user.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
2023-05-01 14:53:23 +00:00
app.db.Save(node)
// assign simple tags
sTags := []string{"tag:test", "tag:foo"}
2023-05-01 14:53:23 +00:00
err = app.SetTags(node, sTags)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
node, err = app.GetNode("test", "testnode")
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
c.Assert(node.ForcedTags, check.DeepEquals, StringList(sTags))
// assign duplicat tags, expect no errors but no doubles in DB
eTags := []string{"tag:bar", "tag:test", "tag:unknown", "tag:test"}
2023-05-01 14:53:23 +00:00
err = app.SetTags(node, eTags)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
node, err = app.GetNode("test", "testnode")
c.Assert(err, check.IsNil)
c.Assert(
2023-05-01 14:53:23 +00:00
node.ForcedTags,
check.DeepEquals,
StringList([]string{"tag:bar", "tag:test", "tag:unknown"}),
)
}
2022-04-16 11:15:04 +00:00
func Test_getTags(t *testing.T) {
type args struct {
aclPolicy *ACLPolicy
2023-05-01 14:53:23 +00:00
node Node
2022-04-16 11:15:04 +00:00
stripEmailDomain bool
}
tests := []struct {
name string
args args
wantInvalid []string
wantValid []string
}{
{
2023-05-01 14:53:23 +00:00
name: "valid tag one node",
2022-04-16 11:15:04 +00:00
args: args{
aclPolicy: &ACLPolicy{
2022-04-16 11:15:04 +00:00
TagOwners: TagOwners{
"tag:valid": []string{"joe"},
},
},
2023-05-01 14:53:23 +00:00
node: Node{
User: User{
2022-04-16 11:15:04 +00:00
Name: "joe",
},
HostInfo: HostInfo{
RequestTags: []string{"tag:valid"},
},
},
stripEmailDomain: false,
},
wantValid: []string{"tag:valid"},
wantInvalid: nil,
},
{
2023-05-01 14:53:23 +00:00
name: "invalid tag and valid tag one node",
2022-04-16 11:15:04 +00:00
args: args{
aclPolicy: &ACLPolicy{
2022-04-16 11:15:04 +00:00
TagOwners: TagOwners{
"tag:valid": []string{"joe"},
},
},
2023-05-01 14:53:23 +00:00
node: Node{
User: User{
2022-04-16 11:15:04 +00:00
Name: "joe",
},
HostInfo: HostInfo{
RequestTags: []string{"tag:valid", "tag:invalid"},
},
},
stripEmailDomain: false,
},
wantValid: []string{"tag:valid"},
wantInvalid: []string{"tag:invalid"},
},
{
name: "multiple invalid and identical tags, should return only one invalid tag",
args: args{
aclPolicy: &ACLPolicy{
2022-04-16 11:15:04 +00:00
TagOwners: TagOwners{
"tag:valid": []string{"joe"},
},
},
2023-05-01 14:53:23 +00:00
node: Node{
User: User{
2022-04-16 11:15:04 +00:00
Name: "joe",
},
HostInfo: HostInfo{
RequestTags: []string{
"tag:invalid",
"tag:valid",
"tag:invalid",
},
},
},
stripEmailDomain: false,
},
wantValid: []string{"tag:valid"},
wantInvalid: []string{"tag:invalid"},
},
{
name: "only invalid tags",
args: args{
aclPolicy: &ACLPolicy{
2022-04-16 11:15:04 +00:00
TagOwners: TagOwners{
"tag:valid": []string{"joe"},
},
},
2023-05-01 14:53:23 +00:00
node: Node{
User: User{
2022-04-16 11:15:04 +00:00
Name: "joe",
},
HostInfo: HostInfo{
RequestTags: []string{"tag:invalid", "very-invalid"},
},
},
stripEmailDomain: false,
},
wantValid: nil,
wantInvalid: []string{"tag:invalid", "very-invalid"},
},
{
name: "empty ACLPolicy should return empty tags and should not panic",
args: args{
aclPolicy: nil,
2023-05-01 14:53:23 +00:00
node: Node{
User: User{
Name: "joe",
},
HostInfo: HostInfo{
RequestTags: []string{"tag:invalid", "very-invalid"},
},
},
stripEmailDomain: false,
},
wantValid: nil,
wantInvalid: nil,
},
2022-04-16 11:15:04 +00:00
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
gotValid, gotInvalid := getTags(
test.args.aclPolicy,
2023-05-01 14:53:23 +00:00
test.args.node,
2022-04-16 11:15:04 +00:00
test.args.stripEmailDomain,
)
2022-04-25 20:07:44 +00:00
for _, valid := range gotValid {
if !contains(test.wantValid, valid) {
2022-04-25 20:27:44 +00:00
t.Errorf(
"valids: getTags() = %v, want %v",
gotValid,
test.wantValid,
)
2022-04-25 20:07:44 +00:00
break
}
2022-04-16 11:15:04 +00:00
}
2022-04-25 20:07:44 +00:00
for _, invalid := range gotInvalid {
if !contains(test.wantInvalid, invalid) {
2022-04-25 20:27:44 +00:00
t.Errorf(
"invalids: getTags() = %v, want %v",
gotInvalid,
test.wantInvalid,
)
2022-04-25 20:07:44 +00:00
break
}
2022-04-16 11:15:04 +00:00
}
})
}
}
func Test_getFilteredByACLPeers(t *testing.T) {
type args struct {
2023-05-01 14:53:23 +00:00
nodes []Node
rules []tailcfg.FilterRule
node *Node
}
tests := []struct {
2022-02-21 09:02:59 +00:00
name string
args args
2023-05-01 14:53:23 +00:00
want Nodes
}{
{
name: "all hosts can talk to each other",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
2022-02-21 15:06:20 +00:00
},
User: User{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: "*"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.1")},
User: User{Name: "joe"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.2")},
User: User{Name: "marc"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.3")},
User: User{Name: "mickael"},
},
},
},
{
name: "One host can talk to another, but not all hosts",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
2022-02-21 15:06:20 +00:00
},
User: User{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"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.1")},
User: User{Name: "joe"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.2")},
User: User{Name: "marc"},
},
},
},
{
name: "host cannot directly talk to destination, but return path is authorized",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
2022-02-21 15:06:20 +00:00
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "joe"},
},
{
2022-02-21 15:06:20 +00:00
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
2022-02-21 15:06:20 +00:00
},
User: User{Name: "marc"},
},
{
2022-02-21 15:06:20 +00:00
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
2022-02-21 15:06:20 +00:00
},
User: User{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"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.2")},
User: User{Name: "marc"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.3")},
User: User{Name: "mickael"},
},
},
},
{
name: "rules allows all hosts to reach one destination",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
},
User: User{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
},
},
{
name: "rules allows all hosts to reach one destination, destination can reach all hosts",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
},
User: User{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
},
User: User{Name: "mickael"},
},
},
},
{
name: "rule allows all hosts to reach all destinations",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
},
User: User{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "*"},
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.2")},
User: User{Name: "marc"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.3")},
User: User{Name: "mickael"},
},
},
},
{
name: "without rule all communications are forbidden",
args: args{
2023-05-01 14:53:23 +00:00
nodes: []Node{ // list of all nodes in the database
{
ID: 1,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.1"),
},
User: User{Name: "joe"},
},
{
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.2"),
},
User: User{Name: "marc"},
},
{
ID: 3,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
2022-09-01 22:04:31 +00:00
netip.MustParseAddr("100.64.0.3"),
},
User: User{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 2,
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{netip.MustParseAddr("100.64.0.2")},
User: User{Name: "marc"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{},
},
{
// Investigating 699
2023-05-01 14:53:23 +00:00
// Found some nodes: [ts-head-8w6paa ts-unstable-lys2ib ts-head-upcrmb ts-unstable-rlwpvr] node=ts-head-8w6paa
// ACL rules generated ACL=[{"DstPorts":[{"Bits":null,"IP":"*","Ports":{"First":0,"Last":65535}}],"SrcIPs":["fd7a:115c:a1e0::3","100.64.0.3","fd7a:115c:a1e0::4","100.64.0.4"]}]
// ACL Cache Map={"100.64.0.3":{"*":{}},"100.64.0.4":{"*":{}},"fd7a:115c:a1e0::3":{"*":{}},"fd7a:115c:a1e0::4":{"*":{}}}
name: "issue-699-broken-star",
args: args{
2023-05-01 14:53:23 +00:00
nodes: Nodes{ //
{
ID: 1,
Hostname: "ts-head-upcrmb",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.3"),
netip.MustParseAddr("fd7a:115c:a1e0::3"),
},
User: User{Name: "user1"},
},
{
ID: 2,
Hostname: "ts-unstable-rlwpvr",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.4"),
netip.MustParseAddr("fd7a:115c:a1e0::4"),
},
User: User{Name: "user1"},
},
{
ID: 3,
Hostname: "ts-head-8w6paa",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.1"),
netip.MustParseAddr("fd7a:115c:a1e0::1"),
},
User: User{Name: "user2"},
},
{
ID: 4,
Hostname: "ts-unstable-lys2ib",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.2"),
netip.MustParseAddr("fd7a:115c:a1e0::2"),
},
User: User{Name: "user2"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
DstPorts: []tailcfg.NetPortRange{
{
IP: "*",
Ports: tailcfg.PortRange{First: 0, Last: 65535},
},
},
SrcIPs: []string{
"fd7a:115c:a1e0::3", "100.64.0.3",
"fd7a:115c:a1e0::4", "100.64.0.4",
},
},
},
2023-05-01 14:53:23 +00:00
node: &Node{ // current node
ID: 3,
Hostname: "ts-head-8w6paa",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.1"),
netip.MustParseAddr("fd7a:115c:a1e0::1"),
},
User: User{Name: "user2"},
},
},
2023-05-01 14:53:23 +00:00
want: Nodes{
{
ID: 1,
Hostname: "ts-head-upcrmb",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.3"),
netip.MustParseAddr("fd7a:115c:a1e0::3"),
},
User: User{Name: "user1"},
},
{
ID: 2,
Hostname: "ts-unstable-rlwpvr",
2023-05-01 14:53:23 +00:00
IPAddresses: NodeAddresses{
netip.MustParseAddr("100.64.0.4"),
netip.MustParseAddr("fd7a:115c:a1e0::4"),
},
User: User{Name: "user1"},
},
},
},
}
var lock sync.RWMutex
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
aclRulesMap := generateACLPeerCacheMap(tt.args.rules)
2023-05-01 14:53:23 +00:00
got := filterNodesByACL(
tt.args.node,
tt.args.nodes,
&lock,
aclRulesMap,
2022-02-21 15:06:20 +00:00
)
if !reflect.DeepEqual(got, tt.want) {
2023-05-01 14:53:23 +00:00
t.Errorf("filterNodesByACL() = %v, want %v", got, tt.want)
}
})
}
}
func TestHeadscale_generateGivenName(t *testing.T) {
type args struct {
suppliedName string
randomSuffix bool
}
tests := []struct {
name string
h *Headscale
args args
want *regexp.Regexp
wantErr bool
}{
{
2023-05-01 14:53:23 +00:00
name: "simple node name generation",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
2023-05-01 14:53:23 +00:00
suppliedName: "testnode",
randomSuffix: false,
},
2023-05-01 14:53:23 +00:00
want: regexp.MustCompile("^testnode$"),
wantErr: false,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with 53 chars",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine",
randomSuffix: false,
},
want: regexp.MustCompile("^testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine$"),
wantErr: false,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with 63 chars",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
2023-05-01 14:53:23 +00:00
suppliedName: "nodeeee12345678901234567890123456789012345678901234567890123",
randomSuffix: false,
},
2023-05-01 14:53:23 +00:00
want: regexp.MustCompile("^nodeeee12345678901234567890123456789012345678901234567890123$"),
wantErr: false,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with 64 chars",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
2023-05-01 14:53:23 +00:00
suppliedName: "nodeeee123456789012345678901234567890123456789012345678901234",
randomSuffix: false,
},
want: nil,
wantErr: true,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with 73 chars",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
2023-05-01 14:53:23 +00:00
suppliedName: "nodeeee123456789012345678901234567890123456789012345678901234567890123",
randomSuffix: false,
},
want: nil,
wantErr: true,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with random suffix",
h: &Headscale{
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
suppliedName: "test",
randomSuffix: true,
},
2023-05-01 14:53:23 +00:00
want: regexp.MustCompile(fmt.Sprintf("^test-[a-z0-9]{%d}$", NodeGivenNameHashLength)),
wantErr: false,
},
{
2023-05-01 14:53:23 +00:00
name: "node name with 63 chars with random suffix",
h: &Headscale{
2022-06-05 15:47:26 +00:00
cfg: &Config{
OIDC: OIDCConfig{
StripEmaildomain: true,
},
},
},
args: args{
2023-05-01 14:53:23 +00:00
suppliedName: "nodeeee12345678901234567890123456789012345678901234567890123",
randomSuffix: true,
},
2023-05-01 14:53:23 +00:00
want: regexp.MustCompile(fmt.Sprintf("^nodeeee1234567890123456789012345678901234567890123-[a-z0-9]{%d}$", NodeGivenNameHashLength)),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.h.generateGivenName(tt.args.suppliedName, tt.args.randomSuffix)
if (err != nil) != tt.wantErr {
t.Errorf(
"Headscale.GenerateGivenName() error = %v, wantErr %v",
err,
tt.wantErr,
)
2022-06-26 09:43:17 +00:00
return
}
if tt.want != nil && !tt.want.MatchString(got) {
t.Errorf(
"Headscale.GenerateGivenName() = %v, does not match %v",
tt.want,
got,
)
}
if len(got) > labelHostnameLength {
t.Errorf(
"Headscale.GenerateGivenName() = %v is larger than allowed DNS segment %d",
got,
labelHostnameLength,
)
}
})
}
}
2022-08-24 12:09:06 +00:00
func (s *Suite) TestAutoApproveRoutes(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_autoapprovers.hujson")
c.Assert(err, check.IsNil)
user, err := app.CreateUser("test")
2022-08-24 12:09:06 +00:00
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil)
2022-08-24 12:09:06 +00:00
c.Assert(err, check.IsNil)
nodeKey := key.NewNode()
2022-09-04 23:33:53 +00:00
defaultRoute := netip.MustParsePrefix("0.0.0.0/0")
route1 := netip.MustParsePrefix("10.10.0.0/16")
// Check if a subprefix of an autoapproved route is approved
route2 := netip.MustParsePrefix("10.11.0.0/24")
2022-08-24 12:09:06 +00:00
2023-05-01 14:53:23 +00:00
node := Node{
2022-08-24 12:09:06 +00:00
ID: 0,
MachineKey: "foo",
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
Hostname: "test",
UserID: user.ID,
2022-08-24 12:09:06 +00:00
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
HostInfo: HostInfo{
RequestTags: []string{"tag:exit"},
2022-09-04 23:33:53 +00:00
RoutableIPs: []netip.Prefix{defaultRoute, route1, route2},
2022-08-24 12:09:06 +00:00
},
2022-09-04 23:33:53 +00:00
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
2022-08-24 12:09:06 +00:00
}
2023-05-01 14:53:23 +00:00
app.db.Save(&node)
2022-08-24 12:09:06 +00:00
2023-05-01 14:53:23 +00:00
err = app.processNodeRoutes(&node)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
node0ByID, err := app.GetNodeByID(0)
2022-08-24 12:09:06 +00:00
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
err = app.EnableAutoApprovedRoutes(node0ByID)
c.Assert(err, check.IsNil)
2023-05-01 14:53:23 +00:00
enabledRoutes, err := app.GetEnabledRoutes(node0ByID)
c.Assert(err, check.IsNil)
c.Assert(enabledRoutes, check.HasLen, 3)
2022-08-24 12:09:06 +00:00
}