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 tosubscribe
- The set of subjects that a user can subscribe toallow_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.
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