Checks/SERVER_007

NATS Authentication Not Required: What It Means and How to Fix It

Severity
Critical
Category
Health
Applies to
Server
Check ID
SERVER_007
Detection threshold
auth_required = false

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.

Why this matters

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.

Common causes

  • 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.

How to diagnose

Check the server’s auth status

Terminal window
# Quick check via server info
nats server info

Look for the Auth Required field. If it shows false, the server accepts unauthenticated connections.

Check via the monitoring endpoint

Terminal window
curl -s http://localhost:8222/varz | jq '.auth_required'

A response of false confirms the server is not enforcing authentication.

Verify across the entire cluster

Terminal window
# List all servers and their configuration
nats server list

Check each server in the list. It’s possible for some cluster members to require auth while others don’t, especially during rolling configuration changes.

Test by connecting without credentials

The simplest test: try to connect without any credentials:

Terminal window
# If this succeeds, auth is not required
nats pub test.subject "hello" --server nats://your-server:4222

If the publish succeeds without specifying any user, token, NKey, or credentials file, the server is accepting anonymous connections.

Check the effective server configuration

Terminal window
# Review the server config file directly
cat /etc/nats/nats-server.conf
# Or check where the server is loading config from
ps aux | grep nats-server

Look for authorization, accounts, or operator blocks. If none are present, authentication is disabled.

How to fix it

Immediate: enable authentication

The fastest way to secure a running server is to add token authentication and reload:

nats-server.conf
1
authorization {
2
token: "your-secure-token-here"
3
}
Terminal window
# Generate a bcrypted password for the config
nats server passwd
# Reload config without restarting (no downtime)
nats-server --signal reload

Warning: 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.

Short-term: implement proper authentication

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):

nats-server.conf
1
authorization {
2
users: [
3
{ nkey: UABC123... } # User's public NKey
4
]
5
}
1
// Go client connecting with NKey
2
opt, err := nats.NkeyOptionFromSeed("path/to/user.nk")
3
if err != nil {
4
log.Fatal(err)
5
}
6
nc, err := nats.Connect("nats://your-server:4222", opt)
7
if err != nil {
8
log.Fatal(err)
9
}
1
# Python (nats.py) connecting with NKey
2
import nats
3
import os
4
5
async 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 nc

JWT/operator mode (recommended for multi-tenant or dynamic deployments):

nats-server.conf
1
operator: /path/to/operator.jwt
2
resolver: {
3
type: full
4
dir: /path/to/jwt
5
allow_delete: false
6
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):

nats-server.conf
1
tls {
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: true
6
}
7
8
authorization {
9
users: [
10
{ user: "CN=order-service,O=MyOrg" } # Maps TLS client cert CN to NATS user
11
]
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:

nats-server.conf
1
authorization {
2
auth_callout {
3
issuer: "Aabc123..."
4
auth_users: ["auth-service"]
5
account: AUTH
6
}
7
}

Long-term: enforce auth as policy

Add auth checks to your deployment pipeline. Validate that every server configuration file includes an authentication block before it can be deployed:

Terminal window
# Simple CI check — verify auth_required isn't false
nats server info --server $SERVER_URL | grep -q "Auth Required.*true" || exit 1

Use 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.

Frequently asked questions

Is it safe to enable auth on a running NATS server?

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.

What authentication method should I use for production?

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.

Can I use mutual TLS instead of NATS-level authentication?

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.

Why is this check critical and not just a warning?

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.

Does account isolation work without authentication?

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.

Proactive monitoring for NATS authentication not required with Synadia Insights

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.

Start a 14-day Insights trial
Cancel