Skip to content

NatsAuth

MaxThom edited this page Sep 5, 2025 · 1 revision

NATS Authentication Setup for Mir

Overview

This guide sets up NATS authentication using NKeys with two isolated accounts:

  • MirDev: Development environment
  • MirTest: Testing/staging environment

Generate Account Seeds

# 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
# ACTEST7PZTKJSBTR7BF6TBK3D776734PWHWDKO7HFMQOM5BIOYPSTEST

Note: The first line is the seed (private key starting with 'S'), the second is the public key (starting with 'A').

NATS Server Configuration

Create nats-server.conf:

port: 4222

accounts {
  MirDev: {
    nkey: ADDEV3JXYTXOJEL6LNAXDREUGRX35BOLZI3B4PFFAC7IRPR3OA4QNKDEV
  }

  MirTest: {
    nkey: ACTEST7PZTKJSBTR7BF6TBK3D776734PWHWDKO7HFMQOM5BIOYPSTEST
  }
}

Store Seeds Securely

# 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.nk

Connect from Go

package 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()
}

Test with CLI

# 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 subscription

Reload server.conf

1. 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))

Security Notes

  1. Never commit seed files to version control
  2. Keep different seeds for different environments
  3. Use file permissions to restrict access (600 or 400)
  4. Consider encrypting seeds at rest for production
  5. Backup seeds securely - losing them means losing access

Key Benefits

  • 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

TLS Encryption for Data in Transit

While NKeys provide strong authentication, they don't encrypt data in transit. For production deployments, TLS encryption is essential to protect sensitive telemetry and command data.

Generate TLS Certificates

Self-Signed Certificates (Development)

# Create certificate directory
mkdir -p /etc/mir/certs
cd /etc/mir/certs

# Generate Certificate Authority (CA)
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.pem \
  -subj "/C=US/ST=State/L=City/O=MirIoT/CN=Mir-CA"

# Generate server certificate
openssl genrsa -out server-key.pem 4096
openssl req -new -key server-key.pem -out server.csr \
  -subj "/C=US/ST=State/L=City/O=MirIoT/CN=mir-nats-server"

# Create server extensions file (important for hostname verification)
cat > server-extfile.cnf <<EOF
subjectAltName = DNS:localhost,DNS:mir-nats-server,DNS:*.mir.local,IP:127.0.0.1
EOF

# Sign server certificate
openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -out server-cert.pem -extfile server-extfile.cnf -CAcreateserial

# Generate client certificate
openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -out client.csr \
  -subj "/C=US/ST=State/L=City/O=MirIoT/CN=mir-client"

# Sign client certificate
openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -out client-cert.pem -CAcreateserial

# Clean up and set permissions
rm *.csr *.cnf
chmod 400 *-key.pem
chmod 444 *.pem

