A NATS server with auth_required = false accepts connections from any client without verifying identity. This means anyone who can reach the server’s client port can publish, subscribe, and access every subject — including system subjects. In production, this is a critical security misconfiguration.
An unauthenticated NATS server is an open door. Any client that can reach port 4222 (or whatever client port is configured) can connect and immediately publish to or subscribe on any subject. There is no identity verification, no access control, no audit trail. In a microservices environment, this means a compromised or rogue service can read every message flowing through the system, inject malicious messages into any subject, and impersonate any other service.
The risk compounds in multi-tenant or account-based deployments. NATS accounts provide subject namespace isolation — but accounts are an authorization feature that depends on authentication. Without authentication, account isolation doesn’t exist. All subjects across all accounts are accessible to every connection. A deployment that appears to have tenant isolation via accounts but doesn’t enforce authentication has no actual isolation at all.
This is also the default configuration. NATS server ships with authentication disabled out of the box, because it’s designed to be easy to start for development. But that development default gets deployed to production more often than anyone admits — through oversight, through “temporary” staging configs that become permanent, or through a config change that accidentally removes the auth block. Insights flags this continuously because the cost of missing it is total exposure of your messaging layer.
Default configuration deployed to production. The NATS server starts with no authentication by default. If no authorization, accounts, or operator block is added to the configuration, the server accepts anonymous connections. This is the most common cause — a default or minimal config that was never hardened.
Auth block commented out or removed during debugging. An operator disables authentication temporarily to troubleshoot a connection issue, then forgets to re-enable it. The server continues running without auth, and nothing breaks visibly because clients that were already configured with credentials still connect fine.
Dev/staging configuration deployed to production. A configuration file intended for local development or staging — where auth is intentionally relaxed — is deployed to a production server through a CI/CD pipeline error or manual mistake.
Configuration file override. The server is started with a -c flag pointing to a different config file than expected, or an include directive pulls in a file that overrides the auth settings. The operator believes auth is configured, but the effective configuration disagrees.
Incomplete migration to JWT authentication. The team started migrating from static auth to operator/JWT mode but didn’t complete the migration on all servers. Some servers still run the old config without an operator block.
# Quick check via server infonats server infoLook for the Auth Required field. If it shows false, the server accepts unauthenticated connections.
curl -s http://localhost:8222/varz | jq '.auth_required'A response of false confirms the server is not enforcing authentication.
# List all servers and their configurationnats server listCheck each server in the list. It’s possible for some cluster members to require auth while others don’t, especially during rolling configuration changes.
The simplest test: try to connect without any credentials:
# If this succeeds, auth is not requirednats pub test.subject "hello" --server nats://your-server:4222If the publish succeeds without specifying any user, token, NKey, or credentials file, the server is accepting anonymous connections.
# Review the server config file directlycat /etc/nats/nats-server.conf
# Or check where the server is loading config fromps aux | grep nats-serverLook for authorization, accounts, or operator blocks. If none are present, authentication is disabled.
The fastest way to secure a running server is to add token authentication and reload:
1authorization {2 token: "your-secure-token-here"3}# Generate a bcrypted password for the confignats server passwd
# Reload config without restarting (no downtime)nats-server --signal reloadWarning: Enabling auth on a running server will disconnect all clients that don’t present valid credentials on their next reconnect. Coordinate with application teams before enabling.
Token auth is a stopgap. For production, choose from several authentication methods. Avoid using no_auth_user in production — it silently allows unauthenticated connections by mapping them to a default user, defeating the purpose of authentication.
NKey authentication (recommended for static deployments):
1authorization {2 users: [3 { nkey: UABC123... } # User's public NKey4 ]5}1// Go client connecting with NKey2opt, err := nats.NkeyOptionFromSeed("path/to/user.nk")3if err != nil {4 log.Fatal(err)5}6nc, err := nats.Connect("nats://your-server:4222", opt)7if err != nil {8 log.Fatal(err)9}1# Python (nats.py) connecting with NKey2import nats3import os4
5async def connect():6 seed = open("path/to/user.nk", "r").read().strip()7 nc = await nats.connect(8 "nats://your-server:4222",9 nkey=seed,10 )11 return ncJWT/operator mode (recommended for multi-tenant or dynamic deployments):
1operator: /path/to/operator.jwt2resolver: {3 type: full4 dir: /path/to/jwt5 allow_delete: false6 interval: "2m"7}JWT mode provides decentralized authentication — accounts and users are managed through signed credentials without modifying the server configuration.
TLS certificate mapping (for environments with existing PKI):
1tls {2 cert_file: "/path/to/server-cert.pem"3 key_file: "/path/to/server-key.pem"4 ca_file: "/path/to/ca.pem"5 verify_and_map: true6}7
8authorization {9 users: [10 { user: "CN=order-service,O=MyOrg" } # Maps TLS client cert CN to NATS user11 ]12}Auth callout (for centralized policy enforcement):
Auth callout delegates authentication decisions to an external service, enabling centralized policy enforcement, integration with existing identity providers, and dynamic credential validation:
1authorization {2 auth_callout {3 issuer: "Aabc123..."4 auth_users: ["auth-service"]5 account: AUTH6 }7}Add auth checks to your deployment pipeline. Validate that every server configuration file includes an authentication block before it can be deployed:
# Simple CI check — verify auth_required isn't falsenats server info --server $SERVER_URL | grep -q "Auth Required.*true" || exit 1Use configuration management. If you manage server configs through Ansible, Terraform, or Helm, ensure the auth block is a required, non-overridable section of your templates.
Monitor continuously. Insights evaluates auth_required on every server at every collection epoch. If a server loses its auth configuration — through a bad deploy, a config reload, or a restart with wrong flags — the check fires immediately. This catches the scenarios that one-time CI checks miss.
Yes, but it requires coordination. You can add authentication to the server config and send a reload signal (nats-server --signal reload) without restarting. The server will start requiring auth for all new connections. Existing connections remain active until they reconnect. Make sure all clients are configured with valid credentials before reloading, or they’ll fail to reconnect. In a cluster, roll the change one server at a time to avoid a thundering herd of disconnections.
For static deployments with a known set of services, NKey authentication is the best balance of security and simplicity. NKeys use Ed25519 challenge-response — no passwords cross the wire, and keys can be rotated per-user. For multi-tenant deployments or environments where accounts and users change frequently, JWT/operator mode is the right choice. It decentralizes credential management and enables per-account isolation without modifying server configuration. Avoid plain text username/password in production — it’s better than nothing, but credentials are sent in the clear during the CONNECT handshake unless TLS is also enabled.
Mutual TLS (mTLS) verifies client identity via X.509 certificates at the transport layer. NATS supports mapping TLS client certificates to accounts and users. You can use mTLS as your authentication mechanism, but you still need an authorization or accounts block in the server config to map certificate subjects to NATS identities. TLS alone (without mapping) encrypts the connection but doesn’t enforce NATS-level access control.
Because the blast radius is total. A server without authentication exposes every subject, every message, and every JetStream stream to anyone who can reach the port. There’s no partial exposure — it’s all or nothing. Even in a private network, unauthenticated servers are one misconfigured firewall rule or one compromised container away from a full messaging layer breach. The severity reflects the potential impact, not the likelihood.
No. NATS accounts provide subject namespace isolation, but they’re an authorization feature that sits on top of authentication. Without authentication, the server has no way to determine which account a client belongs to. All clients connect to the default account (or the global account), and all subject isolation configured via accounts is effectively bypassed.
With 100+ always-on audit Checks from the NATS experts, Insights helps you find and fix problems before they become costly incidents.
No alert rules to write. No dashboards to maintain.
News and content from across the community