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