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_destinationsto 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_aton 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¶
- messaging/nats/architecture — for the Operator/Account/User trust diagram.
- messaging/nats/operations — for
nsccommands and JWT push procedures. - messaging/index — for cross-broker security comparison.