|
|
|
@ -17,10 +17,12 @@ import (
|
|
|
|
|
|
|
|
|
|
"tailscale.com/control/controlclient"
|
|
|
|
|
"tailscale.com/hostinfo"
|
|
|
|
|
"tailscale.com/ipn"
|
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
|
"tailscale.com/tka"
|
|
|
|
|
"tailscale.com/types/key"
|
|
|
|
|
"tailscale.com/types/netmap"
|
|
|
|
|
"tailscale.com/types/persist"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func fakeControlClient(t *testing.T, c *http.Client) *controlclient.Auto {
|
|
|
|
@ -62,6 +64,7 @@ func fakeNoiseServer(t *testing.T, handler http.HandlerFunc) (*httptest.Server,
|
|
|
|
|
|
|
|
|
|
func TestTKAEnablementFlow(t *testing.T) {
|
|
|
|
|
networkLockAvailable = func() bool { return true } // Enable the feature flag
|
|
|
|
|
nodePriv := key.NewNode()
|
|
|
|
|
|
|
|
|
|
// Make a fake TKA authority, getting a usable genesis AUM which
|
|
|
|
|
// our mock server can communicate.
|
|
|
|
@ -83,8 +86,11 @@ func TestTKAEnablementFlow(t *testing.T) {
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(body); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if body.NodeID != 420 {
|
|
|
|
|
t.Errorf("bootstrap nodeID=%v, want 420", body.NodeID)
|
|
|
|
|
if body.Version != tailcfg.CurrentCapabilityVersion {
|
|
|
|
|
t.Errorf("bootstrap CapVer = %v, want %v", body.Version, tailcfg.CurrentCapabilityVersion)
|
|
|
|
|
}
|
|
|
|
|
if body.NodeKey != nodePriv.Public() {
|
|
|
|
|
t.Errorf("bootstrap nodeKey=%v, want %v", body.NodeKey, nodePriv.Public())
|
|
|
|
|
}
|
|
|
|
|
if body.Head != "" {
|
|
|
|
|
t.Errorf("bootstrap head=%s, want empty hash", body.Head)
|
|
|
|
@ -112,11 +118,13 @@ func TestTKAEnablementFlow(t *testing.T) {
|
|
|
|
|
cc: cc,
|
|
|
|
|
ccAuto: cc,
|
|
|
|
|
logf: t.Logf,
|
|
|
|
|
prefs: &ipn.Prefs{
|
|
|
|
|
Persist: &persist.Persist{PrivateNodeKey: nodePriv},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b.mu.Lock()
|
|
|
|
|
err = b.tkaSyncIfNeededLocked(&netmap.NetworkMap{
|
|
|
|
|
SelfNode: &tailcfg.Node{ID: 420},
|
|
|
|
|
TKAEnabled: true,
|
|
|
|
|
TKAHead: tka.AUMHash{},
|
|
|
|
|
})
|
|
|
|
@ -136,6 +144,7 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
networkLockAvailable = func() bool { return true } // Enable the feature flag
|
|
|
|
|
temp := t.TempDir()
|
|
|
|
|
os.Mkdir(filepath.Join(temp, "tka"), 0755)
|
|
|
|
|
nodePriv := key.NewNode()
|
|
|
|
|
|
|
|
|
|
// Make a fake TKA authority, to seed local state.
|
|
|
|
|
disablementSecret := bytes.Repeat([]byte{0xa5}, 32)
|
|
|
|
@ -153,6 +162,7 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
t.Fatalf("tka.Create() failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
returnWrongSecret := false
|
|
|
|
|
ts, client := fakeNoiseServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
switch r.URL.Path {
|
|
|
|
@ -161,14 +171,11 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(body); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var disablement []byte
|
|
|
|
|
switch body.NodeID {
|
|
|
|
|
case 42:
|
|
|
|
|
disablement = bytes.Repeat([]byte{0x42}, 32) // wrong secret
|
|
|
|
|
case 420:
|
|
|
|
|
disablement = disablementSecret
|
|
|
|
|
default:
|
|
|
|
|
t.Errorf("bootstrap nodeID=%v, wanted 42 or 420", body.NodeID)
|
|
|
|
|
if body.Version != tailcfg.CurrentCapabilityVersion {
|
|
|
|
|
t.Errorf("bootstrap CapVer = %v, want %v", body.Version, tailcfg.CurrentCapabilityVersion)
|
|
|
|
|
}
|
|
|
|
|
if body.NodeKey != nodePriv.Public() {
|
|
|
|
|
t.Errorf("nodeKey=%v, want %v", body.NodeKey, nodePriv.Public())
|
|
|
|
|
}
|
|
|
|
|
var head tka.AUMHash
|
|
|
|
|
if err := head.UnmarshalText([]byte(body.Head)); err != nil {
|
|
|
|
@ -178,6 +185,13 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
t.Errorf("reported head = %x, want %x", head, authority.Head())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var disablement []byte
|
|
|
|
|
if returnWrongSecret {
|
|
|
|
|
disablement = bytes.Repeat([]byte{0x42}, 32) // wrong secret
|
|
|
|
|
} else {
|
|
|
|
|
disablement = disablementSecret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(200)
|
|
|
|
|
out := tailcfg.TKABootstrapResponse{
|
|
|
|
|
DisablementSecret: disablement,
|
|
|
|
@ -203,13 +217,15 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
authority: authority,
|
|
|
|
|
storage: chonk,
|
|
|
|
|
},
|
|
|
|
|
prefs: &ipn.Prefs{
|
|
|
|
|
Persist: &persist.Persist{PrivateNodeKey: nodePriv},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test that the wrong disablement secret does not shut down the authority.
|
|
|
|
|
// NodeID == 42 indicates this scenario to our mock server.
|
|
|
|
|
returnWrongSecret = true
|
|
|
|
|
b.mu.Lock()
|
|
|
|
|
err = b.tkaSyncIfNeededLocked(&netmap.NetworkMap{
|
|
|
|
|
SelfNode: &tailcfg.Node{ID: 42},
|
|
|
|
|
TKAEnabled: false,
|
|
|
|
|
TKAHead: authority.Head(),
|
|
|
|
|
})
|
|
|
|
@ -222,10 +238,9 @@ func TestTKADisablementFlow(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test the correct disablement secret shuts down the authority.
|
|
|
|
|
// NodeID == 420 indicates this scenario to our mock server.
|
|
|
|
|
returnWrongSecret = false
|
|
|
|
|
b.mu.Lock()
|
|
|
|
|
err = b.tkaSyncIfNeededLocked(&netmap.NetworkMap{
|
|
|
|
|
SelfNode: &tailcfg.Node{ID: 420},
|
|
|
|
|
TKAEnabled: false,
|
|
|
|
|
TKAHead: authority.Head(),
|
|
|
|
|
})
|
|
|
|
|