A machine makes its tunnel key first and joins through the tunnel
nox-mesh-host key makes the tunnel key, or reads the one made, and prints its public half for the token to be issued for. enrol with a token that carries a tunnel takes that key, refuses another, writes mesh0 with the hub as its one peer and starts it, then reaches the bus over it (novox/hq ADR 0169). Tokens without a tunnel enrol as before.
This commit is contained in:
@@ -409,3 +409,26 @@ func TestATokenSaysWhatTheMeshCallsThisMachine(t *testing.T) {
|
||||
t.Fatalf("the name did not survive the token: %q", token.Node)
|
||||
}
|
||||
}
|
||||
|
||||
// A token through the tunnel carries the one peer, in the field names the control plane writes
|
||||
// (novox/hq ADR 0169), and an incomplete tunnel is refused naming what is missing.
|
||||
func TestATokenThroughTheTunnelParsesAndAPartOneIsRefused(t *testing.T) {
|
||||
whole := map[string]any{"v": 1, "node": "n", "broker": "10.42.0.1:4222", "fingerprint": "sha256:x",
|
||||
"signer": make([]byte, 32), "secret": "s",
|
||||
"tunnel": map[string]any{"key": "k", "address": "10.42.0.9/32", "range": "10.42.0.0/16",
|
||||
"hub_key": "h", "hub_endpoint": "198.51.100.1:51820"}}
|
||||
raw, _ := json.Marshal(whole)
|
||||
got, err := ParseToken(base64.RawURLEncoding.EncodeToString(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Tunnel == nil || got.Tunnel.HubEndpoint != "198.51.100.1:51820" || got.Tunnel.Range != "10.42.0.0/16" {
|
||||
t.Fatalf("the tunnel was not read: %+v", got.Tunnel)
|
||||
}
|
||||
whole["tunnel"] = map[string]any{"key": "k"}
|
||||
raw, _ = json.Marshal(whole)
|
||||
if _, err := ParseToken(base64.RawURLEncoding.EncodeToString(raw)); err == nil ||
|
||||
!strings.Contains(err.Error(), "the hub's tunnel key") {
|
||||
t.Fatalf("a token with half a tunnel was taken: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The node's key on the private network, which is a different key from the one that says who it
|
||||
@@ -64,6 +66,19 @@ func OverlayKeyFrom(privateBase64 string) (OverlayKey, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LoadOverlayKey reads the key `key` made and left in its file (novox/hq ADR 0169).
|
||||
func LoadOverlayKey(path string) (OverlayKey, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return OverlayKey{}, err
|
||||
}
|
||||
key, err := OverlayKeyFrom(strings.TrimSpace(string(raw)))
|
||||
if err != nil {
|
||||
return OverlayKey{}, fmt.Errorf("%s does not hold a tunnel key: %w", path, err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// OverlayKeyPath is where the private half lives: a file of its own, referenced by the interface
|
||||
// configuration rather than embedded in it.
|
||||
//
|
||||
|
||||
@@ -35,6 +35,21 @@ type Token struct {
|
||||
// firewall found here before enrolling, because an adopted node keeps that firewall in force.
|
||||
// Absent for a converged node.
|
||||
Adopted bool `json:"adopted,omitempty"`
|
||||
|
||||
// Tunnel is this machine's first tunnel, when the token was issued for the key it made with
|
||||
// `key` (novox/hq ADR 0169): its own address and the hub to reach. It brings the tunnel up from
|
||||
// this alone and reaches the bus over it, so the bus never has to face the internet.
|
||||
Tunnel *TokenTunnel `json:"tunnel,omitempty"`
|
||||
}
|
||||
|
||||
// TokenTunnel is the joining machine's side of its first tunnel. Field names are the wire format
|
||||
// the control plane writes.
|
||||
type TokenTunnel struct {
|
||||
Key string `json:"key"`
|
||||
Address string `json:"address"`
|
||||
Range string `json:"range"`
|
||||
HubKey string `json:"hub_key"`
|
||||
HubEndpoint string `json:"hub_endpoint"`
|
||||
}
|
||||
|
||||
// ParseToken reads a token a person pasted.
|
||||
@@ -70,6 +85,17 @@ func ParseToken(encoded string) (Token, error) {
|
||||
if strings.TrimSpace(t.Secret) == "" {
|
||||
missing = append(missing, "the one-time secret")
|
||||
}
|
||||
if tt := t.Tunnel; tt != nil {
|
||||
for _, part := range []struct{ value, says string }{
|
||||
{tt.Key, "the tunnel key it was issued for"}, {tt.Address, "this machine's address"},
|
||||
{tt.Range, "the private network's range"}, {tt.HubKey, "the hub's tunnel key"},
|
||||
{tt.HubEndpoint, "where the hub's tunnel is dialled"},
|
||||
} {
|
||||
if strings.TrimSpace(part.value) == "" {
|
||||
missing = append(missing, part.says)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
// Refused whole rather than used partially. A token missing the fingerprint would have
|
||||
// this node connect to whatever answers at that address, and one missing the signing key
|
||||
|
||||
Reference in New Issue
Block a user