Files
mesh-catalog/modules/nats/internal/busconn/busconn.go
T
jschoubben 35ff67bda0
mesh/delivery-group group feat/774-l3-reland rejected: composed together: the checker's verdict at the members' heads (b48c90fea31f): integration: mesh-self-ch…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: mesh-self-check/cmd/mesh-self-check.TestTheSelfCheckWholeRunsOnTheBus: the run answered {Run:doctor-1793351737-1 At:20…
Design 47's last milestones: the keeper, self-check, healers, hand-acts, provisioner, vault, artifact sweep, disk readings, credential forms (issues 774, 1952, 1953, 1954, 1955)
One commit with exactly the tree of the reviewed head 35b4509c0a, on main 12c2574c9d, so two compiled programs added and removed on the branch leave no trace in history (issue 1577).
2026-10-30 02:21:51 +01:00

107 lines
4.0 KiB
Go

// Package busconn dials the bus as a credential the mesh sealed to a module: the one place the module's
// programs learn how (the snapshot, and the tools that change the bus).
package busconn
import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/nats-io/nats.go"
)
// Credential is what the mesh seals to a module so it can reach the bus (mesh-controller
// cmd/mesh-controller issueWith): where, the server's certificate by fingerprint, who, and the
// password. The address is not used here — this runs beside the server, and the server is reached on
// loopback — but the fingerprint is: loopback or not, the certificate is the mesh's own.
type Credential struct {
URL string `json:"url"`
Fingerprint string `json:"fingerprint,omitempty"`
User string `json:"user"`
Password string `json:"password"`
}
func Read(path string) (Credential, error) {
var raw []byte
var err error
if path == "-" {
raw, err = io.ReadAll(io.LimitReader(os.Stdin, 1<<20))
} else {
raw, err = os.ReadFile(path)
}
if err != nil {
return Credential{}, fmt.Errorf("cannot read the credential: %w", err)
}
var c Credential
if err := json.Unmarshal(raw, &c); err != nil {
return Credential{}, errors.New("the credential is not the JSON the mesh seals to a module (url, fingerprint, user, password)")
}
if c.User == "" || c.Password == "" {
return Credential{}, errors.New("the credential names no user or password, so the bus would refuse it")
}
return c, nil
}
// Connect dials the bus as the credential's user. A `tls://` server is held to the credential's
// fingerprint and nothing else — the mesh's bus presents a certificate of its own, in no trust store,
// so a name check could only fail or be skipped. A plain `nats://` server is accepted only on
// loopback, and is what a restore's own private server is.
func Connect(server string, c Credential, name string) (*nats.Conn, error) {
opts := []nats.Option{
nats.UserInfo(c.User, c.Password),
nats.Name(name),
// Answers come to the user's own inbox and no other: the bus grants every user its own
// `_INBOX.<user>.>` and nothing wider (novox/hq design 25 §4).
nats.CustomInboxPrefix("_INBOX." + c.User),
nats.Timeout(10 * time.Second),
// One attempt: a program that cannot reach the bus fails, loudly, rather than waiting for it.
nats.NoReconnect(),
}
switch {
case strings.HasPrefix(server, "tls://"):
if c.Fingerprint == "" {
return nil, errors.New("the credential carries no fingerprint, so the bus's certificate cannot be checked")
}
opts = append(opts, nats.Secure(PinnedTo(c.Fingerprint)))
case strings.HasPrefix(server, "nats://127.0.0.1:"), strings.HasPrefix(server, "nats://localhost:"):
if c.Fingerprint != "" {
// The mesh's bus requires TLS; a plain connection to it would be refused anyway.
opts = append(opts, nats.Secure(PinnedTo(c.Fingerprint)))
}
default:
return nil, fmt.Errorf("%s: the bus is reached over tls:// (or plain nats:// on loopback, for a restore's own server)", server)
}
nc, err := nats.Connect(server, opts...)
if err != nil {
return nil, fmt.Errorf("cannot reach the bus at %s as %s: %w", server, c.User, err)
}
return nc, nil
}
// PinnedTo accepts exactly the certificate with this SHA-256 — the same pin every machine holds
// (mesh-controller internal/broker PinnedToFingerprint).
func PinnedTo(want string) *tls.Config {
return &tls.Config{
InsecureSkipVerify: true, //nolint:gosec // replaced by the pin below, which is stricter
MinVersion: tls.VersionTLS12,
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return errors.New("the bus presented no certificate")
}
sum := sha256.Sum256(rawCerts[0])
if got := "sha256:" + hex.EncodeToString(sum[:]); got != want {
return fmt.Errorf("the bus presented a certificate this mesh does not know (%s)", got)
}
return nil
},
}
}