Authorization Overview
NATS Server authorization controls what authenticated users can publish and subscribe to. Permissions are granted at the subject level, providing fine-grained access control.
Permissions System
The permission system operates on two dimensions:
- Publish Permissions - Control which subjects a user can publish messages to
- Subscribe Permissions - Control which subjects a user can subscribe to
Each permission dimension supports:
- Allow rules - Explicitly permit access to subjects
- Deny rules - Explicitly prohibit access to subjects (override allow rules)
Basic Permission Structure
permissions {
publish {
allow: ["subject1", "subject2.>"]
deny: ["subject2.admin"]
}
subscribe {
allow: ["results.>", "_INBOX.>"]
deny: ["results.internal.>"]
}
}
Subject Permission Rules
Wildcards
NATS supports two wildcard types:
* - Matches a single token
> - Matches one or more tokens (must be last)
Examples:
permissions {
publish {
allow: [
"events.*", # events.login, events.logout
"logs.app.>" # logs.app.error, logs.app.info.user
]
}
}
Deny Override
Deny rules always override allow rules:
permissions {
publish {
allow: ["data.>"]
deny: ["data.sensitive.>"]
}
}
This user can publish to data.public but NOT data.sensitive.passwords.
User-Level Permissions
Username/Password Users
authorization {
users = [
{
user: "publisher"
password: "$2a$11$..."
permissions: {
publish: ["events.>"]
subscribe: [] # Cannot subscribe
}
}
{
user: "subscriber"
password: "$2a$11$..."
permissions: {
publish: ["_INBOX.>"]
subscribe: ["events.>"]
}
}
]
}
NKey Users
authorization {
users = [
{
nkey: "UCKASD5KPQQYHB6KYD7RC62VZQN7VRU5NN2BKL7UFBQ3UBYAJQPVNHSQ"
permissions: {
publish: {
allow: ["orders.>"]
deny: ["orders.cancel"]
}
subscribe: {
allow: ["results.>", "_INBOX.>"]
}
}
}
]
}
Account-Based Authorization
Accounts provide complete isolation between groups of users.
Multi-Account Configuration
accounts {
PROD: {
users: [
{
user: "prod_user"
password: "$2a$11$..."
permissions: {
publish: ["production.>"]
subscribe: ["production.>"]
}
}
]
}
DEV: {
users: [
{
user: "dev_user"
password: "$2a$11$..."
permissions: {
publish: ["dev.>"]
subscribe: ["dev.>"]
}
}
]
}
}
Users in different accounts cannot communicate unless explicit account imports/exports are configured.
Account Isolation
Accounts provide:
- Subject namespace isolation -
production.orders in PROD is separate from production.orders in DEV
- Resource isolation - Connection and subscription limits per account
- Security boundaries - Complete separation between tenants
Response Permissions
Allow clients to respond to request-reply messages dynamically:
authorization {
users = [
{
user: "service"
password: "$2a$11$..."
permissions: {
publish: {}
subscribe: ["requests.>"]
responses: {
max: 100 # Max responses allowed
ttl: "10s" # Response permission expires after 10s
}
}
}
]
}
Response Permission Behavior
- User subscribes to
requests.service
- Request arrives with reply subject
_INBOX.abc123
- User granted temporary publish permission to
_INBOX.abc123
- Permission expires after TTL or max messages reached
Default Response Limits
From source code (server/auth.go:256-261):
DEFAULT_ALLOW_RESPONSE_MAX_MSGS = 1
DEFAULT_ALLOW_RESPONSE_EXPIRATION = 2 * time.Minute
Permission Examples
Read-Only Consumer
{
user: "consumer"
password: "$2a$11$..."
permissions: {
publish: ["_INBOX.>"] # Only reply subjects
subscribe: ["events.>", "data.>"]
}
}
Publisher-Only
{
user: "publisher"
password: "$2a$11$..."
permissions: {
publish: ["events.app.>"]
subscribe: [] # No subscriptions allowed
}
}
Request-Reply Service
{
user: "api_service"
password: "$2a$11$..."
permissions: {
subscribe: ["api.requests.>"]
responses: {
max: 1
ttl: "30s"
}
}
}
Admin User
{
user: "admin"
password: "$2a$11$..."
permissions: {
publish: {
allow: [">"]
deny: ["$SYS.>"]
}
subscribe: {
allow: [">"]
deny: ["$SYS.>"]
}
}
}
Service-Specific Permissions
{
user: "order_service"
password: "$2a$11$..."
permissions: {
publish: {
allow: [
"orders.created",
"orders.updated",
"orders.cancelled"
]
}
subscribe: {
allow: [
"orders.commands.>",
"_INBOX.>"
]
}
}
}
Connection Type Restrictions
Restrict users to specific connection types:
authorization {
users = [
{
user: "mqtt_only"
password: "$2a$11$..."
allowed_connection_types: ["MQTT"]
permissions: {
publish: ["sensors.>"]
subscribe: ["commands.>"]
}
}
]
}
Supported connection types:
STANDARD - Regular NATS clients
WEBSOCKET - WebSocket clients
LEAFNODE - Leaf node connections
MQTT - MQTT clients
Route Permissions
Control what subjects can be imported/exported between servers in a cluster:
cluster {
name: "production"
permissions: {
import: {
allow: ["public.>"]
deny: ["public.internal.>"]
}
export: {
allow: ["public.>"]
deny: ["public.internal.>"]
}
}
}
Account Imports and Exports
Accounts can selectively share subjects:
Export from Account
accounts {
SERVICE: {
exports: [
{stream: "service.api.>"} # Public API
{service: "service.request.>"} # Request-reply service
]
}
}
Import into Account
accounts {
CLIENT: {
imports: [
{stream: {account: "SERVICE", subject: "service.api.>"}, prefix: "external.service"}
{service: {account: "SERVICE", subject: "service.request.>"}, to: "service.req"}
]
}
}
Permission Validation
From server/auth.go implementation:
Publish Check
- Check deny list first - if matched, reject
- If allow list empty, default permit
- If allow list exists, subject must match
Subscribe Check
- Check deny list first - if matched, reject
- If allow list empty, default permit
- If allow list exists, subject must match
Testing Permissions
Test permissions before deploying:
# Test publish permission
nats-cli pub test.subject "hello" --creds=user.creds
# Test subscribe permission
nats-cli sub test.subject --creds=user.creds
Best Practices
1. Principle of Least Privilege
Grant minimal required permissions:
# Good - Specific permissions
permissions: {
publish: ["metrics.service1.>"]
subscribe: ["config.service1.>"]
}
# Bad - Overly permissive
permissions: {
publish: [">"]
subscribe: [">"]
}
2. Use Deny Rules for Exceptions
permissions: {
publish: {
allow: ["logs.>"]
deny: ["logs.sensitive.>"]
}
}
3. Organize by Account
Separate environments and tenants:
accounts {
PROD: { ... }
STAGING: { ... }
DEV: { ... }
}
4. Enable Response Permissions
For request-reply patterns:
responses: {
max: 1
ttl: "5s"
}