Skip to content

Security

NATS' security model is decentralized and identity-first. Every connection presents a credential signed by an account key, signed by an operator key, anchored at a trusted operator NKey configured on each server.

Authentication

NATS supports several auth modes; production deployments should use mode (c):

Mode Use Case
(a) None Local dev only.
(b) Username/password or token Small deployments, quick start.
(c) Decentralized JWT (Operator → Account → User) Production, multi-tenant, supercluster.
(d) Static NKey Headless services where rotating creds is hard; no JWT, but cryptographic.
(e) TLS client certificate mTLS-only setups where PKI is the source of truth.
(f) External callout auth_callout block delegates to an external Ed25519-signed service.

Decentralized JWT model

flowchart LR
    Op["Operator NKey (O)\nroot of trust"]
    SAcc["System Account ($SYS)"]
    AccA["Account A"]
    AccB["Account B"]
    UA["User A1"]
    UA2["User A2"]
    UB["User B1"]
    Op -->|signs| SAcc
    Op -->|signs| AccA
    Op -->|signs| AccB
    AccA -->|signs| UA
    AccA -->|signs| UA2
    AccB -->|signs| UB
    Resolver["Account JWT resolver"]
    AccA -. JWT .- Resolver
    AccB -. JWT .- Resolver
    SAcc -. JWT .- Resolver
    Server["nats-server\ntrust = Operator pubkey"]
    Resolver --> Server
  • Operator NKey — Ed25519 keypair. The public key is configured on every server (operator: O...).
  • Account JWT — issued by the operator (or a delegated signing key). Contains permissions, limits, exports, imports.
  • User JWT — issued by the account. Contains pub/sub permissions, response permissions, max subscriptions.
  • Resolver — the server fetches Account JWTs at runtime from MEMORY, the embedded NATS resolver, or a URL.

NKey-only auth

When even JWT bookkeeping is too heavy, an NKey alone can authenticate. The server stores authorized public keys; clients sign a server-issued nonce with their private key.

Authorization

Permissions live in user JWTs:

{
  "pub": { "allow": ["app.events.>"], "deny": [] },
  "sub": { "allow": ["app.cmd.>"],   "deny": ["app.cmd.admin.>"] },
  "resp": { "max": 1, "ttl": "5s" }
}
Concept Effect
pub.allow / deny Subjects this user may publish to.
sub.allow / deny Subjects this user may subscribe to.
resp Special "allow responses" rule for request-reply patterns — see --allow-pub-response on nsc edit signing-key.
Account exports/imports Cross-account flows. Can be public (any account can import) or private (per-import token).

Subject-level isolation

Two accounts cannot see each other's subjects, full stop. There is no privileged subject. This is the cornerstone of multi-tenant NATS.

Limits enforced server-side

  • Max subscriptions per user / account.
  • Max payload, max in-flight requests.
  • Max JetStream storage (memory + file) per account.
  • Max gateway + leaf connections per account.

Encryption

In transit

  • TLS 1.2 / 1.3 on every listener (clients, routes, gateways, leaf nodes, MQTT, WS, monitoring).
  • Per-listener configuration: a hub may require mTLS for gateway peers but optional TLS for clients.
  • Cipher suites configurable; restrict to AEAD only.
tls {
  cert_file: "/etc/nats/server.pem"
  key_file:  "/etc/nats/server.key"
  ca_file:   "/etc/nats/ca.pem"
  verify:    true
  verify_and_map: true   # map cert SAN to user identity
  cipher_suites: ["TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256"]
}

At rest (JetStream)

JetStream supports per-account at-rest encryption with two cipher options:

jetstream {
  store_dir: /data/jetstream
  cipher: chachapoly      # or "aes"
  key: "<base64 32 bytes>" # or use key_file
}
  • All blocks, indexes, and consumer state files are encrypted with a per-server symmetric key.
  • Re-keying requires a re-encrypt sweep; document it in your operations runbook.

Server-to-server

Cluster routes, gateways, and leaf nodes each have their own TLS configuration block — required for any prod deployment.

Audit & Observability

  • Connection events are published on the system account: $SYS.ACCOUNT.<acc-id>.CONNECT, DISCONNECT, AUTH_ERROR.
  • Account auth tracing can be enabled with trace_destinations to forward to a separate audit subject.
  • Server logs capture failed auth and permission denials; ship to your SIEM.
  • JetStream advisories publish on $JS.EVENT.ADVISORY.> — message-loss, consumer-leader-changes, stream-create/delete.

Threat Model

Threat Mitigation
Operator key theft Keep operator key offline / in an HSM. Use account signing keys for routine operations.
Account JWT forgery Mitigated by Operator-signed JWTs; servers validate each chain.
User JWT theft Issue short-lived JWTs (expires_at); rotate user creds; revoke via account-level revocations.
Subject leakage across accounts Architectural impossibility — subjects are account-scoped. Verify exports/imports are correctly bounded.
Slow-consumer DoS write_deadline, max_pending, and slow-consumer eviction.
JetStream storage tampering At-rest encryption (chachapoly/aes); FS-level integrity (dm-verity / fs-verity); audit JetStream advisory subjects.
MITM on cluster routes Require route-level mTLS. Servers refuse non-TLS routes when tls is set on the route block.
Compromised leaf node Leaf is account-scoped; its subjects can only reach what the leaf account can already see.
Replay of published messages At app level: include nonce / dedupe via JetStream Nats-Msg-Id header (dupe_window).
Gateway spoofing Gateway authentication uses TLS + (optionally) auth.users block; rotate certs.
Resolver poisoning Pin resolver TLS certificate; resolver responses are themselves Operator-signed JWTs.

CVE History

NATS has had a relatively clean security record. Notable items:

CVE Year Affected Summary
CVE-2024-39830 2024 nats-server before 2.10.16 Improper validation of user permissions could allow request-reply auth bypass under specific permission patterns. Fixed in 2.10.16.
CVE-2023-47092 2023 NATS account JWT Improper handling of certain account JWT claims; fixed in 2.10.x.
CVE-2022-24450 2022 nats-server 2.7.x Privileges over system account leaked from a non-system context in certain cluster configs.

Always pin to the latest patch in your minor line and subscribe to the nats-io/nats-server security advisories feed.

Hardening Checklist

  • Operator key kept offline; routine signing uses account signing keys.
  • mTLS on every listener (clients, routes, gateways, leaf nodes).
  • System account user creds restricted to ops tooling only.
  • Per-account JetStream storage encryption enabled.
  • Per-user expires_at on JWTs; revocations list maintained.
  • Connection event subjects ($SYS.ACCOUNT.*.CONNECT) shipped to SIEM.
  • Slow-consumer alarms wired to oncall (Prometheus exporter nats_consumer_slow_consumer_count).
  • Resolver TLS certificate pinned; only the operator can publish JWT updates.
  • No PII in subject tokens.
  • auth_callout (if used) deployed in HA with mTLS to nats-server.

Cross-references