Take over the found tunnel: its key, its port, its peers; stop it, never flush
On an adopted machine the private network takes the predecessor's tunnel over in place (hq ADR 0105). Genesis finds the one interface up besides the mesh's own, settles the hub's port and the mesh's range on it, and skips ADR 0100's non-overlap check for a range that is now the tunnel's; a --hub-port or --overlay-range that disagrees is refused naming the tunnel's. At enrolment the found interface's private key becomes this node's overlay key — the one credential the mesh takes rather than mints — stored where a generated one is stored, never printed and never sent; the tunnel (port, address, range, peers) travels with the keys so the mesh composes from it before the first declaration. The interface's service may say what it takes over. Before the mesh's unit starts, the found configuration is kept like any held file and the found unit is stopped and disabled; nothing is flushed, and an interface still up after its unit stopped refuses the takeover rather than half-working. The report says what was carried: interface, port, range, peer count, taken or not, and where the original was kept.
This commit is contained in:
@@ -43,6 +43,27 @@ func GenerateOverlayKey() (OverlayKey, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OverlayKeyFrom makes this node's overlay key from a private key it did not generate: the found
|
||||
// tunnel's, on an adopted node whose private network takes that tunnel over (novox/hq ADR 0105).
|
||||
// The one case where the mesh takes a credential it did not mint. From here on it is stored and
|
||||
// sealed exactly as a generated one — in the identity file and the key file, readable by root
|
||||
// alone — and the mesh receives only the public half, derived here from the private one so the
|
||||
// two cannot disagree.
|
||||
func OverlayKeyFrom(privateBase64 string) (OverlayKey, error) {
|
||||
raw, err := base64.StdEncoding.DecodeString(privateBase64)
|
||||
if err != nil {
|
||||
return OverlayKey{}, fmt.Errorf("the found tunnel's private key is not base64: %w", err)
|
||||
}
|
||||
private, err := ecdh.X25519().NewPrivateKey(raw)
|
||||
if err != nil {
|
||||
return OverlayKey{}, fmt.Errorf("the found tunnel's private key is not a Curve25519 key: %w", err)
|
||||
}
|
||||
return OverlayKey{
|
||||
Public: base64.StdEncoding.EncodeToString(private.PublicKey().Bytes()),
|
||||
Private: base64.StdEncoding.EncodeToString(private.Bytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OverlayKeyPath is where the private half lives: a file of its own, referenced by the interface
|
||||
// configuration rather than embedded in it.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// novox/hq ADR 0105: the found tunnel's private key becomes the node's overlay key, stored as a
|
||||
// generated one is, and the public half the mesh records is derived from it — so the peers that
|
||||
// know the tunnel by that key keep reaching it.
|
||||
func TestAnOverlayKeyTakenFromAFoundTunnelIsTheSameKey(t *testing.T) {
|
||||
generated, err := GenerateOverlayKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
taken, err := OverlayKeyFrom(generated.Private)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if taken.Public != generated.Public || taken.Private != generated.Private {
|
||||
t.Fatalf("a key taken from a private half is not that key: %+v vs %+v", taken, generated)
|
||||
}
|
||||
for _, bad := range []string{"", "not base64!", "c2hvcnQ="} {
|
||||
if _, err := OverlayKeyFrom(bad); err == nil || !strings.Contains(err.Error(), "found tunnel") {
|
||||
t.Errorf("%q was taken as a key: %v", bad, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user