The control plane declares, and hears back
`declare` sends a node a signed declaration; `serve` now also consumes reports. Signed over the exact bytes published, which is what the node verifies. Anything re-encoding in between would sign one thing and check another, and a difference in key order alone would have a node refuse a declaration that was genuinely the mesh's. Sent to the node's queue directly rather than through the exchange: a declaration is for one node, and routing by name through a shared exchange means a binding per node that nothing removes when a node is retired. Enrolment now issues the node its own broker password, replacing the token's secret, and tells it the broker address, the fingerprint and the signing key -- so a node can reconnect after a restart without a person and a new token, which is what makes disconnection ordinary rather than a crisis. A report is a statement, not a write. What a node says it applied is its own account of its own machine, kept as a copy for recovery rather than as a source.
This commit is contained in:
@@ -70,6 +70,8 @@ func run() error {
|
||||
return brokerCommand(args[1:])
|
||||
case "serve":
|
||||
return serve(ctx)
|
||||
case "declare":
|
||||
return declare(ctx, args[1:])
|
||||
case "version":
|
||||
fmt.Println(version)
|
||||
return nil
|
||||
@@ -93,6 +95,7 @@ func usage() {
|
||||
identity show this control plane's signing key
|
||||
broker show where the broker is, and what to expect there
|
||||
serve consume what nodes say, and answer
|
||||
declare <node> <file> send a node a signed declaration
|
||||
version what this binary is
|
||||
|
||||
Each context reaches its own store through its own credential (novox/hq ADR 0008), named
|
||||
@@ -389,7 +392,19 @@ func serve(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := link.Connect(link.Enrolment{Inventory: inv, Identity: ident, Broker: management})
|
||||
// Where the broker is and what to expect there, so a node can be told how to come back
|
||||
// without a person and a new token.
|
||||
known, err := broker.FromEnvironment()
|
||||
if err != nil && !errors.Is(err, broker.ErrNotConfigured) {
|
||||
return err
|
||||
}
|
||||
if errors.Is(err, broker.ErrNotConfigured) {
|
||||
fmt.Printf("no broker address configured, so enrolled nodes will not be told how to "+
|
||||
"reconnect. Set %s and %s.\n", broker.AddressVar, broker.CertificateVar)
|
||||
}
|
||||
|
||||
server, err := link.Connect(link.Enrolment{
|
||||
Inventory: inv, Identity: ident, Management: management, Broker: known})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -397,3 +412,50 @@ func serve(ctx context.Context) error {
|
||||
|
||||
return server.Serve(ctx)
|
||||
}
|
||||
|
||||
// declare sends one node a declaration, signed.
|
||||
//
|
||||
// Signed here rather than trusted from the broker: a node connects to the broker and takes
|
||||
// instruction from the control plane behind it, and those are two identities. If a node believed
|
||||
// whatever arrived on its queue, a compromised broker could forge declarations — and since the
|
||||
// host applies whatever the link delivers, that is the whole machine (novox/hq ADR 0004).
|
||||
func declare(ctx context.Context, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("declare <node> <declaration.json>")
|
||||
}
|
||||
node, path := args[0], args[1]
|
||||
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ident, err := openIdentity(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ident.Close()
|
||||
|
||||
// The node has to exist before it can be told anything. Publishing to a queue nobody consumes
|
||||
// would sit there looking like success.
|
||||
inv, err := openInventory(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inv.Close()
|
||||
if _, err := inv.NodeByName(ctx, node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := link.Connect(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
if err := link.Declare(ctx, server.Channel(), ident, node, raw, 15*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("sent %s a signed declaration (%d bytes)\n", node, len(raw))
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user