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.
369 lines
9.2 KiB
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)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|