NATS Supercluster with JWT and TLS

NATS Version ARM64 License

Production‑ready, geo‑distributed NATS messaging backbone with clustering, gateways, leafnodes, JWT authentication, and mutual TLS — all running on ARM64.


Table of Contents


Overview

This repository provides a production‑ready, geo‑distributed NATS messaging backbone running natively on ARM64 hardware (Raspberry Pi, Apple Silicon, AWS Graviton). It features:

All communication is secured with mutual TLS, and authentication is handled via NKeys and JWT credentials.


Hardware and OS Requirements

Component Version/Spec
CPU ARM64 (aarch64)
OS Debian GNU/Linux 12 (bookworm) or later
Docker 24+
NATS CLI 0.4.0+
Python 3.9+ with nats-py
OpenTofu 1.12.1
nsc 2.15.0

Architecture

Ports

Service Port Description
Client MSG 4222 Client connections (via HAProxy)
Cluster Route 6222 Internal cluster mesh
Gateway 7222 Cross site gateway connections
Leafnode 7333 Leafnode listener
Monitoring 8222 HTTP monitoring endpoint
NATS UI 31311 Web UI (exposed on host)

Diagram

flowchart TB subgraph Site1["<b>Site 1</b>"] direction TB subgraph LeafNode1["Leaf Node"] nl11["nats-leaf-s1-1<br/>172.22.0.50"] end LB1["lb-s1.ifri.akl<br/>HAProxy<br/>172.22.0.20"] subgraph Cluster1["SITE1_CLUSTER"] n11["nats-s1-1<br/>172.22.0.10"] n12["nats-s1-2<br/>172.22.0.11"] n13["nats-s1-3<br/>172.22.0.12"] end UI1["nats-ui<br/>NATS NUI<br/>172.22.0.25"] end subgraph Site2["<b>Site 2</b>"] direction TB LB2["lb-s2.ifri.akl<br/>HAProxy<br/>172.22.0.40"] subgraph Cluster2["SITE2_CLUSTER"] n21["nats-s2-1<br/>172.22.0.30"] n22["nats-s2-2<br/>172.22.0.31"] n23["nats-s2-3<br/>172.22.0.32"] end UI2["nats-ui<br/>NATS NUI<br/>172.22.0.45"] end Admin["Admin"] Client1["Clients"] Client2["Clients"] %% Internal Cluster Routes n11 <-->|route| n12 n11 <-->|route| n13 n12 <-->|route| n13 n21 <-->|route| n22 n21 <-->|route| n23 n22 <-->|route| n23 %% LB → Cluster LB1 -->|msg| n11 LB1 -->|msg| n12 LB1 -->|msg| n13 LB2 -->|msg| n21 LB2 -->|msg| n22 LB2 -->|msg| n23 %% Leaf Node → LB → Cluster LeafNode1 <==>|leaf| LB1 %% Client Connections Client1 <==>|msg| LeafNode1 Client2 <==>|msg| LB2 %% Admin → NATS UI Admin -->|http| UI1 Admin -->|http| UI2 %% NATS UI → Cluster Monitor UI1 -.->|monitor| Cluster1 UI2 -.->|monitor| Cluster2 %% Gateway Connections (cross-site) n11 <==>|gateway| n21 n12 <==>|gateway| n22 n13 <==>|gateway| n23 %% Styles classDef site1 fill:#ff9999,stroke:#cc0000,stroke-width:2px,color:#fff classDef site2 fill:#99ff99,stroke:#00cc00,stroke-width:2px,color:#000 classDef lb fill:#ffff99,stroke:#cccc00,stroke-width:2px classDef clients fill:#ddd,stroke:#888,stroke-width:2px classDef leaf fill:#dda0dd,stroke:#663399,stroke-width:2px,color:#fff classDef ui fill:#87CEEB,stroke:#4682B4,stroke-width:2px,color:#fff classDef admin fill:#FFD700,stroke:#B8860B,stroke-width:2px class LB1,LB2 lb class n11,n12,n13 site1 class n21,n22,n23 site2 class Client1,Client2 clients class nl11 leaf class UI1,UI2 ui class Admin admin

TLS Certificates Structure

All connections (client, cluster, leafnode, gateway) use TLS with mutual authentication.

certs/
├── ca/
│   └── ca.crt
└── tls/
    ├── nats-s1-1.crt
    ├── nats-s1-1.key
    ├── nats-s1-2.crt
    ├── nats-s1-2.key
    ├── nats-s1-3.crt
    ├── nats-s1-3.key
    ├── nats-s2-1.crt
    ├── nats-s2-1.key
    ├── nats-s2-2.crt
    ├── nats-s2-2.key
    ├── nats-s2-3.crt
    ├── nats-s2-3.key
    ├── nats-leaf-s1-1.crt
    ├── nats-leaf-s1-1.key
    ├── testclient.crt
    └── testclient.key

