feat(embeddednats): nkey CustomClientAuthentication against the allowlist

busauth.NewNkeyAuthenticator verifies a client's nkey signature over the
server nonce (decoding like nats-server: raw-url then std base64), maps the
nkey to its Ed25519 hex, and consults an injected IsAuthorized predicate.
Checking on every connection (rather than a static Options.Nkeys map) means
revoking a user denies its next connection with no restart. embeddednats
gains StartHostAuth(auth) and sets AlwaysEnableNonce so the server advertises
the nonce nkey clients need; Start/StartHost stay open (auth=nil) for dev.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 12:37:46 +02:00
parent 413dd61041
commit b09bafe242
2 changed files with 75 additions and 0 deletions
+18
View File
@@ -30,6 +30,16 @@ 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)
}
// 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.
func StartHostAuth(storeDir, host string, port int, auth server.Authentication) (*server.Server, error) {
opts := &server.Options{
JetStream: true,
StoreDir: storeDir,
@@ -40,6 +50,14 @@ func StartHost(storeDir, host string, port int) (*server.Server, error) {
NoLog: true,
NoSigs: true,
}
if auth != nil {
opts.CustomClientAuthentication = 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
}
ns, err := server.NewServer(opts)
if err != nil {