Unify trunk on main: initialization → main #3
@@ -28,6 +28,9 @@ func Path(statePath string) string {
|
||||
return filepath.Join(filepath.Dir(statePath), FileName)
|
||||
}
|
||||
|
||||
// dirOf is where a node keeps everything it knows about itself.
|
||||
func dirOf(statePath string) string { return filepath.Dir(statePath) }
|
||||
|
||||
// Identity is this node's own keypair, the name the mesh knows it by, and what it needs to get
|
||||
// back to that mesh without a person.
|
||||
//
|
||||
@@ -44,6 +47,10 @@ type Identity struct {
|
||||
Private []byte `json:"private"`
|
||||
|
||||
Membership Membership `json:"membership"`
|
||||
|
||||
// Overlay is this node's key on the private network. Generated here, like the identity above,
|
||||
// and for the same reason: the mesh computes a graph it cannot impersonate.
|
||||
Overlay OverlayKey `json:"overlay"`
|
||||
}
|
||||
|
||||
// Membership is how this node reaches the mesh it belongs to, and who it believes.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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"
|
||||
}
|
||||
Reference in New Issue
Block a user