Routes

╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                              Cluster Report                                                             │
├───────────┬─────────┬───────────┬─────────────────────────┬───────────────────┬────────┬──────────┬───────┬──────┬──────────┬───────────┤
│ Server    │ Cluster │ Name      │ Account                 │ Address           │ ID     │ Uptime   │ RTT   │ Subs │ Bytes In │ Bytes Out │
├───────────┼─────────┼───────────┼─────────────────────────┼───────────────────┼────────┼──────────┼───────┼──────┼──────────┼───────────┤
│ nats-s1-1 │ c1_red  │ nats-s1-2 │                         │ 172.22.0.11:6222  │ rid:9  │ 1h28m54s │ 371µs │ 22   │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.11:6222  │ rid:13 │ 1h28m54s │ 375µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.11:6222  │ rid:17 │ 1h28m54s │ 353µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.11:6222  │ rid:8  │ 1h28m54s │ 206µs │ 80   │ 1.7 MiB  │ 3.1 MiB   │
│           │         │ nats-s1-3 │                         │ 172.22.0.12:6222  │ rid:14 │ 1h28m54s │ 313µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.12:33742 │ rid:11 │ 1h28m54s │ 530µs │ 6    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.12:6222  │ rid:10 │ 1h28m54s │ 359µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.12:33756 │ rid:12 │ 1h28m54s │ 632µs │ 79   │ 4.1 MiB  │ 3.1 MiB   │
│ nats-s1-2 │ c1_red  │ nats-s1-1 │                         │ 172.22.0.10:38838 │ rid:17 │ 1h28m54s │ 221µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.10:38816 │ rid:9  │ 1h28m54s │ 750µs │ 2    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.10:38828 │ rid:14 │ 1h28m54s │ 211µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.10:38826 │ rid:8  │ 1h28m54s │ 196µs │ 76   │ 3.1 MiB  │ 1.7 MiB   │
│           │         │ nats-s1-3 │                         │ 172.22.0.12:60276 │ rid:10 │ 1h28m54s │ 176µs │ 6    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.12:60292 │ rid:13 │ 1h28m54s │ 229µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.12:60296 │ rid:16 │ 1h28m54s │ 307µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.12:60264 │ rid:11 │ 1h28m54s │ 758µs │ 79   │ 4.3 MiB  │ 1.9 MiB   │
│ nats-s1-3 │ c1_red  │ nats-s1-1 │                         │ 172.22.0.10:6222  │ rid:11 │ 1h28m54s │ 784µs │ 2    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.10:55438 │ rid:8  │ 1h28m54s │ 240µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.10:55442 │ rid:14 │ 1h28m54s │ 271µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.10:6222  │ rid:12 │ 1h28m54s │ 246µs │ 76   │ 3.1 MiB  │ 4.1 MiB   │
│           │         │ nats-s1-2 │                         │ 172.22.0.11:6222  │ rid:9  │ 1h28m54s │ 219µs │ 22   │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.11:6222  │ rid:13 │ 1h28m54s │ 227µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.11:6222  │ rid:17 │ 1h28m54s │ 209µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.11:6222  │ rid:10 │ 1h28m54s │ 600µs │ 80   │ 1.9 MiB  │ 4.2 MiB   │
│ nats-s2-1 │ c2_blu  │ nats-s2-2 │                         │ 172.22.0.31:6222  │ rid:10 │ 1h28m58s │ 409µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.31:6222  │ rid:13 │ 1h28m58s │ 254µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.31:6222  │ rid:15 │ 1h28m58s │ 543µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.31:6222  │ rid:11 │ 1h28m58s │ 190µs │ 65   │ 4.6 MiB  │ 1.6 MiB   │
│           │         │ nats-s2-3 │                         │ 172.22.0.32:6222  │ rid:14 │ 1h28m58s │ 502µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.32:6222  │ rid:8  │ 1h28m58s │ 158µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.32:6222  │ rid:16 │ 1h28m58s │ 151µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.32:6222  │ rid:9  │ 1h28m58s │ 194µs │ 66   │ 2.8 MiB  │ 1.6 MiB   │
│ nats-s2-2 │ c2_blu  │ nats-s2-1 │                         │ 172.22.0.30:38494 │ rid:12 │ 1h28m58s │ 226µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.30:38524 │ rid:16 │ 1h28m58s │ 228µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.30:38526 │ rid:18 │ 1h28m58s │ 255µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.30:38508 │ rid:13 │ 1h28m58s │ 181µs │ 76   │ 1.6 MiB  │ 4.6 MiB   │
│           │         │ nats-s2-3 │                         │ 172.22.0.32:6222  │ rid:9  │ 1h28m59s │ 236µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.32:6222  │ rid:14 │ 1h28m58s │ 235µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.32:38162 │ rid:10 │ 1h28m59s │ 420µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.32:6222  │ rid:8  │ 1h28m59s │ 685µs │ 66   │ 2.6 MiB  │ 4.2 MiB   │
│ nats-s2-3 │ c2_blu  │ nats-s2-1 │                         │ 172.22.0.30:36868 │ rid:12 │ 1h28m58s │ 174µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.30:36886 │ rid:15 │ 1h28m58s │ 486µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.30:36900 │ rid:17 │ 1h28m58s │ 381µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.30:36874 │ rid:13 │ 1h28m58s │ 210µs │ 76   │ 1.6 MiB  │ 2.8 MiB   │
│           │         │ nats-s2-2 │                         │ 172.22.0.31:33248 │ rid:14 │ 1h28m58s │ 258µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.31:6222  │ rid:10 │ 1h28m59s │ 253µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │                         │ 172.22.0.31:33226 │ rid:9  │ 1h28m59s │ 599µs │ 0    │ 0 B      │ 0 B       │
│           │         │           │ ACMJJEA4TT...UZ5PIJR6QR │ 172.22.0.31:33240 │ rid:8  │ 1h28m59s │ 225µs │ 65   │ 4.3 MiB  │ 2.6 MiB   │
├───────────┼─────────┼───────────┼─────────────────────────┼───────────────────┼────────┼──────────┼───────┼──────┼──────────┼───────────┤
│ 6         │ 2       │           │                         │ 48                │        │          │       │ 944  │ 36 MiB   │ 36 MiB    │
╰───────────┴─────────┴───────────┴─────────────────────────┴───────────────────┴────────┴──────────┴───────┴──────┴──────────┴───────────╯

