NATS Logo by Example

Permissions in Authentication and Authorization

Permissions are defined on users either in a server config file or when using decentralized auth with JWTs.

The types of permissions that can be set are straightforward:

  • publish - The set of subjects that a user can publish messages to
  • subscribe - The set of subjects that a user can subscribe to
  • allow_responses - A special permission type for subscribers who need to respond to clients when using the request-reply pattern

For publish and subscribe, they optionally be declared under allow or deny blocks. If no block or the allow block is used this implies everything is denied by default and only subjects listed will be allowed.

If a deny block is used, all subjects are allowed by default, except for those being denied.

For core NATS pub-sub permissions, defining permissions is straightforward since they map one-to-one with the messages being sent. However, with JetStream, there is an API that the client libraries abstract away. See the JetStream protocol here.

CLI Go Python Deno Node Rust C# Java Ruby Elixir C
Jump to the output or the recording
$ nbe run auth/perms/cli
View the source code or learn how to run this example yourself

Code

#!/bin/sh


set -euo pipefail

Define configuration with JetStream enabled and two users. The goal is to have a shared KV, but each user can only put/get keys that are scoped to them. This also leverages the private inbox pattern to ensure another user can top snoop on shared _INBOX responses. Note, the $ is escaped here to prevent bash variable substitution.

cat <<- EOF > auth.conf
jetstream: {}


authorization: {
  users: [
    {
      user: admin, password: "admin",
      permissions: {
        subscribe: ">",
        publish: ">"
      }
    },
    {
      user: router001, password: "router001",
      permissions: {
        subscribe: [
          "_INBOX_router001.>"
        ],
        publish: [
          "\$JS.API.STREAM.INFO.KV_test_kv",
          "\$KV.test_kv.dev.001.>",
          "\$JS.API.DIRECT.GET.KV_test_kv.\$KV.test_kv.dev.001.>"
        ]
      }
    },
    {
      user: router002, password: "router002",
      permissions: {
        subscribe: [
          "_INBOX_router002.>"
        ],
        publish: [
          "\$JS.API.STREAM.INFO.KV_test_kv",
          "\$KV.test_kv.dev.002.>",
          "\$JS.API.DIRECT.GET.KV_test_kv.\$KV.test_kv.dev.002.>"
        ]
      }
    },


  ]
}
EOF

Start the servers and sleep to startup.

nats-server -c auth.conf > /dev/null 2>&1 &


sleep 1

Save a context for each user.

nats context save \
  --user admin \
  --password admin \
  admin


nats context save \
  --user router001 \
  --password router001 \
  --inbox-prefix _INBOX_router001 \
  router001


nats context save \
  --user router002 \
  --password router002 \
  --inbox-prefix _INBOX_router002 \
  router002

Create a KV “test_kv” with admin user.

nats --context=admin kv add \
  --history=1 \
  --ttl=0 \
  --replicas=1 \
  --max-value-size=-1 \
  --max-bucket-size=-1 \
  --storage=file \
  test_kv

Have router001 user put and get a key.

nats --context=router001 \
  kv put test_kv 'dev.001.temp' '80c'


nats --context=router001 \
  kv get test_kv 'dev.001.temp'

Have router002 attempt to get dev.001 key.

nats --context=router002 \
  kv get test_kv 'dev.001.temp'

Output

Network 5da35085_default  Creating
Network 5da35085_default  Created
NATS Configuration Context "admin"

      Server URLs: nats://127.0.0.1:4222
         Username: admin
         Password: *********
            Token: admin
             Path: /nsc/.config/nats/context/admin.json

NATS Configuration Context "router001"

      Server URLs: nats://127.0.0.1:4222
         Username: router001
         Password: *********
            Token: router001
     Inbox Prefix: _INBOX_router001
             Path: /nsc/.config/nats/context/router001.json

NATS Configuration Context "router002"

      Server URLs: nats://127.0.0.1:4222
         Username: router002
         Password: *********
            Token: router002
     Inbox Prefix: _INBOX_router002
             Path: /nsc/.config/nats/context/router002.json

Information for Key-Value Store Bucket test_kv created 2022-10-21T15:20:46Z

Configuration:

          Bucket Name: test_kv
         History Kept: 1
        Values Stored: 0
   Backing Store Kind: JetStream
  Maximum Bucket Size: unlimited
   Maximum Value Size: unlimited
     JetStream Stream: KV_test_kv
              Storage: File
80c
test_kv > dev.001.temp created @ 21 Oct 22 15:20 UTC

80c

15:20:46 Unexpected NATS error from server nats://127.0.0.1:4222: nats: Permissions Violation for Publish to "$JS.API.DIRECT.GET.KV_test_kv.$KV.test_kv.dev.001.temp"
nats: error: context deadline exceeded
exit status 1

Recording

Note, playback is half speed to make it a bit easier to follow.