You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tailscale/cmd/tailscale/cli/network-lock_test.go

369 lines
9.2 KiB

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package cli
import (
"bytes"
"net/netip"
"testing"
"github.com/google/go-cmp/cmp"
"go4.org/mem"
"tailscale.com/cmd/tailscale/cli/jsonoutput"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
"tailscale.com/types/tkatype"
)
func TestNetworkLockLogOutput(t *testing.T) {
votes := uint(1)
aum1 := tka.AUM{
MessageKind: tka.AUMAddKey,
Key: &tka.Key{
Kind: tka.Key25519,
Votes: 1,
Public: []byte{2, 2},
},
}
h1 := aum1.Hash()
aum2 := tka.AUM{
MessageKind: tka.AUMRemoveKey,
KeyID: []byte{3, 3},
PrevAUMHash: h1[:],
Signatures: []tkatype.Signature{
{
KeyID: []byte{3, 4},
Signature: []byte{4, 5},
},
},
Meta: map[string]string{"en": "three", "de": "drei", "es": "tres"},
}
h2 := aum2.Hash()
aum3 := tka.AUM{
MessageKind: tka.AUMCheckpoint,
PrevAUMHash: h2[:],
State: &tka.State{
Keys: []tka.Key{
{
Kind: tka.Key25519,
Votes: 1,
Public: []byte{1, 1},
Meta: map[string]string{"en": "one", "de": "eins", "es": "uno"},
},
},
DisablementSecrets: [][]byte{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
},
},
Votes: &votes,
}
updates := []ipnstate.NetworkLockUpdate{
{
Hash: aum3.Hash(),
Change: aum3.MessageKind.String(),
Raw: aum3.Serialize(),
},
{
Hash: aum2.Hash(),
Change: aum2.MessageKind.String(),
Raw: aum2.Serialize(),
},
{
Hash: aum1.Hash(),
Change: aum1.MessageKind.String(),
Raw: aum1.Serialize(),
},
}
t.Run("human-readable", func(t *testing.T) {
t.Parallel()
var outBuf bytes.Buffer
json := jsonoutput.JSONSchemaVersion{}
useColor := false
printNetworkLockLog(updates, &outBuf, json, useColor)
t.Logf("%s", outBuf.String())
want := `update 4M4Q3IXBARPQMFVXHJBDCYQMWU5H5FBKD7MFF75HE4O5JMIWR2UA (checkpoint)
Disablement values:
- 010203
- 040506
- 070809
Keys:
Type: 25519
KeyID: tlpub:0101
Metadata: map[de:eins en:one es:uno]
update BKVVXHOVBW7Y7YXYTLVVLMNSYG6DS5GVRVSYZLASNU3AQKA732XQ (remove-key)
KeyID: tlpub:0303
update UKJIKFHILQ62AEN7MQIFHXJ6SFVDGQCQA3OHVI3LWVPM736EMSAA (add-key)
Type: 25519
KeyID: tlpub:0202
`
if diff := cmp.Diff(outBuf.String(), want); diff != "" {
t.Fatalf("wrong output (-got, +want):\n%s", diff)
}
})
jsonV1 := `{
"SchemaVersion": "1",
"Messages": [
{
"Hash": "4M4Q3IXBARPQMFVXHJBDCYQMWU5H5FBKD7MFF75HE4O5JMIWR2UA",
"AUM": {
"MessageKind": "checkpoint",
"PrevAUMHash": "BKVVXHOVBW7Y7YXYTLVVLMNSYG6DS5GVRVSYZLASNU3AQKA732XQ",
"State": {
"DisablementSecrets": [
"010203",
"040506",
"070809"
],
"Keys": [
{
"Kind": "25519",
"Votes": 1,
"Public": "tlpub:0101",
"Meta": {
"de": "eins",
"en": "one",
"es": "uno"
}
}
],
"StateID1": 0,
"StateID2": 0
},
"Votes": 1
},
"Raw": "pAEFAlggCqtbndUNv4_i-JrrVbGywbw5dNWNZYysEm02CCgf3q8FowH2AoNDAQIDQwQFBkMHCAkDgaQBAQIBA0IBAQyjYmRlZGVpbnNiZW5jb25lYmVzY3VubwYB"
},
{
"Hash": "BKVVXHOVBW7Y7YXYTLVVLMNSYG6DS5GVRVSYZLASNU3AQKA732XQ",
"AUM": {
"MessageKind": "remove-key",
"PrevAUMHash": "UKJIKFHILQ62AEN7MQIFHXJ6SFVDGQCQA3OHVI3LWVPM736EMSAA",
"KeyID": "tlpub:0303",
"Meta": {
"de": "drei",
"en": "three",
"es": "tres"
},
"Signatures": [
{
"KeyID": "tlpub:0304",
"Signature": "BAU="
}
]
},
"Raw": "pQECAlggopKFFOhcPaARv2QQU90-kWozQFAG3Hqja7Vez-_EZIAEQgMDB6NiZGVkZHJlaWJlbmV0aHJlZWJlc2R0cmVzF4GiAUIDBAJCBAU="
},
{
"Hash": "UKJIKFHILQ62AEN7MQIFHXJ6SFVDGQCQA3OHVI3LWVPM736EMSAA",
"AUM": {
"MessageKind": "add-key",
"Key": {
"Kind": "25519",
"Votes": 1,
"Public": "tlpub:0202"
}
},
"Raw": "owEBAvYDowEBAgEDQgIC"
}
]
}
`
t.Run("json-1", func(t *testing.T) {
t.Parallel()
var outBuf bytes.Buffer
json := jsonoutput.JSONSchemaVersion{
IsSet: true,
Value: 1,
}
useColor := false
printNetworkLockLog(updates, &outBuf, json, useColor)
want := jsonV1
if diff := cmp.Diff(outBuf.String(), want); diff != "" {
t.Fatalf("wrong output (-got, +want):\n%s", diff)
}
})
}
func TestNetworkLockStatusOutput(t *testing.T) {
aum := tka.AUM{
MessageKind: tka.AUMNoOp,
}
h := aum.Hash()
head := [32]byte(h[:])
nodeKey1 := key.NodePublicFromRaw32(mem.B(bytes.Repeat([]byte{1}, 32)))
nodeKey2 := key.NodePublicFromRaw32(mem.B(bytes.Repeat([]byte{2}, 32)))
nodeKey3 := key.NodePublicFromRaw32(mem.B(bytes.Repeat([]byte{3}, 32)))
nlPub := key.NLPublicFromEd25519Unsafe(bytes.Repeat([]byte{4}, 32))
trustedNlPub := key.NLPublicFromEd25519Unsafe(bytes.Repeat([]byte{5}, 32))
tailnetIPv4_A, tailnetIPv6_A := netip.MustParseAddr("100.99.99.99"), netip.MustParseAddr("fd7a:115c:a1e0::701:b62a")
tailnetIPv4_B, tailnetIPv6_B := netip.MustParseAddr("100.88.88.88"), netip.MustParseAddr("fd7a:115c:a1e0::4101:512f")
t.Run("json-1", func(t *testing.T) {
for _, tt := range []struct {
Name string
Status ipnstate.NetworkLockStatus
Want string
}{
{
Name: "tailnet-lock-disabled",
Status: ipnstate.NetworkLockStatus{Enabled: false},
Want: `{
"SchemaVersion": "1",
"Enabled": false
}
`,
},
{
Name: "tailnet-lock-disabled-with-keys",
Status: ipnstate.NetworkLockStatus{
Enabled: false,
NodeKey: &nodeKey1,
PublicKey: trustedNlPub,
},
Want: `{
"SchemaVersion": "1",
"Enabled": false,
"PublicKey": "tlpub:0505050505050505050505050505050505050505050505050505050505050505",
"NodeKey": "nodekey:0101010101010101010101010101010101010101010101010101010101010101"
}
`,
},
{
Name: "tailnet-lock-enabled",
Status: ipnstate.NetworkLockStatus{
Enabled: true,
Head: &head,
PublicKey: nlPub,
NodeKey: &nodeKey1,
NodeKeySigned: false,
NodeKeySignature: nil,
TrustedKeys: []ipnstate.TKAKey{
{
Kind: tka.Key25519.String(),
Votes: 1,
Key: trustedNlPub,
Metadata: map[string]string{"en": "one", "de": "eins", "es": "uno"},
},
},
VisiblePeers: []*ipnstate.TKAPeer{
{
Name: "authentic-associate",
ID: tailcfg.NodeID(1234),
StableID: tailcfg.StableNodeID("1234_AAAA_TEST"),
TailscaleIPs: []netip.Addr{tailnetIPv4_A, tailnetIPv6_A},
NodeKey: nodeKey2,
NodeKeySignature: tka.NodeKeySignature{
SigKind: tka.SigDirect,
Pubkey: []byte("22222222222222222222222222222222"),
KeyID: []byte("44444444444444444444444444444444"),
Signature: []byte("1234567890"),
WrappingPubkey: []byte("0987654321"),
},
},
},
FilteredPeers: []*ipnstate.TKAPeer{
{
Name: "bogus-bandit",
ID: tailcfg.NodeID(5678),
StableID: tailcfg.StableNodeID("5678_BBBB_TEST"),
TailscaleIPs: []netip.Addr{tailnetIPv4_B, tailnetIPv6_B},
NodeKey: nodeKey3,
},
},
StateID: 98989898,
},
Want: `{
"SchemaVersion": "1",
"Enabled": true,
"PublicKey": "tlpub:0404040404040404040404040404040404040404040404040404040404040404",
"NodeKey": "nodekey:0101010101010101010101010101010101010101010101010101010101010101",
"Head": "WYIVHDR7JUIXBWAJT5UPSCAILEXB7OMINDFEFEPOPNTUCNXMY2KA",
"NodeKeySigned": false,
"NodeKeySignature": null,
"TrustedKeys": [
{
"Kind": "25519",
"Votes": 1,
"Public": "tlpub:0505050505050505050505050505050505050505050505050505050505050505",
"Meta": {
"de": "eins",
"en": "one",
"es": "uno"
}
}
],
"VisiblePeers": [
{
"ID": "1234_AAAA_TEST",
"DNSName": "authentic-associate",
"TailscaleIPs": [
"100.99.99.99",
"fd7a:115c:a1e0::701:b62a"
],
"NodeKey": "nodekey:0202020202020202020202020202020202020202020202020202020202020202",
"NodeKeySignature": {
"SigKind": "direct",
"PublicKey": "tlpub:3232323232323232323232323232323232323232323232323232323232323232",
"KeyID": "tlpub:3434343434343434343434343434343434343434343434343434343434343434",
"Signature": "MTIzNDU2Nzg5MA==",
"WrappingPublicKey": "tlpub:30393837363534333231"
}
}
],
"FilteredPeers": [
{
"ID": "5678_BBBB_TEST",
"DNSName": "bogus-bandit",
"TailscaleIPs": [
"100.88.88.88",
"fd7a:115c:a1e0::4101:512f"
],
"NodeKey": "nodekey:0303030303030303030303030303030303030303030303030303030303030303"
}
],
"State": 98989898
}
`,
},
} {
t.Run(tt.Name, func(t *testing.T) {
t.Parallel()
var outBuf bytes.Buffer
err := jsonoutput.PrintNetworkLockStatusJSONV1(&outBuf, &tt.Status)
if err != nil {
t.Fatalf("PrintNetworkLockStatusJSONV1: %v", err)
}
if diff := cmp.Diff(outBuf.String(), tt.Want); diff != "" {
t.Fatalf("wrong output (-got, +want):\n%s", diff)
}
})
}
})
}