diff --git a/cmd/mesh-controller/build.go b/cmd/mesh-controller/build.go index 1506400..a612778 100644 --- a/cmd/mesh-controller/build.go +++ b/cmd/mesh-controller/build.go @@ -216,6 +216,11 @@ func builderCommand(ctx context.Context, args []string) error { } defer inv.Close() + brokerAddr, err := brokerReachableAt(ctx, inv, known, *forNode) + if err != nil { + return err + } + // The URL and what verifies the broker, together. A mesh's broker presents a certificate // of the mesh's own, which is in no public trust store — so a URL on its own reaches only // a broker somebody else vouches for, and the connection fails at TLS with an error about @@ -228,7 +233,7 @@ func builderCommand(ctx context.Context, args []string) error { URL string `json:"url"` Fingerprint string `json:"fingerprint,omitempty"` }{ - URL: fmt.Sprintf("amqps://%s:%s@%s/", name, password, known.Address), + URL: fmt.Sprintf("amqps://%s:%s@%s/", name, password, brokerAddr), Fingerprint: known.Fingerprint, }) if err != nil { diff --git a/cmd/mesh-controller/modules.go b/cmd/mesh-controller/modules.go index 4a12a0b..bacdba2 100644 --- a/cmd/mesh-controller/modules.go +++ b/cmd/mesh-controller/modules.go @@ -8,6 +8,7 @@ import ( "errors" "flag" "fmt" + "net" "os" "sort" "strings" @@ -283,6 +284,10 @@ func moduleCommand(ctx context.Context, args []string) error { if err != nil { return fmt.Errorf("cannot deliver a credential without knowing where the broker is: %w", err) } + brokerAddr, err := brokerReachableAt(ctx, inv, known, *forNode) + if err != nil { + return err + } // The URL and what verifies the broker, together — a mesh's broker presents its own // certificate, in no public trust store, so a URL alone fails at TLS (as `builder issue`). held, err := json.Marshal(struct { @@ -291,7 +296,7 @@ func moduleCommand(ctx context.Context, args []string) error { Node string `json:"node"` Module string `json:"module"` }{ - URL: fmt.Sprintf("amqps://%s:%s@%s/", account, password, known.Address), + URL: fmt.Sprintf("amqps://%s:%s@%s/", account, password, brokerAddr), Fingerprint: known.Fingerprint, // The node and module the account is for, so the runtime names its queue as the mesh // scoped it (..events) without a manifest having to interpolate a node. @@ -461,3 +466,37 @@ func pinCommand(ctx context.Context, args []string, setting bool) error { fmt.Printf(" run `push %s` to send it\n", args[0]) return nil } + +// brokerReachableAt is the broker's address as the given node can reach it. +// +// The genesis address (MESH_BROKER_ADDRESS) is the broker's public endpoint — reachable from the +// control-node itself, but not routed to another node, whose firewall admits only the overlay +// (from:mesh). The foundation, and so the broker, sits on the control-node, which is the overlay +// hub; a node that is on the overlay reaches the broker by the hub's `.internal` name, which the +// firewall admits and every node resolves. A node not yet on the overlay — at genesis, before any +// `overlay place`, which is when the builder's account is issued — keeps the genesis address it +// was given, so nothing about bring-up changes. This is issue 055. +func brokerReachableAt(ctx context.Context, inv *inventory.Inventory, known broker.Broker, node string) (string, error) { + overlays, err := inv.Overlays(ctx) + if err != nil { + return "", err + } + var hub string + onOverlay := false + for _, o := range overlays { + if o.Hub && o.Address != "" { + hub = o.Name + } + if o.Name == node && o.Address != "" { + onOverlay = true + } + } + if hub == "" || !onOverlay { + return known.Address, nil + } + _, port, err := net.SplitHostPort(known.Address) + if err != nil { + return known.Address, nil + } + return net.JoinHostPort(overlay.InternalName(hub), port), nil +}