The vault's own on a real bus: a node-engine gets its own secrets sealed to its stated key, another machine cannot ask in its name, the controller is refused (issue 1126, ADR 0550 §5)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 87d94d96cca1 on da29f77b1081 | novox/mesh-catalog#505 in group feat/1126-nodes-compose
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 87d94d96cca1 on da29f77b1081 | novox/mesh-catalog#505 in group feat/1126-nodes-compose
This commit is contained in:
@@ -5,6 +5,8 @@ package main
|
||||
// key (or a first one) raises `secret-changed` for the machine's core secrets, the same key says nothing.
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -100,14 +102,17 @@ authorization {
|
||||
users = [
|
||||
{ user: "novox.mesh-vault", password: "pw", permissions: {
|
||||
publish: { deny: [">"] }
|
||||
subscribe: { allow: ["mesh.seat.mesh-vault.call.node-key.*"] }
|
||||
subscribe: { allow: ["mesh.seat.mesh-vault.call.node-key.*", "mesh.seat.mesh-vault.call.own.*"] }
|
||||
allow_responses: true } }
|
||||
{ user: "node.ace", password: "pw", permissions: {
|
||||
publish: { allow: ["mesh.seat.mesh-vault.call.node-key.node_ace"] }
|
||||
publish: { allow: ["mesh.seat.mesh-vault.call.node-key.node_ace", "mesh.seat.mesh-vault.call.own.node_ace"] }
|
||||
subscribe: { allow: ["_INBOX.node.ace.>"] } } }
|
||||
{ user: "node.g14", password: "pw", permissions: {
|
||||
publish: { allow: ["mesh.seat.mesh-vault.call.node-key.node_g14"] }
|
||||
publish: { allow: ["mesh.seat.mesh-vault.call.node-key.node_g14", "mesh.seat.mesh-vault.call.own.node_g14"] }
|
||||
subscribe: { allow: ["_INBOX.node.g14.>"] } } }
|
||||
{ user: "controller", password: "pw", permissions: {
|
||||
publish: { allow: ["mesh.seat.mesh-vault.call.own.controller"] }
|
||||
subscribe: { allow: ["_INBOX.controller.>"] } } }
|
||||
]
|
||||
}
|
||||
`
|
||||
@@ -135,39 +140,47 @@ func TestOnARealBusANodeEngineStatesOnlyItsOwnKey(t *testing.T) {
|
||||
t.Cleanup(s.Shutdown)
|
||||
|
||||
v := newVault(t, nil)
|
||||
tool := toolNamed(t, definitions(&Env{Vault: v.Vault}), Seat+".node-key")
|
||||
tools := map[string]toolDef{"node-key": toolNamed(t, definitions(&Env{Vault: v.Vault}), Seat+".node-key"),
|
||||
"own": toolNamed(t, definitions(&Env{Vault: v.Vault}), Seat+".own")}
|
||||
vaultConn := connectAs(t, s.ClientURL(), "novox.mesh-vault")
|
||||
if _, err := vaultConn.Subscribe("mesh.seat.mesh-vault.call.node-key.*", func(m *nats.Msg) {
|
||||
serve := func(m *nats.Msg) {
|
||||
parts := strings.Split(m.Subject, ".")
|
||||
var args map[string]any
|
||||
_ = json.Unmarshal(m.Data, &args)
|
||||
answer := busAnswer{}
|
||||
if got, err := tool.runAs(untoken(parts[len(parts)-1]), args); err != nil {
|
||||
if got, err := tools[parts[len(parts)-2]].runAs(untoken(parts[len(parts)-1]), args); err != nil {
|
||||
answer.Error = err.Error()
|
||||
} else {
|
||||
answer.Result, _ = json.Marshal(got)
|
||||
}
|
||||
raw, _ := json.Marshal(answer)
|
||||
_ = m.Respond(raw)
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, verb := range []string{"node-key", "own"} {
|
||||
if _, err := vaultConn.Subscribe("mesh.seat.mesh-vault.call."+verb+".*", serve); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_ = vaultConn.Flush()
|
||||
|
||||
say := func(user, subject, key string) (string, error) {
|
||||
raw, _ := json.Marshal(map[string]string{"key": key})
|
||||
call := func(user, subject string, args any) (json.RawMessage, error) {
|
||||
raw, _ := json.Marshal(args)
|
||||
msg, err := connectAs(t, s.ClientURL(), user).Request(subject, raw, 3*time.Second)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
var a busAnswer
|
||||
_ = json.Unmarshal(msg.Data, &a)
|
||||
if a.Error != "" {
|
||||
return a.Error, errString(a.Error)
|
||||
return nil, errString(a.Error)
|
||||
}
|
||||
return "", nil
|
||||
return a.Result, nil
|
||||
}
|
||||
aceKey, _ := aSealingKey(t)
|
||||
say := func(user, subject, key string) (string, error) {
|
||||
_, err := call(user, subject, map[string]string{"key": key})
|
||||
return "", err
|
||||
}
|
||||
aceKey, acePriv := aSealingKey(t)
|
||||
g14Key, _ := aSealingKey(t)
|
||||
if _, err := say("node.ace", "mesh.seat.mesh-vault.call.node-key.node_ace", aceKey); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -188,4 +201,32 @@ func TestOnARealBusANodeEngineStatesOnlyItsOwnKey(t *testing.T) {
|
||||
if got, held, _ := v.Store.NodeKey("g14"); !held || got != g14Key {
|
||||
t.Errorf("g14's key is %q, %v", got, held)
|
||||
}
|
||||
|
||||
// Issue 1126 (ADR 0547 §2, ADR 0550 §5): `own` on the same bus. node.ace asks its own and gets its module's secret
|
||||
// sealed to the key it stated, opening with its key alone; g14 cannot ask in ace's name, and asking in its own gets
|
||||
// none of ace's; the controller is refused, as no node-engine.
|
||||
if _, err := moveIn(t, v, "node.ace", MoveOf{Node: "ace", Module: "web", Name: "password", Origin: "made", Generation: "g-1",
|
||||
SealingKey: aceKey}, "ace-web-pw"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, err := call("node.ace", "mesh.seat.mesh-vault.call.own.node_ace", map[string]any{})
|
||||
var own OwnAnswer
|
||||
if err != nil || json.Unmarshal(raw, &own) != nil || own.Node != "ace" || len(own.Own) != 1 {
|
||||
t.Fatalf("ace's own over the bus: %s, %v", raw, err)
|
||||
}
|
||||
if value, err := seal.Open(own.Own[0].Sealed, acePriv); err != nil || string(value) != "ace-web-pw" {
|
||||
t.Errorf("ace's secret does not open with ace's key: %q, %v", value, err)
|
||||
}
|
||||
if strings.Contains(string(raw), "ace-web-pw") {
|
||||
t.Error("ace's secret crossed the bus in the clear")
|
||||
}
|
||||
if _, err := call("node.g14", "mesh.seat.mesh-vault.call.own.node_ace", map[string]any{}); err == nil {
|
||||
t.Error("node.g14 asked ace's own in ace's name")
|
||||
}
|
||||
if raw, err := call("node.g14", "mesh.seat.mesh-vault.call.own.node_g14", map[string]any{}); err != nil || strings.Contains(string(raw), "web") {
|
||||
t.Errorf("g14 asking its own was answered %s, %v", raw, err)
|
||||
}
|
||||
if _, err := call("controller", "mesh.seat.mesh-vault.call.own.controller", map[string]any{}); err == nil || !strings.Contains(err.Error(), "not a node-engine") {
|
||||
t.Errorf("the controller asking own: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user