package identity import ( "crypto/ecdh" "crypto/rand" "encoding/base64" "fmt" ) // The node's key on the private network, which is a different key from the one that says who it // is — and deliberately so. // // novox/hq 08-connectivity: each node generates its own keypair, the private half never leaves // the machine, and the public half is published to the mesh. That means the control plane // computes a peer graph it cannot itself impersonate: it knows every public key and holds no // private one, so it can say who may talk to whom without being able to pretend to be any of them. // // Separate from the identity keypair because they are verified by different things at different // times — the identity signs messages to the mesh, this one encrypts traffic between nodes — and // a key used for two purposes is one rotation away from breaking the other. // OverlayKey is a Curve25519 keypair, which is what WireGuard uses. type OverlayKey struct { // Public is what travels. Base64, which is the form WireGuard configuration files use, so it // is carried the way it will be written rather than converted at the last moment. Public string `json:"public"` // Private never leaves this machine. It is written to a file of its own that the interface // configuration points at, so the control plane can compose that configuration without ever // holding this. Private string `json:"private"` } // GenerateOverlayKey makes this node's keypair for the private network. func GenerateOverlayKey() (OverlayKey, error) { private, err := ecdh.X25519().GenerateKey(rand.Reader) if err != nil { return OverlayKey{}, fmt.Errorf("cannot generate this node's overlay 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. // // That separation is what lets the mesh compose the configuration. WireGuard's `PostUp` can set a // private key from a file, so the declaration the control plane sends names this path and carries // no secret — and the file it names was written by the node, from a key nothing else ever saw. func OverlayKeyPath(statePath string) string { return dirOf(statePath) + "/overlay.key" }