Production Certificates (Let's Encrypt)

# Install certbot
sudo apt-get install certbot

# Generate certificate
sudo certbot certonly --standalone -d nats.yourdomain.com

# Copy certificates
sudo cp /etc/letsencrypt/live/nats.yourdomain.com/fullchain.pem /etc/mir/certs/server-cert.pem
sudo cp /etc/letsencrypt/live/nats.yourdomain.com/privkey.pem /etc/mir/certs/server-key.pem
sudo chmod 400 /etc/mir/certs/server-key.pem

NATS Server Configuration with TLS + NKeys

Update nats-server.conf to include TLS:

port: 4222

# TLS Configuration
tls {
  cert_file: "/etc/mir/certs/server-cert.pem"
  key_file:  "/etc/mir/certs/server-key.pem"
  ca_file:   "/etc/mir/certs/ca.pem"
  verify:    true  # Require client certificates
}

# Account configuration with NKeys (unchanged)
accounts {
  MirDev: {
    nkey: ADDEV3JXYTXOJEL6LNAXDREUGRX35BOLZI3B4PFFAC7IRPR3OA4QNKDEV
  }
  
  MirTest: {
    nkey: ACTEST7PZTKJSBTR7BF6TBK3D776734PWHWDKO7HFMQOM5BIOYPSTEST
  }
}

# Cluster TLS (if using clustered NATS)
cluster {
  tls {
    cert_file: "/etc/mir/certs/server-cert.pem"
    key_file:  "/etc/mir/certs/server-key.pem"
    ca_file:   "/etc/mir/certs/ca.pem"
  }
}

Go Client with TLS + NKeys

package main

import (
    "crypto/tls"
    "log"
    "github.com/nats-io/nats.go"
)

func main() {
    // Connect with both TLS encryption and NKey authentication
    nc, err := nats.Connect("tls://localhost:4222",
        // NKey authentication
        nats.NkeyOptionFromSeed("/etc/mir/mirdev.seed"),
        // TLS encryption
        nats.ClientCert("/etc/mir/certs/client-cert.pem", "/etc/mir/certs/client-key.pem"),
        nats.RootCAs("/etc/mir/certs/ca.pem"))
    if err != nil {
        log.Fatal("Secure connection failed:", err)
    }
    defer nc.Close()
    
    // Alternative: Custom TLS config
    tlsConfig := &tls.Config{
        ServerName: "mir-nats-server",
        MinVersion: tls.VersionTLS12,
    }
    
    ncCustom, err := nats.Connect("tls://localhost:4222",
        nats.NkeyOptionFromSeed("/etc/mir/mirdev.seed"),
        nats.Secure(tlsConfig),
        nats.RootCAs("/etc/mir/certs/ca.pem"))
}

Device-Specific Certificates

For IoT devices, generate unique certificates per device:

#!/bin/bash
# generate-device-cert.sh
DEVICE_ID=$1
if [ -z "$DEVICE_ID" ]; then
    echo "Usage: $0 <device-id>"
    exit 1
fi

# Generate device certificate
openssl genrsa -out device-${DEVICE_ID}-key.pem 4096
openssl req -new -key device-${DEVICE_ID}-key.pem -out device-${DEVICE_ID}.csr \
  -subj "/C=US/ST=State/L=City/O=MirIoT/OU=Devices/CN=device-${DEVICE_ID}"

# Sign with CA
openssl x509 -req -days 365 -in device-${DEVICE_ID}.csr \
  -CA ca.pem -CAkey ca-key.pem \
  -out device-${DEVICE_ID}-cert.pem -CAcreateserial

rm device-${DEVICE_ID}.csr
chmod 400 device-${DEVICE_ID}-key.pem

Test TLS Connection

# Verify certificate
openssl x509 -in server-cert.pem -text -noout

# Test TLS connection
openssl s_client -connect localhost:4222 -CAfile ca.pem

# Test with NATS CLI
nats --tlscert=client-cert.pem --tlskey=client-key.pem --tlsca=ca.pem \
     --nkey=/etc/mir/mirdev.seed \
     --server=tls://localhost:4222 pub test "hello"

JetStream Encryption at Rest

For data stored in JetStream, add encryption at rest:

jetstream {
  store_dir: /data/jetstream
  cipher: chachapoly
  key: $JS_KEY  # Use environment variable
}

Security Architecture Summary

  1. Authentication: NKeys verify device/client identity
  2. Encryption in Transit: TLS protects data between devices and NATS
  3. Encryption at Rest: JetStream cipher protects stored messages
  4. Authorization: Account isolation ensures data segregation
  5. Certificate Management: Unique certificates per device for additional security

TLS Security Notes

  1. Use TLS 1.2 or higher in production
  2. Rotate certificates regularly (automate with cron)
  3. Monitor certificate expiration dates
  4. Use strong cipher suites only
  5. Implement certificate pinning for IoT devices
  6. Store private keys securely with restricted permissions (400)

Clone this wiki locally