Open every link with no signer instead of following the controller's key
The controller that signed the hand-over, search, judgement, access and move asks ended (issue 1126), so a link believes none of them by having no signer, and the per-ask key it was told to follow goes with the lease. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -1120,9 +1120,6 @@ func runLink(ctx context.Context, opts options) error {
|
||||
Membership: membership,
|
||||
// And the public halves and certificate its record says (novox/hq ADR 0555 §3), read at each facts report.
|
||||
Keys: recordKeys(opts.state, trust.Trusts),
|
||||
// And no controller's key: every ask the controller would sign is refused, never verified with the membership's
|
||||
// signer (novox/hq ADR 0563 §7; issue 1126, the controller ended by its unassignment).
|
||||
ControllerKey: noControllerKey,
|
||||
// And the first have this build writes is what proves it to the launcher (ADR 0539 §9, ADR 0227 §8).
|
||||
HaveWritten: proof.haveWritten,
|
||||
// And the operator's hand-over of a directory used as found, carried by the controller from the
|
||||
|
||||
@@ -36,10 +36,6 @@ type meshTrust struct {
|
||||
// newMeshTrust trusts nothing until the vault's key is read.
|
||||
func newMeshTrust() *meshTrust { return &meshTrust{} }
|
||||
|
||||
// noControllerKey is the controller's key as this engine trusts it: none. A link told it refuses every ask signed as
|
||||
// the controller's, where a link told nothing would verify it with the membership's signer (link.HandOverCheck).
|
||||
func noControllerKey() ed25519.PublicKey { return nil }
|
||||
|
||||
// onChange names who is told when what it trusts changed.
|
||||
func (t *meshTrust) onChange(f func()) {
|
||||
t.mu.Lock()
|
||||
|
||||
@@ -76,13 +76,9 @@ const MaxHandOverBy = 200
|
||||
// HandOverCheck is what an ask is held to: the mesh's key, this node's name, the clock, and the nonces taken.
|
||||
type HandOverCheck struct {
|
||||
Signer ed25519.PublicKey
|
||||
// SignerNow, when set, is the controller's key as the engine trusts it at this ask, read per ask in place of Signer:
|
||||
// nil once the controller's lease is handed away, which refuses every ask signed by its key (novox/hq ADR 0563 §7,
|
||||
// ADR 0550 §2).
|
||||
SignerNow func() ed25519.PublicKey
|
||||
Node string
|
||||
Now func() time.Time
|
||||
Nonces *Nonces
|
||||
Node string
|
||||
Now func() time.Time
|
||||
Nonces *Nonces
|
||||
}
|
||||
|
||||
// Nonces are the nonces of asks taken, each kept until its ask expires: an ask is taken once. Kept in memory —
|
||||
@@ -162,11 +158,6 @@ func believedBytesWithin(check HandOverCheck, context string, withPath bool, bod
|
||||
return HandOverAsk{}, nil, "the ask is not a signed ask: " + err.Error()
|
||||
}
|
||||
signer := check.Signer
|
||||
if check.SignerNow != nil {
|
||||
if signer = check.SignerNow(); signer == nil {
|
||||
return HandOverAsk{}, nil, "the controller's lease is handed away, and no ask signed by its key is taken"
|
||||
}
|
||||
}
|
||||
if len(signer) != ed25519.PublicKeySize ||
|
||||
!ed25519.Verify(signer, append([]byte(context), signed.Ask...), signed.Signature) {
|
||||
return HandOverAsk{}, nil, "the ask is not signed by the mesh"
|
||||
|
||||
@@ -148,20 +148,14 @@ func TestAHandOverAskIsBelievedOnlySignedForThisNodeUnexpiredAndOnce(t *testing.
|
||||
if strings.Join(given, "; ") != "/srv/notes by jo through mesh-cli on anchor; /refused by jo through mesh-cli on anchor" {
|
||||
t.Fatalf("the handler was given %v", given)
|
||||
}
|
||||
// ADR 0563 §7, ADR 0550 §2: after the lease write the engine answers no ask signed by the controller's key. The key
|
||||
// is read per ask from what the engine trusts now; nil (the lease handed away) refuses, and nothing is handed over.
|
||||
trusted := m.Signer
|
||||
following := HandOverCheck{Signer: m.Signer, SignerNow: func() ed25519.PublicKey { return trusted }, Node: m.Node,
|
||||
Now: func() time.Time { return now }, Nonces: &Nonces{}}
|
||||
// Issue 1126: a link opened on the mesh has no signer (the controller that signed these asks ended), so an ask signed
|
||||
// by the membership's key, the controller's, is refused and nothing is handed over.
|
||||
before := len(given)
|
||||
trusted = nil
|
||||
late := signedAsk(t, key, with(func(a *HandOverAsk) { a.Expires = now.Add(time.Minute); a.Nonce = strings.Repeat("cd", 16) }))
|
||||
if a := AnswerHandOver(following, handle, late); !strings.Contains(a.Refused, "lease is handed away") || len(given) != before {
|
||||
t.Fatalf("after the lease write an ask signed by the controller was answered %+v", a)
|
||||
}
|
||||
trusted = m.Signer
|
||||
if a := AnswerHandOver(following, handle, late); a.Refused != "" || len(given) != before+1 {
|
||||
t.Fatalf("while the controller holds the lease its ask was answered %+v", a)
|
||||
opened := (&natsLink{node: m.Node}).check()
|
||||
opened.Now, opened.Nonces = func() time.Time { return now }, &Nonces{}
|
||||
late := signedAsk(t, key, with(func(a *HandOverAsk) { a.Nonce = strings.Repeat("cd", 16) }))
|
||||
if a := AnswerHandOver(opened, handle, late); !strings.Contains(a.Refused, "not signed by the mesh") || len(given) != before {
|
||||
t.Fatalf("a link opened on the mesh answered an ask signed by the controller's key: %+v", a)
|
||||
}
|
||||
// A nonce is forgotten once its ask has expired, so the set does not grow for ever.
|
||||
now = now.Add(HandOverGood + time.Second)
|
||||
@@ -192,23 +186,17 @@ func TestAHandOverIsAnsweredOnlyWhenItCarriesAReply(t *testing.T) {
|
||||
// handingLink is a quiet link that hears hand-overs, keeping the handler it was given.
|
||||
type handingLink struct {
|
||||
*quietLink
|
||||
given chan HandOverFunc
|
||||
signer chan func() ed25519.PublicKey
|
||||
given chan HandOverFunc
|
||||
}
|
||||
|
||||
func (l handingLink) HearHandOvers(h HandOverFunc) { l.given <- h }
|
||||
|
||||
// FollowSigner keeps what the link is told to verify the controller's asks with.
|
||||
func (l handingLink) FollowSigner(f func() ed25519.PublicKey) { l.signer <- f }
|
||||
|
||||
// **The link is given the queue's hand-over** when it is served: what the controller asks reaches the engine's
|
||||
// judgement, and nothing else.
|
||||
func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) {
|
||||
m, _ := aMember(t)
|
||||
l := handingLink{quietLink: newQuietLink(), given: make(chan HandOverFunc, 1), signer: make(chan func() ed25519.PublicKey, 1)}
|
||||
l := handingLink{quietLink: newQuietLink(), given: make(chan HandOverFunc, 1)}
|
||||
q := &Session{Membership: m, Timeout: time.Second}
|
||||
// The link verifies the controller's asks with the key the engine trusts now (ADR 0563 §7).
|
||||
q.ControllerKey = func() ed25519.PublicKey { return nil }
|
||||
q.HandOver = func(path, by string) (string, error) { return "the queue's: " + path + " by " + by, nil }
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
served := make(chan error, 1)
|
||||
@@ -221,14 +209,6 @@ func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) {
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("the link was never given the queue's hand-over")
|
||||
}
|
||||
select {
|
||||
case f := <-l.signer:
|
||||
if f() != nil {
|
||||
t.Fatal("the link was given another key than the queue's")
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("the link was never given the key the engine trusts now")
|
||||
}
|
||||
stop()
|
||||
<-served
|
||||
// 774 (ADR 0563 §1): a call of the node seat reaches its verb with its caller as the subject proves it, unproven on
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
@@ -39,14 +38,11 @@ func natsURL(address string) string {
|
||||
|
||||
// natsLink is this node's connection as a JetStream subscription.
|
||||
type natsLink struct {
|
||||
// mu guards signerNow (FollowSigner).
|
||||
mu sync.Mutex
|
||||
signerNow func() ed25519.PublicKey
|
||||
conn *nats.Conn
|
||||
js nats.JetStreamContext
|
||||
asking *nats.Subscription
|
||||
asked chan struct{}
|
||||
handing *nats.Subscription
|
||||
conn *nats.Conn
|
||||
js nats.JetStreamContext
|
||||
asking *nats.Subscription
|
||||
asked chan struct{}
|
||||
handing *nats.Subscription
|
||||
// searching hears the controller's terminal asking for a fresh setuid search (novox/hq issue 361).
|
||||
searching *nats.Subscription
|
||||
// judging hears the controller asking for a read-only judgement of this node's directories (issue 358).
|
||||
@@ -117,7 +113,9 @@ func dialNats(ctx context.Context, m Membership, timeout time.Duration) (Link, e
|
||||
}
|
||||
|
||||
l := &natsLink{
|
||||
conn: conn, js: js, node: m.Node, user: user, signer: m.Signer,
|
||||
// No signer: the asks these subjects carry were signed by the controller, which ended by its unassignment
|
||||
// (issue 1126), so a link opened on the mesh believes none of them (ADR 0563 §7, ADR 0550 §2).
|
||||
conn: conn, js: js, node: m.Node, user: user,
|
||||
lost: make(chan error, 1),
|
||||
asked: make(chan struct{}, 1),
|
||||
refused: make(chan string, 64),
|
||||
@@ -163,19 +161,9 @@ func (l *natsLink) hearAsks() {
|
||||
}
|
||||
}
|
||||
|
||||
// FollowSigner has every signed ask heard on this link verified with the key the engine trusts at that ask, the
|
||||
// controller's while it holds its lease and none from its hand-over (novox/hq ADR 0563 §7, ADR 0550 §2).
|
||||
func (l *natsLink) FollowSigner(f func() ed25519.PublicKey) {
|
||||
l.mu.Lock()
|
||||
l.signerNow = f
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// check is how a signed ask is believed on this link: the static membership signer until FollowSigner names the trust.
|
||||
// check is how a signed ask is believed on this link: by the link's signer, which a link opened on the mesh has none of.
|
||||
func (l *natsLink) check() HandOverCheck {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return HandOverCheck{Signer: l.signer, SignerNow: l.signerNow, Node: l.node}
|
||||
return HandOverCheck{Signer: l.signer, Node: l.node}
|
||||
}
|
||||
|
||||
// HearHandOvers listens for the controller asking this node to hand a directory used as found to the mesh
|
||||
|
||||
@@ -199,12 +199,6 @@ func serve(ctx context.Context, link Link, m Membership, queue *Session, say Ann
|
||||
}
|
||||
// And the controller asking this node to hand a directory used as found to the mesh (novox/hq issue
|
||||
// 356), answered on the link the question came over.
|
||||
// Every signed ask is verified with the key trusted at that ask, none after the lease write (ADR 0563 §7).
|
||||
if f, ok := link.(interface {
|
||||
FollowSigner(func() ed25519.PublicKey)
|
||||
}); ok && queue.ControllerKey != nil {
|
||||
f.FollowSigner(queue.ControllerKey)
|
||||
}
|
||||
if h, ok := link.(HandOvers); ok {
|
||||
h.HearHandOvers(queue.HandOver)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package link
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -54,9 +53,6 @@ type Session struct {
|
||||
// MoveSecret moves one value sealed to this machine into the vault, asked by the controller during the move of
|
||||
// the secrets (design 57's V3, issue 676). Nil answers every such ask with a refusal.
|
||||
MoveSecret MoveSecretFunc
|
||||
// ControllerKey is the controller's key as the engine trusts it now: nil once its lease is handed away (novox/hq
|
||||
// ADR 0563 §7). Each link verifies every signed ask with it, read per ask; nil ControllerKey keeps the membership's.
|
||||
ControllerKey func() ed25519.PublicKey
|
||||
// Acts is the node seat node-engine this engine holds (novox/hq ADR 0563): served on every link, and its asks to the
|
||||
// operator asked and heard over it. Nil serves no seat.
|
||||
Acts *HandActs
|
||||
|
||||
Reference in New Issue
Block a user