feat(mesh): NATS TLS + token auth client support
- CastleNATSClient accepts a token; a tls:// server URL makes nats-py verify the
broker against the system CA (trusts the wildcard's Let's Encrypt issuer, no
custom CA needed)
- config: CASTLE_API_NATS_TOKEN; main wires it through
Server side (instance): nats-server.conf gains tls{} (wildcard cert via
expose.tcp.tls) + authorization{token}; clients use tls://castle-nats.<domain>.
This commit is contained in:
@@ -17,6 +17,7 @@ class Settings(BaseSettings):
|
|||||||
# Mesh coordination (all off by default — single-node works without them)
|
# Mesh coordination (all off by default — single-node works without them)
|
||||||
nats_enabled: bool = False
|
nats_enabled: bool = False
|
||||||
nats_url: str = "nats://localhost:4222"
|
nats_url: str = "nats://localhost:4222"
|
||||||
|
nats_token: str | None = None
|
||||||
mdns_enabled: bool = False
|
mdns_enabled: bool = False
|
||||||
|
|
||||||
model_config = {
|
model_config = {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|||||||
local_hostname=registry.node.hostname,
|
local_hostname=registry.node.hostname,
|
||||||
local_registry=registry,
|
local_registry=registry,
|
||||||
servers=settings.nats_url,
|
servers=settings.nats_url,
|
||||||
|
token=settings.nats_token,
|
||||||
)
|
)
|
||||||
await nats_client.start()
|
await nats_client.start()
|
||||||
app.state.nats_client = nats_client
|
app.state.nats_client = nats_client
|
||||||
|
|||||||
@@ -50,10 +50,12 @@ class CastleNATSClient:
|
|||||||
local_hostname: str,
|
local_hostname: str,
|
||||||
local_registry: NodeRegistry,
|
local_registry: NodeRegistry,
|
||||||
servers: str | list[str] = "nats://localhost:4222",
|
servers: str | list[str] = "nats://localhost:4222",
|
||||||
|
token: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._local_hostname = local_hostname
|
self._local_hostname = local_hostname
|
||||||
self._local_registry = local_registry
|
self._local_registry = local_registry
|
||||||
self._servers = servers
|
self._servers = servers
|
||||||
|
self._token = token or None
|
||||||
self._nc: nats.NATS | None = None
|
self._nc: nats.NATS | None = None
|
||||||
self._kv = None
|
self._kv = None
|
||||||
self._presence_kv = None
|
self._presence_kv = None
|
||||||
@@ -77,9 +79,13 @@ class CastleNATSClient:
|
|||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
"""Connect, publish our registry, seed state, and start watchers."""
|
"""Connect, publish our registry, seed state, and start watchers."""
|
||||||
|
# A `tls://` server URL makes nats-py verify the server against the system
|
||||||
|
# CA bundle — which trusts the wildcard's Let's Encrypt issuer, so no custom
|
||||||
|
# CA is needed. `token` authenticates this node to the broker.
|
||||||
self._nc = await nats.connect(
|
self._nc = await nats.connect(
|
||||||
self._servers,
|
self._servers,
|
||||||
name=f"castle-{self._local_hostname}",
|
name=f"castle-{self._local_hostname}",
|
||||||
|
token=self._token,
|
||||||
max_reconnect_attempts=-1, # reconnect forever — nodes come and go
|
max_reconnect_attempts=-1, # reconnect forever — nodes come and go
|
||||||
)
|
)
|
||||||
js = self._nc.jetstream()
|
js = self._nc.jetstream()
|
||||||
|
|||||||
Reference in New Issue
Block a user