headscale/hscontrol/types/node_test.go

455 lines
8.6 KiB
Go

package types
import (
"net/netip"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/juanfont/headscale/hscontrol/util"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
func Test_NodeCanAccess(t *testing.T) {
iap := func(ipStr string) *netip.Addr {
ip := netip.MustParseAddr(ipStr)
return &ip
}
tests := []struct {
name string
node1 Node
node2 Node
rules []tailcfg.FilterRule
want bool
}{
{
name: "no-rules",
node1: Node{
IPv4: iap("10.0.0.1"),
},
node2: Node{
IPv4: iap("10.0.0.2"),
},
rules: []tailcfg.FilterRule{},
want: false,
},
{
name: "wildcard",
node1: Node{
IPv4: iap("10.0.0.1"),
},
node2: Node{
IPv4: iap("10.0.0.2"),
},
rules: []tailcfg.FilterRule{
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{
IP: "*",
Ports: tailcfg.PortRangeAny,
},
},
},
},
want: true,
},
{
name: "other-cant-access-src",
node1: Node{
IPv4: iap("100.64.0.1"),
},
node2: Node{
IPv4: iap("100.64.0.3"),
},
rules: []tailcfg.FilterRule{
{
SrcIPs: []string{"100.64.0.2/32"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.3/32", Ports: tailcfg.PortRangeAny},
},
},
},
want: false,
},
{
name: "dest-cant-access-src",
node1: Node{
IPv4: iap("100.64.0.3"),
},
node2: Node{
IPv4: iap("100.64.0.2"),
},
rules: []tailcfg.FilterRule{
{
SrcIPs: []string{"100.64.0.2/32"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.3/32", Ports: tailcfg.PortRangeAny},
},
},
},
want: false,
},
{
name: "src-can-access-dest",
node1: Node{
IPv4: iap("100.64.0.2"),
},
node2: Node{
IPv4: iap("100.64.0.3"),
},
rules: []tailcfg.FilterRule{
{
SrcIPs: []string{"100.64.0.2/32"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.3/32", Ports: tailcfg.PortRangeAny},
},
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.node1.CanAccess(tt.rules, &tt.node2)
if got != tt.want {
t.Errorf("canAccess() failed: want (%t), got (%t)", tt.want, got)
}
})
}
}
func TestNodeFQDN(t *testing.T) {
tests := []struct {
name string
node Node
dns tailcfg.DNSConfig
domain string
want string
wantErr string
}{
{
name: "all-set",
node: Node{
GivenName: "test",
User: User{
Name: "user",
},
},
dns: tailcfg.DNSConfig{
Proxied: true,
},
domain: "example.com",
want: "test.user.example.com",
},
{
name: "no-given-name",
node: Node{
User: User{
Name: "user",
},
},
dns: tailcfg.DNSConfig{
Proxied: true,
},
domain: "example.com",
wantErr: "failed to create valid FQDN: node has no given name",
},
{
name: "no-user-name",
node: Node{
GivenName: "test",
User: User{},
},
dns: tailcfg.DNSConfig{
Proxied: true,
},
domain: "example.com",
wantErr: "failed to create valid FQDN: node user has no name",
},
{
name: "no-magic-dns",
node: Node{
GivenName: "test",
User: User{
Name: "user",
},
},
dns: tailcfg.DNSConfig{
Proxied: false,
},
domain: "example.com",
want: "test",
},
{
name: "no-dnsconfig",
node: Node{
GivenName: "test",
User: User{
Name: "user",
},
},
domain: "example.com",
want: "test",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.node.GetFQDN(&tc.dns, tc.domain)
if (err != nil) && (err.Error() != tc.wantErr) {
t.Errorf("GetFQDN() error = %s, wantErr %s", err, tc.wantErr)
return
}
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("GetFQDN unexpected result (-want +got):\n%s", diff)
}
})
}
}
func TestPeerChangeFromMapRequest(t *testing.T) {
nKeys := []key.NodePublic{
key.NewNode().Public(),
key.NewNode().Public(),
key.NewNode().Public(),
}
dKeys := []key.DiscoPublic{
key.NewDisco().Public(),
key.NewDisco().Public(),
key.NewDisco().Public(),
}
tests := []struct {
name string
node Node
mapReq tailcfg.MapRequest
want tailcfg.PeerChange
}{
{
name: "preferred-derp-changed",
node: Node{
ID: 1,
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Endpoints: []netip.AddrPort{},
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 998,
},
},
},
mapReq: tailcfg.MapRequest{
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 999,
},
},
},
want: tailcfg.PeerChange{
NodeID: 1,
DERPRegion: 999,
},
},
{
name: "preferred-derp-no-changed",
node: Node{
ID: 1,
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Endpoints: []netip.AddrPort{},
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 100,
},
},
},
mapReq: tailcfg.MapRequest{
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 100,
},
},
},
want: tailcfg.PeerChange{
NodeID: 1,
DERPRegion: 0,
},
},
{
name: "preferred-derp-no-mapreq-netinfo",
node: Node{
ID: 1,
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Endpoints: []netip.AddrPort{},
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 200,
},
},
},
mapReq: tailcfg.MapRequest{
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Hostinfo: &tailcfg.Hostinfo{},
},
want: tailcfg.PeerChange{
NodeID: 1,
DERPRegion: 0,
},
},
{
name: "preferred-derp-no-node-netinfo",
node: Node{
ID: 1,
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Endpoints: []netip.AddrPort{},
Hostinfo: &tailcfg.Hostinfo{},
},
mapReq: tailcfg.MapRequest{
NodeKey: nKeys[0],
DiscoKey: dKeys[0],
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 200,
},
},
},
want: tailcfg.PeerChange{
NodeID: 1,
DERPRegion: 200,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := tc.node.PeerChangeFromMapRequest(tc.mapReq)
if diff := cmp.Diff(tc.want, got, cmpopts.IgnoreFields(tailcfg.PeerChange{}, "LastSeen")); diff != "" {
t.Errorf("Patch unexpected result (-want +got):\n%s", diff)
}
})
}
}
func TestApplyPeerChange(t *testing.T) {
tests := []struct {
name string
nodeBefore Node
change *tailcfg.PeerChange
want Node
}{
{
name: "hostinfo-and-netinfo-not-exists",
nodeBefore: Node{},
change: &tailcfg.PeerChange{
DERPRegion: 1,
},
want: Node{
Hostinfo: &tailcfg.Hostinfo{
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 1,
},
},
},
},
{
name: "hostinfo-netinfo-not-exists",
nodeBefore: Node{
Hostinfo: &tailcfg.Hostinfo{
Hostname: "test",
},
},
change: &tailcfg.PeerChange{
DERPRegion: 3,
},
want: Node{
Hostinfo: &tailcfg.Hostinfo{
Hostname: "test",
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 3,
},
},
},
},
{
name: "hostinfo-netinfo-exists-derp-set",
nodeBefore: Node{
Hostinfo: &tailcfg.Hostinfo{
Hostname: "test",
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 999,
},
},
},
change: &tailcfg.PeerChange{
DERPRegion: 2,
},
want: Node{
Hostinfo: &tailcfg.Hostinfo{
Hostname: "test",
NetInfo: &tailcfg.NetInfo{
PreferredDERP: 2,
},
},
},
},
{
name: "endpoints-not-set",
nodeBefore: Node{},
change: &tailcfg.PeerChange{
Endpoints: []netip.AddrPort{
netip.MustParseAddrPort("8.8.8.8:88"),
},
},
want: Node{
Endpoints: []netip.AddrPort{
netip.MustParseAddrPort("8.8.8.8:88"),
},
},
},
{
name: "endpoints-set",
nodeBefore: Node{
Endpoints: []netip.AddrPort{
netip.MustParseAddrPort("6.6.6.6:66"),
},
},
change: &tailcfg.PeerChange{
Endpoints: []netip.AddrPort{
netip.MustParseAddrPort("8.8.8.8:88"),
},
},
want: Node{
Endpoints: []netip.AddrPort{
netip.MustParseAddrPort("8.8.8.8:88"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.nodeBefore.ApplyPeerChange(tt.change)
if diff := cmp.Diff(tt.want, tt.nodeBefore, util.Comparers...); diff != "" {
t.Errorf("Patch unexpected result (-want +got):\n%s", diff)
}
})
}
}