-
Notifications
You must be signed in to change notification settings - Fork 0
ADR 25_nats_nkey
MaxThom edited this page Sep 5, 2025
·
1 revision
Extend CLI to have a server list
Checkout NSC and do some testing to manage a server https://docs.nats.io/using-nats/nats-tools/nsc
To use NKeys, we also need to make it great to use. We need to private the set of tools in the MirCLI to generate keys.
We need to be able to add new tenant (account) and add device (user) to the account.
Hierarchy:
- Operators are responsible for running nats-servers, and issuing account JWTs. Operators set the limits on what an account can do, such as the number of connections, data limits, etc.
- Accounts are responsible for issuing user JWTs. An account defines streams and services that can be exported to other accounts. Likewise, they import streams and services from other accounts.
- Users are issued by an account, and encode limits regarding usage and authorization over the account's subject space.
o default to context name
u default to context url
a default to mir
--no-exec just print the commands
mir tools security init --operator <o> --url <u> --account <a>
nsc add operator --generate-signing-key --sys --name <o>
nsc edit operator --service-url <u> --account-jwt-server-url <u>
nsc add account -n <a>
mir tools security edit --operator <o> --url <u>
nsc env -o <o>
nsc edit operator --service-url <u> --account-jwt-server-url <u>
path default to ./resolver.conf, must handle with or without filename
mir tools security generate-resolver --operator <o> <path>
nsc env -o <o>
nsc generate config --nats-resolver > <path>/resolver.conf
mir tools security env -o <o>
nsc env -o <o>
mir tools security pull --operator <o>
nsc env -o <o>
nsc pull -A
mir tools security push --operator <o>
nsc env -o <o>
nsc push -A
mir tools security generate-creds --operator <o> --account <a> <user>
nsc env -o <o>
nsc generate creds -a <a> -n <user>
mir tools security add client --operator <o> --account <a> <name> --readonly
nsc env -o <o>
nsc add user -a <a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.core.v1alpha.list" \
--allow-pub "client.*.cmd.v1alpha.list" \
--allow-pub "client.*.cfg.v1alpha.list" \
--allow-pub "client.*.tlm.v1alpha.list" \
--allow-pub "client.*.evt.v1alpha.list"
mir tools security add client --operator <o> --account <a> <name>
nsc env -o <o>
nsc add user -a <a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.>"
mir tools security add client --operator <o> --account <a> <name> --swarm
nsc env -o <o>
nsc add user -a <a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.>" \
--allow-pub "device.*.>" \
--allow-sub "*.>"
mir tools security add device --operator <o> --account <a> <name>
nsc env -o <o>
nsc add user -a <a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pub "device.<name>.>" \
--allow-sub "<name>.>"
mir tools security add device --operator <o> --account <a> <name> --wildcard
nsc env -o <o>
nsc add user -<a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pub "device.*.>" \
--allow-sub "*.>"
mir tools security add module --operator <o> --account <a> <name>
nsc add user -a <a> -n <name> \
--allow-pubsub "_INBOX.>" \
--allow-pubsub "client.*.>" \
--allow-pubsub "event.*.>" \
--allow-sub "device.*.>" \
--allow-pub "*.>"# Create operator with SYS user
nsc add operator --generate-signing-key --sys --name mir
# Set serving URL
nsc edit operator --service-url nats://localhost:4222 --account-jwt-server-url nats://localhost:4222
# See which operator and account in use and paths
nsc env
# Info
nsc describe operator mir
nsc describe account mir
# List keys
nsc list keys --all
# Generate resolver config
nsc generate config --nats-resolver > resolver.conf
# Start server with config below
nats-server -c config.conf
# Listen
nats sub --creds ~/.local/share/nats/nsc/keys/creds/mir/SYS/sys.creds ">"
nsc sub --user sys ">"
# Publish
nats pub --creds ~/.local/share/nats/nsc/keys/creds/mir/SYS/sys.creds hello NATS
nsc pub --user sys hello NATS
## Should not user SYS account, add new one
# Add account and user
nsc add account -n mir
nsc add user -a mir -n mir
# Update server with new auth
nsc push -a mir
# Pull config from server locally
nsc pull -A
# Test
nsc sub --user mir ">"
nsc pub --user mir hello NATSconfig.conf
server_name: servertest
listen: 127.0.0.1:4222
http: 8222
jetstream: enabled
include resolver.conf# Read only
nsc [add|edit] user -a mir -n opread \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.core.v1alpha.list" \
--allow-pub "client.*.cmd.v1alpha.list" \
--allow-pub "client.*.cfg.v1alpha.list" \
--allow-pub "client.*.tlm.v1alpha.list" \
--allow-pub "client.*.evt.v1alpha.list"
# Read write
nsc [add|edit] user -a mir -n oprw \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.core.v1alpha.list" \
--allow-pub "client.*.core.v1alpha.create" \
--allow-pub "client.*.core.v1alpha.update" \
--allow-pub "client.*.core.v1alpha.delete" \
--allow-pub "client.*.cmd.v1alpha.list" \
--allow-pub "client.*.cmd.v1alpha.send" \
--allow-pub "client.*.cfg.v1alpha.list" \
--allow-pub "client.*.cfg.v1alpha.send" \
--allow-pub "client.*.tlm.v1alpha.list" \
--allow-pub "client.*.evt.v1alpha.list" \
--allow-pub "client.*.evt.v1alpha.delete"
# Read write simplified
nsc [add|edit] user -a mir -n oprw \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.>"
# Read write simplified with swarm capability
nsc [add|edit] user -a mir -n oprw \
--allow-pubsub "_INBOX.>" \
--allow-pub "client.*.>" \
--allow-pub "device.*.>" \
--allow-sub "*.>"# * can be replaced with device id
nsc [add|edit] user -a mir -n device \
--allow-pubsub "_INBOX.>" \
--allow-pub "device.*.core.v1alpha.heartbeat" \
--allow-pub "device.*.core.v1alpha.schema" \
--allow-pub "device.*.tlm.v1alpha.proto" \
--allow-pub "device.*.cfg.v1alpha.proto" \
--allow-pub "device.*.cfg.v1alpha.desiredproperties" \
--allow-sub "*.v1alpha.schema" \
--allow-sub "*.v1alpha.config" \
--allow-sub "*.v1alpha.command" \
# Simplified
# * can be replaced with device id
nsc [add|edit] user -a mir -n device \
--allow-pubsub "_INBOX.>" \
--allow-pub "device.*.>" \
--allow-sub "*.>"nsc [add|edit] user -a mir -n module \
--allow-pubsub "_INBOX.>" \
--allow-pubsub "client.*.>" \
--allow-pubsub "event.*.>" \
--allow-sub "device.*.>" \
--allow-pub "*.>"This guide sets up NATS authentication using NKeys with two isolated accounts:
- MirDev: Development environment
- MirTest: Testing/staging environment
# Generate MirDev account
nk -gen account -pubout > mirdev.nk
# Example output:
# SAABXI3YM7XQL5J5VQZZYGGLLH3WPTGBUBP5QHHEV2GFHCF7LHCOZ4DEV
# ADDEV3JXYTXOJEL6LNAXDREUGRX35BOLZI3B4PFFAC7IRPR3OA4QNKDEV
# Generate MirTest account
nk -gen account -pubout > mirtest.nk
# Example output:
# SAACYX2KEOOQ52BMGX3W5F6L5OXQZJDVWT3GVWL3Y7HXSGVQRH2EATEST
# ACTEST7PZTKJSBTR7BF6TBK3D776734PWHWDKO7HFMQOM5BIOYPSTESTNote: The first line is the seed (private key starting with 'S'), the second is the public key (starting with 'A').
Create nats-server.conf:
port: 4222
accounts {
MirDev: {
nkey: ADDEV3JXYTXOJEL6LNAXDREUGRX35BOLZI3B4PFFAC7IRPR3OA4QNKDEV
}
MirTest: {
nkey: ACTEST7PZTKJSBTR7BF6TBK3D776734PWHWDKO7HFMQOM5BIOYPSTEST
}
}# Create directory
mkdir -p /etc/mir
chmod 700 /etc/mir
# Extract just the seeds (first line)
head -n1 mirdev.nk > /etc/mir/mirdev.seed
head -n1 mirtest.nk > /etc/mir/mirtest.seed
# Set restrictive permissions
chmod 600 /etc/mir/*.seed
# Remove original files with both keys
rm mirdev.nk mirtest.nkpackage main
import (
"log"
"github.com/nats-io/nats.go"
)
func main() {
// Connect to MirDev
ncDev, err := nats.Connect("nats://localhost:4222",
nats.NkeyOptionFromSeed("/etc/mir/mirdev.seed"))
if err != nil {
log.Fatal("Dev connection failed:", err)
}
defer ncDev.Close()
// Connect to MirTest
ncTest, err := nats.Connect("nats://localhost:4222",
nats.NkeyOptionFromSeed("/etc/mir/mirtest.seed"))
if err != nil {
log.Fatal("Test connection failed:", err)
}
defer ncTest.Close()
}# Start NATS server
nats-server -c nats-server.conf
# Test MirDev account (terminal 1)
nats sub --nkey=/etc/mir/mirdev.seed ">"
# Test MirTest account (terminal 2)
nats pub --nkey=/etc/mir/mirtest.seed test.subject "hello"
# Note: Messages published in MirTest won't be visible in MirDev subscription1. Signal-based reload (most common):
# Send HUP signal to NATS server process
kill -HUP <nats-server-pid>
# Or using systemctl if running as service
systemctl reload nats-server
2. Using NATS CLI:
# Reload configuration
nats-server --signal reload
# Or using nats CLI tool
nats server request reload
3. Docker
docker exec <nats-container> nats-server --signal reload
``
## Environment-Based Configuration
For easier switching between environments:
```bash
# Set environment variable
export NATS_NKEY_SEED_FILE=/etc/mir/mirdev.seed # or mirtest.seed
# In your Go code
seedFile := os.Getenv("NATS_NKEY_SEED_FILE")
if seedFile == "" {
seedFile = "/etc/mir/mirdev.seed" // default
}
nc, err := nats.Connect("nats://localhost:4222",
nats.NkeyOptionFromSeed(seedFile))- Never commit seed files to version control
- Keep different seeds for different environments
- Use file permissions to restrict access (600 or 400)
- Consider encrypting seeds at rest for production
- Backup seeds securely - losing them means losing access
- Complete isolation between Dev and Test environments
- No passwords transmitted over network
- Simple management - just one seed per environment
- Secure - uses Ed25519 public key cryptography
- Easy rotation - generate new keys anytime