Gateways

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                               Super Cluster Report                                               │
├───────────┬────────┬──────┬──────────┬──────────────────────────┬────────┬──────────┬───────┬─────────┬──────────┤
│ Server    │ Name   │ Port │ Kind     │ Connection               │ ID     │ Uptime   │ RTT   │ Bytes   │ Accounts │
├───────────┼────────┼──────┼──────────┼──────────────────────────┼────────┼──────────┼───────┼─────────┼──────────┤
│ nats-s1-1 │ c1_red │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Inbound  │ c2_blu 172.22.0.30:36202 │ gid:18 │ 1h28m54s │ 216µs │ 1.7 MiB │ 4        │
│           │        │      │ Outbound │ c2_blu 172.22.0.31:7222  │ gid:23 │ 1h28m53s │ 660µs │ 1.5 MiB │ 3        │
│ nats-s1-2 │ c1_red │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Outbound │ c2_blu 172.22.0.31:7222  │ gid:18 │ 1h28m54s │ 163µs │ 1.7 MiB │ 3        │
│ nats-s1-3 │ c1_red │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Inbound  │ c2_blu 172.22.0.32:34068 │ gid:18 │ 1h28m54s │ 172µs │ 1.2 MiB │ 4        │
│           │        │      │          │ c2_blu 172.22.0.31:46288 │ gid:19 │ 1h28m54s │ 142µs │ 1.4 MiB │ 4        │
│           │        │      │ Outbound │ c2_blu 172.22.0.32:7222  │ gid:22 │ 1h28m54s │ 586µs │ 1.6 MiB │ 3        │
│ nats-s2-1 │ c2_blu │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Outbound │ c1_red 172.22.0.10:7222  │ gid:21 │ 1h28m54s │ 287µs │ 1.7 MiB │ 4        │
│ nats-s2-2 │ c2_blu │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Inbound  │ c1_red 172.22.0.11:52530 │ gid:22 │ 1h28m54s │ 431µs │ 1.7 MiB │ 3        │
│           │        │      │          │ c1_red 172.22.0.10:39888 │ gid:24 │ 1h28m53s │ 534µs │ 1.5 MiB │ 3        │
│           │        │      │ Outbound │ c1_red 172.22.0.12:7222  │ gid:21 │ 1h28m54s │ 314µs │ 1.4 MiB │ 4        │
│ nats-s2-3 │ c2_blu │ 7222 │          │                          │        │          │       │         │          │
│           │        │      │ Inbound  │ c1_red 172.22.0.12:41524 │ gid:22 │ 1h28m54s │ 543µs │ 1.6 MiB │ 3        │
│           │        │      │ Outbound │ c1_red 172.22.0.12:7222  │ gid:20 │ 1h28m54s │ 176µs │ 1.2 MiB │ 4        │
├───────────┼────────┼──────┼──────────┼──────────────────────────┼────────┼──────────┼───────┼─────────┼──────────┤
│ 6         │ 2      │      │          │ 12                       │        │          │       │ 18 MiB  │          │
╰───────────┴────────┴──────┴──────────┴──────────────────────────┴────────┴──────────┴───────┴─────────┴──────────╯

