From e313ac9d3b06e753c73fb56ee6b99adeae17d1f2 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 15:33:20 +0100 Subject: [PATCH] =?UTF-8?q?route-proxy:=20placement=20is=20read=20through?= =?UTF-8?q?=20the=20module=20assigner's=20client,=20and=20a=20bus=20addres?= =?UTF-8?q?s=20is=20said=20without=20its=20credential=20by=20the=20proxy's?= =?UTF-8?q?=20own=20helper=20(N-1,=20ADR=200634=20=C2=A74;=20issue=201966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- .../cmd/route-proxy/bareaddress_test.go | 18 ++++++++++ modules/route-proxy/cmd/route-proxy/bus.go | 35 +++++++++++++------ .../route-proxy/cmd/route-proxy/reach_test.go | 5 +-- modules/route-proxy/go.mod | 2 ++ 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 modules/route-proxy/cmd/route-proxy/bareaddress_test.go diff --git a/modules/route-proxy/cmd/route-proxy/bareaddress_test.go b/modules/route-proxy/cmd/route-proxy/bareaddress_test.go new file mode 100644 index 0000000..f27c010 --- /dev/null +++ b/modules/route-proxy/cmd/route-proxy/bareaddress_test.go @@ -0,0 +1,18 @@ +package main + +import "testing" + +// A message that says where the proxy could not connect never carries the account's password. +func TestABusAddressIsSaidWithoutItsCredential(t *testing.T) { + for in, want := range map[string]string{ + "nats://route-proxy:secret@127.0.0.1:4222": "nats://127.0.0.1:4222", + "route-proxy:secret@127.0.0.1:4222": "127.0.0.1:4222", + "nats://u:a@b@127.0.0.1:4222": "nats://127.0.0.1:4222", // the password holds an at-sign + "nats://127.0.0.1:4222": "nats://127.0.0.1:4222", + "127.0.0.1:4222": "127.0.0.1:4222", + } { + if got := bareAddress(in); got != want { + t.Errorf("bareAddress(%q) = %q, want %q", in, got, want) + } + } +} diff --git a/modules/route-proxy/cmd/route-proxy/bus.go b/modules/route-proxy/cmd/route-proxy/bus.go index b81e00d..a7724a0 100644 --- a/modules/route-proxy/cmd/route-proxy/bus.go +++ b/modules/route-proxy/cmd/route-proxy/bus.go @@ -10,11 +10,10 @@ import ( "github.com/nats-io/nats.go" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/announce" - "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-sdk/go/membership" "git.novox.be/novox/mesh-sdk/go/process" "route-proxy/contract" @@ -35,8 +34,8 @@ func keptStates() []announce.State { // **The routes are the seat mesh-provisioner's plans, alone** (issue 774, research 072 row 34): every plan's entry // naming this proxy as a route's provider, followed, so a route added or withdrawn reaches a running proxy without a // restart, and said on the bus as it changes (contract.EventServed, EventWithdrawn, EventRefused). **Who is inside** -// is every node's address on the private network as the seat mesh-placement decides it (mesh-sdk -// delivery.InsideAddresses), which is who may be served an internal name; until it is read, an internal name is +// is every node's address on the private network as the seat mesh-placement decides it (the module assigner's +// client, InsideAddresses), which is who may be served an internal name; until it is read, an internal name is // served to this machine alone. // follow connects with the credential in path, follows the plans and the placement, and retries the connection for @@ -59,7 +58,7 @@ func followOnce(path string, held *table) error { } conn, err := process.Connect(account, nats.Name(account.Node+"."+account.Module)) if err != nil { - return fmt.Errorf("connecting to the bus at %s: %w", membership.BareAddress(account.URL), err) + return fmt.Errorf("connecting to the bus at %s: %w", bareAddress(account.URL), err) } // What this proxy keeps, said on the bus (novox/hq ADR 0495; issue 774): the names it serves and the names it // refused, readable by any module granted a read of them; the provider helper's entries, read by the machines' @@ -75,8 +74,8 @@ func followOnce(path string, held *table) error { } } // Who is inside first, so the first table is served to the machines it is meant for. - if err := followInside(conn.Read(delivery.PlacementRead), held); err != nil { - log.Printf("cannot follow %s, so an internal name is served to this machine alone: %v", delivery.PlacementRead, err) + if err := followInside(conn.Read(meshmoduleassignerclient.PlacementRead), held); err != nil { + log.Printf("cannot follow %s, so an internal name is served to this machine alone: %v", meshmoduleassignerclient.PlacementRead, err) } if err := followPlans(conn.Read(meshprovisionerclient.PlansRead), newPlanned(account.Node, say), held, say); err != nil { conn.Close() @@ -160,14 +159,14 @@ func sortedNames(m map[string]contract.Event) []string { } // followInside follows the seat mesh-placement's state and serves an internal name to every node's address on the -// private network it decides, each time it changes (mesh-sdk delivery.InsideAddresses; research 072 rows 33 and 34: +// private network it decides, each time it changes (the module assigner client's InsideAddresses; research 072 rows 33 and 34: // the membership that carried them went with the assignments stream). A key that does not read keeps who is inside // as it was. func followInside(placement planWatch, held *table) error { - keys := delivery.NewPlacementKeys() + keys := meshmoduleassignerclient.NewKeys() var read atomic.Bool apply := func() { - inside, err := sourcesOf(delivery.InsideAddresses(keys.All())) + inside, err := sourcesOf(meshmoduleassignerclient.InsideAddresses(keys.All())) if err != nil { log.Printf("the placement says an address that is not one, so who is inside stays as it was: %v", err) return @@ -192,3 +191,19 @@ func followInside(placement planWatch, held *table) error { apply() return nil } + +// bareAddress is a bus address without the credential it may carry, `[scheme://]user:password@host:port`, for a message +// that says where the proxy could not connect: the password is never written in a log. It splits on the last at-sign, +// so a password holding one stays out whole. (Its only reader is this proxy: it was the SDK's membership package's, +// which keeps the runtime's read of its own membership and nothing else.) +func bareAddress(address string) string { + at := strings.LastIndex(address, "@") + if at < 0 { + return address + } + scheme := "" + if i := strings.Index(address[:at], "://"); i >= 0 { + scheme = address[:i+3] + } + return scheme + address[at+1:] +} diff --git a/modules/route-proxy/cmd/route-proxy/reach_test.go b/modules/route-proxy/cmd/route-proxy/reach_test.go index a6960e1..1f58fe5 100644 --- a/modules/route-proxy/cmd/route-proxy/reach_test.go +++ b/modules/route-proxy/cmd/route-proxy/reach_test.go @@ -15,8 +15,9 @@ import ( "strings" "testing" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" "route-proxy/contract" ) @@ -305,7 +306,7 @@ func TestThePlansAreServedAndThePlacementSaysWhoIsInside(t *testing.T) { if code, _ := askFrom(held, "admin.anchor.internal", "10.10.0.7:1"); code != http.StatusNotFound { t.Errorf("before the placement is read, a machine of the mesh was served an internal-only name: %d", code) } - laptop, err := delivery.PlacementValues(delivery.Placement{Node: "laptop", Decided: delivery.NodeDecided{Address: "10.10.0.7"}}) + laptop, err := meshmoduleassignerclient.PlacementValues(seat.Placement{V: meshmoduleassignerclient.Version, Node: "laptop", Decided: seat.NodeDecided{Address: "10.10.0.7"}}) if err != nil { t.Fatal(err) } diff --git a/modules/route-proxy/go.mod b/modules/route-proxy/go.mod index d44eae4..3133873 100644 --- a/modules/route-proxy/go.mod +++ b/modules/route-proxy/go.mod @@ -3,6 +3,8 @@ module route-proxy go 1.26.0 require ( + git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-20261105000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-20261105000000-000000000000 git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-20261105000000-000000000000 git.novox.be/novox/mesh-contracts/provisioning/go/seats/meshprovisioner v0.0.0-20261105000000-000000000000 git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f