diff --git a/pkg/embeddednats/embeddednats.go b/pkg/embeddednats/embeddednats.go index 7291fdad..114352ad 100644 --- a/pkg/embeddednats/embeddednats.go +++ b/pkg/embeddednats/embeddednats.go @@ -6,22 +6,33 @@ package embeddednats import ( + "crypto/tls" "fmt" "time" server "github.com/nats-io/nats-server/v2/server" ) -// Start launches an embedded nats-server with JetStream enabled, listening on -// the given port and persisting JetStream state under storeDir. The listen host -// is left at the nats-server default ("0.0.0.0", all interfaces). It blocks -// until the server is ready to accept connections (up to 5s) and returns the -// running server. The caller is responsible for calling Shutdown on it. -// -// Start is a thin backward-compatible wrapper over StartHost; callers that need -// to control the bind interface (loopback vs LAN) should use StartHost directly. +// ServerConfig is the full set of knobs for the embedded NATS server. The zero +// value (empty StoreDir aside) yields a dev-friendly server: JetStream on, bound +// to all interfaces, no client auth, no TLS. Secured deployments set Auth and +// TLS; tests set Host to loopback and a free Port. +type ServerConfig struct { + StoreDir string // JetStream store directory + Host string // bind interface; "" = nats-server default ("0.0.0.0") + Port int // listen port + // Auth, when non-nil, is installed as CustomClientAuthentication so the data + // plane only accepts approved clients (nkey signature + bus allowlist). + Auth server.Authentication + // TLS, when non-nil, makes the server present a certificate and require TLS + // on the data plane. Clients must trust the issuing CA (see busauth). + TLS *tls.Config +} + +// Start is a thin backward-compatible wrapper: embedded JetStream server on the +// default interface, no auth, no TLS. func Start(storeDir string, port int) (*server.Server, error) { - return StartHost(storeDir, "", port) + return StartServer(ServerConfig{StoreDir: storeDir, Port: port}) } // StartHost is Start with explicit control over the bind interface. host selects @@ -30,34 +41,42 @@ func Start(storeDir string, port int) (*server.Server, error) { // to expose it to the LAN so remote peers (phones, other PCs) can connect. An // empty host falls back to the nats-server default ("0.0.0.0", all interfaces). func StartHost(storeDir, host string, port int) (*server.Server, error) { - return StartHostAuth(storeDir, host, port, nil) + return StartServer(ServerConfig{StoreDir: storeDir, Host: host, Port: port}) } // StartHostAuth is StartHost with an optional custom client authenticator. When -// auth is non-nil it is installed as Options.CustomClientAuthentication, so the -// data plane only accepts clients the authenticator approves (nkey signature + -// bus allowlist). When auth is nil the server accepts any client (the legacy, -// network-trusted behavior) — used by dev stacks and tests that have not enabled -// bus auth. +// auth is non-nil only clients the authenticator approves may connect; when nil +// the server accepts any client (legacy, network-trusted behavior). func StartHostAuth(storeDir, host string, port int, auth server.Authentication) (*server.Server, error) { + return StartServer(ServerConfig{StoreDir: storeDir, Host: host, Port: port, Auth: auth}) +} + +// StartServer launches an embedded nats-server with JetStream from cfg. It +// blocks until the server is ready to accept connections (up to 5s) and returns +// the running server; the caller must Shutdown it. +func StartServer(cfg ServerConfig) (*server.Server, error) { opts := &server.Options{ JetStream: true, - StoreDir: storeDir, - Host: host, - Port: port, + StoreDir: cfg.StoreDir, + Host: cfg.Host, + Port: cfg.Port, DontListen: false, // Keep the embedded server quiet by default; the host app logs the URLs. NoLog: true, NoSigs: true, } - if auth != nil { - opts.CustomClientAuthentication = auth + if cfg.Auth != nil { + opts.CustomClientAuthentication = cfg.Auth // A CustomClientAuthentication alone does not make the server advertise a // nonce in its INFO line, and nats.go refuses to connect with an nkey to a // server that does not ("nkeys not supported by the server"). Forcing the // nonce makes nkey clients sign the challenge our authenticator verifies. opts.AlwaysEnableNonce = true } + if cfg.TLS != nil { + opts.TLSConfig = cfg.TLS + opts.TLS = true + } ns, err := server.NewServer(opts) if err != nil {