mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-05 04:06:35 +02:00
Add a new control/tsp package providing a client for speaking the Tailscale protocol to a coordination server over Noise, along with a cmd/tsp binary exposing it as a low-level composable tool for generating keys, registering nodes, and issuing map requests. Previously developed out-of-tree at github.com/bradfitz/tsp; imported here without git history. Updates #12542 Change-Id: I6ad21143c4aefe8939d4a46ae65b2184173bf69f Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package tsp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"tailscale.com/types/key"
|
|
)
|
|
|
|
func TestNodeFileRoundTrip(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "node.json")
|
|
|
|
nf := NodeFile{
|
|
NodeKey: key.NewNode(),
|
|
MachineKey: key.NewMachine(),
|
|
ServerInfo: ServerInfo{
|
|
URL: "https://controlplane.tailscale.com",
|
|
Key: key.NewMachine().Public(),
|
|
},
|
|
}
|
|
|
|
if err := WriteNodeFile(path, nf); err != nil {
|
|
t.Fatalf("WriteNodeFile: %v", err)
|
|
}
|
|
|
|
got, err := ReadNodeFile(path)
|
|
if err != nil {
|
|
t.Fatalf("ReadNodeFile: %v", err)
|
|
}
|
|
if !got.NodeKey.Equal(nf.NodeKey) {
|
|
t.Errorf("node key mismatch")
|
|
}
|
|
if !got.MachineKey.Equal(nf.MachineKey) {
|
|
t.Errorf("machine key mismatch")
|
|
}
|
|
if got.URL != nf.URL {
|
|
t.Errorf("server URL = %q, want %q", got.URL, nf.URL)
|
|
}
|
|
if got.ServerInfo.Key != nf.ServerInfo.Key {
|
|
t.Errorf("server key mismatch")
|
|
}
|
|
}
|
|
|
|
// TestNodeFileFormat verifies that ReadNodeFile can parse a fixed JSON literal,
|
|
// ensuring we don't accidentally change the on-disk format.
|
|
func TestNodeFileFormat(t *testing.T) {
|
|
const fileContents = `{
|
|
"node_key": "privkey:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
"machine_key": "privkey:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
|
|
"server_url": "https://controlplane.tailscale.com",
|
|
"server_key": "mkey:1111111111111111111111111111111111111111111111111111111111111111"
|
|
}`
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "node.json")
|
|
if err := os.WriteFile(path, []byte(fileContents), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nf, err := ReadNodeFile(path)
|
|
if err != nil {
|
|
t.Fatalf("ReadNodeFile: %v", err)
|
|
}
|
|
if nf.NodeKey.IsZero() {
|
|
t.Error("node key is zero")
|
|
}
|
|
if nf.MachineKey.IsZero() {
|
|
t.Error("machine key is zero")
|
|
}
|
|
if nf.URL != "https://controlplane.tailscale.com" {
|
|
t.Errorf("server URL = %q", nf.URL)
|
|
}
|
|
if nf.ServerInfo.Key.IsZero() {
|
|
t.Error("server key is zero")
|
|
}
|
|
}
|
|
|
|
// TestNodeFileWriteFormat verifies that WriteNodeFile produces the expected
|
|
// JSON field names.
|
|
func TestNodeFileWriteFormat(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "node.json")
|
|
|
|
nf := NodeFile{
|
|
NodeKey: key.NewNode(),
|
|
MachineKey: key.NewMachine(),
|
|
ServerInfo: ServerInfo{
|
|
URL: "https://example.com",
|
|
Key: key.NewMachine().Public(),
|
|
},
|
|
}
|
|
|
|
if err := WriteNodeFile(path, nf); err != nil {
|
|
t.Fatalf("WriteNodeFile: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
t.Fatalf("parsing written JSON: %v", err)
|
|
}
|
|
for _, field := range []string{"node_key", "machine_key", "server_url", "server_key"} {
|
|
if _, ok := raw[field]; !ok {
|
|
t.Errorf("missing JSON field %q in written file", field)
|
|
}
|
|
}
|
|
}
|