2023-05-31 09:59:37 +02:00
|
|
|
package mapper
|
|
|
|
|
|
|
|
import (
|
2024-09-03 00:22:17 -07:00
|
|
|
"encoding/json"
|
2023-05-31 09:59:37 +02:00
|
|
|
"net/netip"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/policy"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/types/key"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestTailNode(t *testing.T) {
|
|
|
|
mustNK := func(str string) key.NodePublic {
|
|
|
|
var k key.NodePublic
|
|
|
|
_ = k.UnmarshalText([]byte(str))
|
|
|
|
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
|
|
|
|
mustDK := func(str string) key.DiscoPublic {
|
|
|
|
var k key.DiscoPublic
|
|
|
|
_ = k.UnmarshalText([]byte(str))
|
|
|
|
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
|
|
|
|
mustMK := func(str string) key.MachinePublic {
|
|
|
|
var k key.MachinePublic
|
|
|
|
_ = k.UnmarshalText([]byte(str))
|
|
|
|
|
|
|
|
return k
|
|
|
|
}
|
|
|
|
|
|
|
|
hiview := func(hoin tailcfg.Hostinfo) tailcfg.HostinfoView {
|
|
|
|
return hoin.View()
|
|
|
|
}
|
|
|
|
|
|
|
|
created := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
|
|
|
lastSeen := time.Date(2009, time.November, 10, 23, 9, 0, 0, time.UTC)
|
|
|
|
expire := time.Date(2500, time.November, 11, 23, 0, 0, 0, time.UTC)
|
|
|
|
|
|
|
|
tests := []struct {
|
2023-06-12 15:29:34 +02:00
|
|
|
name string
|
2023-09-24 13:42:05 +02:00
|
|
|
node *types.Node
|
2023-06-12 15:29:34 +02:00
|
|
|
pol *policy.ACLPolicy
|
|
|
|
dnsConfig *tailcfg.DNSConfig
|
|
|
|
baseDomain string
|
|
|
|
want *tailcfg.Node
|
|
|
|
wantErr bool
|
2023-05-31 09:59:37 +02:00
|
|
|
}{
|
|
|
|
{
|
2023-11-21 18:20:06 +01:00
|
|
|
name: "empty-node",
|
|
|
|
node: &types.Node{
|
2024-08-19 11:41:05 +02:00
|
|
|
GivenName: "empty",
|
|
|
|
Hostinfo: &tailcfg.Hostinfo{},
|
2023-11-21 18:20:06 +01:00
|
|
|
},
|
2023-06-12 15:29:34 +02:00
|
|
|
pol: &policy.ACLPolicy{},
|
|
|
|
dnsConfig: &tailcfg.DNSConfig{},
|
|
|
|
baseDomain: "",
|
2023-11-19 22:37:04 +01:00
|
|
|
want: &tailcfg.Node{
|
2024-08-19 11:41:05 +02:00
|
|
|
Name: "empty",
|
2023-11-19 22:37:04 +01:00
|
|
|
StableID: "0",
|
|
|
|
Addresses: []netip.Prefix{},
|
|
|
|
AllowedIPs: []netip.Prefix{},
|
|
|
|
DERP: "127.3.3.40:0",
|
|
|
|
Hostinfo: hiview(tailcfg.Hostinfo{}),
|
|
|
|
Tags: []string{},
|
|
|
|
PrimaryRoutes: []netip.Prefix{},
|
|
|
|
MachineAuthorized: true,
|
|
|
|
Capabilities: []tailcfg.NodeCapability{
|
|
|
|
"https://tailscale.com/cap/file-sharing", "https://tailscale.com/cap/is-admin",
|
|
|
|
"https://tailscale.com/cap/ssh", "debug-disable-upnp",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: false,
|
2023-05-31 09:59:37 +02:00
|
|
|
},
|
|
|
|
{
|
2023-09-24 13:42:05 +02:00
|
|
|
name: "minimal-node",
|
|
|
|
node: &types.Node{
|
2023-11-19 22:37:04 +01:00
|
|
|
ID: 0,
|
|
|
|
MachineKey: mustMK(
|
|
|
|
"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
|
|
|
|
),
|
|
|
|
NodeKey: mustNK(
|
|
|
|
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
|
|
|
),
|
|
|
|
DiscoKey: mustDK(
|
|
|
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
|
|
|
),
|
2024-04-17 07:03:06 +02:00
|
|
|
IPv4: iap("100.64.0.1"),
|
2023-05-31 09:59:37 +02:00
|
|
|
Hostname: "mini",
|
|
|
|
GivenName: "mini",
|
|
|
|
UserID: 0,
|
|
|
|
User: types.User{
|
|
|
|
Name: "mini",
|
|
|
|
},
|
|
|
|
ForcedTags: []string{},
|
|
|
|
AuthKey: &types.PreAuthKey{},
|
|
|
|
LastSeen: &lastSeen,
|
|
|
|
Expiry: &expire,
|
2023-11-21 18:20:06 +01:00
|
|
|
Hostinfo: &tailcfg.Hostinfo{},
|
2023-05-31 09:59:37 +02:00
|
|
|
Routes: []types.Route{
|
|
|
|
{
|
|
|
|
Prefix: types.IPPrefix(netip.MustParsePrefix("0.0.0.0/0")),
|
|
|
|
Advertised: true,
|
|
|
|
Enabled: true,
|
|
|
|
IsPrimary: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Prefix: types.IPPrefix(netip.MustParsePrefix("192.168.0.0/24")),
|
|
|
|
Advertised: true,
|
|
|
|
Enabled: true,
|
|
|
|
IsPrimary: true,
|
|
|
|
},
|
2023-05-31 15:11:09 +02:00
|
|
|
{
|
|
|
|
Prefix: types.IPPrefix(netip.MustParsePrefix("172.0.0.0/10")),
|
|
|
|
Advertised: true,
|
|
|
|
Enabled: false,
|
|
|
|
IsPrimary: true,
|
|
|
|
},
|
2023-05-31 09:59:37 +02:00
|
|
|
},
|
|
|
|
CreatedAt: created,
|
|
|
|
},
|
2023-06-12 15:29:34 +02:00
|
|
|
pol: &policy.ACLPolicy{},
|
|
|
|
dnsConfig: &tailcfg.DNSConfig{},
|
|
|
|
baseDomain: "",
|
2023-05-31 09:59:37 +02:00
|
|
|
want: &tailcfg.Node{
|
|
|
|
ID: 0,
|
|
|
|
StableID: "0",
|
|
|
|
Name: "mini",
|
|
|
|
|
|
|
|
User: 0,
|
|
|
|
|
|
|
|
Key: mustNK(
|
|
|
|
"nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
|
|
|
),
|
|
|
|
KeyExpiry: expire,
|
|
|
|
|
|
|
|
Machine: mustMK(
|
|
|
|
"mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
|
|
|
|
),
|
|
|
|
DiscoKey: mustDK(
|
|
|
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
|
|
|
),
|
|
|
|
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
|
|
|
|
AllowedIPs: []netip.Prefix{
|
|
|
|
netip.MustParsePrefix("100.64.0.1/32"),
|
|
|
|
netip.MustParsePrefix("0.0.0.0/0"),
|
|
|
|
netip.MustParsePrefix("192.168.0.0/24"),
|
|
|
|
},
|
2023-11-19 22:37:04 +01:00
|
|
|
DERP: "127.3.3.40:0",
|
|
|
|
Hostinfo: hiview(tailcfg.Hostinfo{}),
|
|
|
|
Created: created,
|
2023-05-31 09:59:37 +02:00
|
|
|
|
|
|
|
Tags: []string{},
|
|
|
|
|
|
|
|
PrimaryRoutes: []netip.Prefix{
|
|
|
|
netip.MustParsePrefix("192.168.0.0/24"),
|
|
|
|
},
|
|
|
|
|
|
|
|
LastSeen: &lastSeen,
|
|
|
|
MachineAuthorized: true,
|
|
|
|
|
2023-09-28 12:33:53 -07:00
|
|
|
Capabilities: []tailcfg.NodeCapability{
|
2023-05-31 09:59:37 +02:00
|
|
|
tailcfg.CapabilityFileSharing,
|
|
|
|
tailcfg.CapabilityAdmin,
|
|
|
|
tailcfg.CapabilitySSH,
|
2023-09-28 12:33:53 -07:00
|
|
|
tailcfg.NodeAttrDisableUPnP,
|
2023-05-31 09:59:37 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: false,
|
|
|
|
},
|
|
|
|
// TODO: Add tests to check other aspects of the node conversion:
|
|
|
|
// - With tags and policy
|
|
|
|
// - dnsconfig and basedomain
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2024-02-23 10:59:24 +01:00
|
|
|
cfg := &types.Config{
|
|
|
|
BaseDomain: tt.baseDomain,
|
|
|
|
DNSConfig: tt.dnsConfig,
|
|
|
|
RandomizeClientPort: false,
|
|
|
|
}
|
2023-05-31 09:59:37 +02:00
|
|
|
got, err := tailNode(
|
2023-09-24 13:42:05 +02:00
|
|
|
tt.node,
|
2023-09-28 12:33:53 -07:00
|
|
|
0,
|
2023-05-31 09:59:37 +02:00
|
|
|
tt.pol,
|
2024-02-23 10:59:24 +01:00
|
|
|
cfg,
|
2023-05-31 09:59:37 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
t.Errorf("tailNode() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty()); diff != "" {
|
|
|
|
t.Errorf("tailNode() unexpected result (-want +got):\n%s", diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2024-09-03 00:22:17 -07:00
|
|
|
|
|
|
|
func TestNodeExpiry(t *testing.T) {
|
|
|
|
tp := func(t time.Time) *time.Time {
|
|
|
|
return &t
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
exp *time.Time
|
|
|
|
wantTime time.Time
|
|
|
|
wantTimeZero bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "no-expiry",
|
|
|
|
exp: nil,
|
|
|
|
wantTimeZero: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "zero-expiry",
|
|
|
|
exp: &time.Time{},
|
|
|
|
wantTimeZero: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "localtime",
|
|
|
|
exp: tp(time.Time{}.Local()),
|
|
|
|
wantTimeZero: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
node := &types.Node{
|
|
|
|
GivenName: "test",
|
|
|
|
Expiry: tt.exp,
|
|
|
|
}
|
|
|
|
tn, err := tailNode(
|
|
|
|
node,
|
|
|
|
0,
|
|
|
|
&policy.ACLPolicy{},
|
|
|
|
&types.Config{},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("nodeExpiry() error = %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Round trip the node through JSON to ensure the time is serialized correctly
|
|
|
|
seri, err := json.Marshal(tn)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("nodeExpiry() error = %v", err)
|
|
|
|
}
|
|
|
|
var deseri tailcfg.Node
|
|
|
|
err = json.Unmarshal(seri, &deseri)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("nodeExpiry() error = %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if tt.wantTimeZero {
|
|
|
|
if !deseri.KeyExpiry.IsZero() {
|
|
|
|
t.Errorf("nodeExpiry() = %v, want zero", deseri.KeyExpiry)
|
|
|
|
}
|
|
|
|
} else if deseri.KeyExpiry != tt.wantTime {
|
|
|
|
t.Errorf("nodeExpiry() = %v, want %v", deseri.KeyExpiry, tt.wantTime)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|