Vault: take the node-logins key and the enrolment token from mesh-sdk's contract
The vault wrote its own copies of the NodeLogin key and the token; the node-engine and the grants part read them through mesh-sdk's codec (delivery.EncodeNodeLogin refuses a key without its version, a login that is not the node's, a stage without a rotation). It now writes and reads through that codec and builds the token with delivery.Token, so the shapes are one (ADR 0583 §1 insight, ADR 0587 §3). Built against the group's mesh-sdk with a go.work outside the worktree; go.mod still pins the earlier mesh-sdk, for the lead to re-pin. (issue 774, 774/S43, 774/S46, 774/S49) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -31,9 +32,9 @@ import (
|
||||
// The names enrolment gives: the module that holds the seat mesh-enrolment, the prefix of a token's bus user, and the
|
||||
// vault's state of each node's logins.
|
||||
const (
|
||||
EnrolmentModule = "mesh-enrolment"
|
||||
EnrolUserPrefix = "enrol."
|
||||
NodeLoginsState = "node-logins"
|
||||
EnrolmentModule = delivery.EnrolmentModule
|
||||
EnrolUserPrefix = delivery.EnrolmentUserPrefix
|
||||
NodeLoginsState = delivery.NodeLoginsState
|
||||
// MeshHostModule is the module a node's own login secret is kept under (`own/<node>/mesh-host/<login>`, ADR 0583 §3).
|
||||
MeshHostModule = "mesh-host"
|
||||
)
|
||||
@@ -130,41 +131,9 @@ func (v *Vault) fromEnrolment(caller, verb string) error {
|
||||
// TokenBase is what a token states beside its secret: where the bus is and the certificate to expect there, and the
|
||||
// machine's first tunnel when the token carries one.
|
||||
type TokenBase struct {
|
||||
Broker string `json:"broker"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Tunnel *TokenTunnel `json:"tunnel,omitempty"`
|
||||
}
|
||||
|
||||
// TokenTunnel is the joining machine's side of its first tunnel, in the wire format the node-engine parses.
|
||||
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"`
|
||||
}
|
||||
|
||||
// EnrolToken is the token a person carries to a joining machine, in the wire format of the node-engine's parser
|
||||
// (mesh-host internal/identity.Token): version, node, the bus's address and certificate fingerprint, the vault's public
|
||||
// signing key, the one-time secret, and the tunnel when there is one.
|
||||
type EnrolToken struct {
|
||||
Version int `json:"v"`
|
||||
Node string `json:"node,omitempty"`
|
||||
Broker string `json:"broker,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Signer []byte `json:"signer,omitempty"`
|
||||
Secret string `json:"secret"`
|
||||
Adopted bool `json:"adopted,omitempty"`
|
||||
Tunnel *TokenTunnel `json:"tunnel,omitempty"`
|
||||
}
|
||||
|
||||
// Text is the token as a person types it: base64url of its JSON.
|
||||
func (t EnrolToken) Text() (string, error) {
|
||||
raw, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(raw), nil
|
||||
Broker string `json:"broker"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Tunnel *delivery.TokenTunnel `json:"tunnel,omitempty"`
|
||||
}
|
||||
|
||||
// EnrolmentTokenArgs are `enrolment-token`'s arguments: the node, when it expires (the seat's word, echoed), and which
|
||||
@@ -268,8 +237,8 @@ func (v *Vault) showEnrolmentToken(caller, node string) (EnrolmentTokenAnswer, e
|
||||
if err != nil {
|
||||
return EnrolmentTokenAnswer{}, fmt.Errorf("the token carries the vault's signing key, which is not held yet: %w", err)
|
||||
}
|
||||
text, err := EnrolToken{Version: 1, Node: node, Broker: base.Broker, Fingerprint: base.Fingerprint,
|
||||
Signer: []byte(keys.key.Public().(ed25519.PublicKey)), Secret: secret, Tunnel: base.Tunnel}.Text()
|
||||
text, err := delivery.Token{Version: 1, Node: node, Broker: base.Broker, Fingerprint: base.Fingerprint,
|
||||
Signer: []byte(keys.key.Public().(ed25519.PublicKey)), Secret: secret, Tunnel: base.Tunnel}.Encode()
|
||||
if err != nil {
|
||||
return EnrolmentTokenAnswer{}, err
|
||||
}
|
||||
|
||||
@@ -35,7 +35,10 @@ func (l *loginsState) Get(node string) (*NodeLogin, error) {
|
||||
func (l *loginsState) Put(n NodeLogin) error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
raw, _ := json.Marshal(n)
|
||||
raw, err := delivery.EncodeNodeLogin(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if field := credentialField(raw); field != "" {
|
||||
return fmt.Errorf("%s carries a field %q, which names a credential", n.Node, field)
|
||||
}
|
||||
@@ -152,7 +155,7 @@ func TestTheVaultMakesAnEnrolmentsTokenAccountAndSignature(t *testing.T) {
|
||||
t.Fatalf("show answered %+v, %v", shown, err)
|
||||
}
|
||||
text := openOperator(t, e.operatorPublic, e.operatorPrivate, shown.Sealed)
|
||||
var tok EnrolToken
|
||||
var tok delivery.Token
|
||||
body, _ := base64.RawURLEncoding.DecodeString(text)
|
||||
signer, _ := e.keys()
|
||||
if err := json.Unmarshal(body, &tok); err != nil || tok.Version != 1 || tok.Secret != secret || tok.Node != "new1" ||
|
||||
|
||||
@@ -29,35 +29,28 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// The events a rotation says, and the stages of a node's key.
|
||||
// The events a rotation says and the stages of a node's key are mesh-sdk's (delivery/node_logins.go); a rotation warranted
|
||||
// by a hand act is warranted for a day.
|
||||
const (
|
||||
RotationBegunEvent = "rotation-begun"
|
||||
NodeAccountReadyEvent = "node-account-ready"
|
||||
RotationDoneEvent = "rotation-done"
|
||||
RotationBegunEvent = delivery.RotationBegunEvent
|
||||
NodeAccountReadyEvent = delivery.NodeAccountReadyEvent
|
||||
RotationDoneEvent = delivery.RotationDoneEvent
|
||||
|
||||
StageBegun = "begun"
|
||||
StageReady = "ready"
|
||||
StageConfirmed = "confirmed"
|
||||
StageBegun = delivery.RotationBegun
|
||||
StageReady = delivery.RotationReady
|
||||
StageConfirmed = delivery.RotationConfirmed
|
||||
|
||||
// HandActWithin is how recent a hand act is that warrants a rotation.
|
||||
HandActWithin = 24 * time.Hour
|
||||
)
|
||||
|
||||
// NodeLogin is one key of the vault's state `node-logins`: the login the node's node-engine dials as, and, while a
|
||||
// rotation runs, the other login and how far it has come. Never a value or a hash.
|
||||
type NodeLogin struct {
|
||||
Node string `json:"node"`
|
||||
Login string `json:"login"`
|
||||
// Rotating is the other login while a rotation runs; empty reads as none.
|
||||
Rotating string `json:"rotating,omitempty"`
|
||||
Stage string `json:"stage,omitempty"`
|
||||
Since time.Time `json:"since,omitzero"`
|
||||
// ListRevision is the revision of the loaded user list the rotation began at.
|
||||
ListRevision uint64 `json:"list-revision,omitempty"`
|
||||
}
|
||||
// NodeLogin is one key of the vault's state node-logins (mesh-sdk's contract): the login the node's node-engine dials as,
|
||||
// and while a rotation runs the other login with its stage. Never a value or a hash.
|
||||
type NodeLogin = delivery.NodeLogin
|
||||
|
||||
// NodeLoginStore is the vault's state `node-logins`, by node.
|
||||
type NodeLoginStore interface {
|
||||
@@ -75,15 +68,19 @@ func (busNodeLogins) Get(node string) (*NodeLogin, error) {
|
||||
if err != nil || e == nil {
|
||||
return nil, err
|
||||
}
|
||||
var n NodeLogin
|
||||
if err := json.Unmarshal(e.Value, &n); err != nil {
|
||||
n, err := delivery.DecodeNodeLogin(e.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s is not a node's login: %w", NodeLoginsState, node, err)
|
||||
}
|
||||
return &n, nil
|
||||
}
|
||||
|
||||
func (busNodeLogins) Put(n NodeLogin) error {
|
||||
_, err := stdio.State(NodeLoginsState).Put(n.Node, n)
|
||||
raw, err := delivery.EncodeNodeLogin(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = stdio.State(NodeLoginsState).Put(n.Node, json.RawMessage(raw))
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user