Leaf

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                          Leafnode Report                                                                          │
├───────────┬────────────────┬─────────────────────────┬───────────────────┬───────────┬─────────┬──────────┬──────────┬───────────┬──────┬─────────────────┬───────┤
│ Server    │ Name           │ Account                 │ Address           │ RTT       │ Msgs In │ Msgs Out │ Bytes In │ Bytes Out │ Subs │ Compressed      │ Spoke │
├───────────┼────────────────┼─────────────────────────┼───────────────────┼───────────┼─────────┼──────────┼──────────┼───────────┼──────┼─────────────────┼───────┤
│ nats-s1-2 │ nats-leaf-s1-1 │ AB7D63GOTO...2VWS6C6DM6 │ 172.22.0.50:46582 │ 1.53154ms │ 1       │ 4,952    │ 578 B    │ 364 KiB   │ 15   │ s2_uncompressed │ false │
╰───────────┴────────────────┴─────────────────────────┴───────────────────┴───────────┴─────────┴──────────┴──────────┴───────────┴──────┴─────────────────┴───────╯

JWT Authentication (Operator Mode)

NATS uses Operator Mode for secure, scalable authentication. Accounts and users are defined as JWTs (JSON Web Tokens) managed by nsc. This replaces the older inline authentication method.

Ansible Role for JWT Management

The nats_jwt Ansible role automates the creation and management of: - Operator (IFRI) – the root of trust. - Accounts – teams with JetStream limits (e.g., TEAM_ABB, TEAM_INFRA). - Users – applications with subject‑based permissions. - Credentials (.creds files) – the secure identity for connecting to NATS.

Directory structure

config
├── IFRI.jwt
├── jwt-mem-resolver.conf
└── jwt-nats-resolver.conf
nsc-store
└── IFRI
    ├── accounts
    │   ├── SYS
    │   │   ├── SYS.jwt
    │   │   └── users
    │   │       └── sys.jwt
    │   ├── TEAM_ABB
    │   │   ├── TEAM_ABB.jwt
    │   │   └── users
    │   │       ├── tabb_app11.jwt
    │   │       └── tabb_app12.jwt
    │   ├── TEAM_CDD
    │   │   ├── TEAM_CDD.jwt
    │   │   └── users
    │   │       ├── tcdd_app21.jwt
    │   │       └── tcdd_app22.jwt
    │   └── TEAM_INFRA
    │       ├── TEAM_INFRA.jwt
    │       └── users
    │           ├── tinfra_leaf.jwt
    │           └── tinfra_test.jwt
    └── IFRI.jwt
nkeys/creds
└── IFRI
    ├── SYS
    │   └── sys.creds
    ├── TEAM_ABB
    │   ├── tabb_app11.creds
    │   └── tabb_app12.creds
    ├── TEAM_CDD
    │   ├── tcdd_app21.creds
    │   └── tcdd_app22.creds
    └── TEAM_INFRA
        ├── tinfra_leaf.creds
        └── tinfra_test.creds

OpenTofu Provisioning

The NATS JetStream provider allows infrastructure‑as‑code management of streams and consumers.

provider "jetstream" {
  alias    = "s1"
  servers = "tls://lb-s1.ifri.akl:4222"
  credentials   = "${path.module}/../creds/tinfra_test.creds"
  tls {
    ca_file = "${path.module}/../certs/ca/ca.crt"
  }
}

provider "jetstream" {
  alias    = "s2"
  servers = "tls://lb-s2.ifri.akl:4222"
  credentials   = "${path.module}/../creds/tinfra_test.creds"
  tls {
    ca_file = "${path.module}/../certs/ca/ca.crt"
  }
}

provider "jetstream" {
  alias    = "leaf"
  servers = "tls://nats-leaf-s1-1.ifri.akl:4222"
  credentials   = "${path.module}/../creds/tinfra_test.creds"
  tls {
    ca_file = "${path.module}/../certs/ca/ca.crt"
  }
}