Stellar ControlMission control · by Stellar Systems v0.1.0

Security

TLS, Encryption and Secrets

Transport security of NATS, the API and the leaf nodes, encryption at rest of JetStream, and where the secrets live.

Stellar Control protects its data in transit with TLS on every hop, at rest with JetStream encryption and encrypted disks, and keeps its secrets out of any repository. Between tenants of a shared platform, NATS accounts do the isolation (see Cells); encryption protects disks and backups.

Layers#

LayerProtection
Clients ↔ NATSTLS required (tls://), CA given by the deployment; client certificate optional (mTLS)
Leaf nodes, cluster routesTLS; leaf nodes also over WebSocket on 443
API, web console, editorHTTPS and WSS, terminated by a reverse proxy (one subdomain per cell, ACME certificates); the CLI speaks HTTPS and WSS
Data at restJetStream encryption (streams, KV buckets, object stores), one key per server; encrypted disks for the rest
SecretsOperator key offline; per cell, the account signing key, OIDC secrets and the JetStream key in encrypted Nomad variables or Vault; never in a repository

TLS to NATS#

Services of the core#

YAML
nats:
  url: tls://nats.mcs.example.org:4222
  tls:
    ca: /etc/stellar/tls/ca.pem          # TLS required, server checked against this CA
    cert: /etc/stellar/tls/client.pem    # with key: mTLS
    key: /etc/stellar/tls/client-key.pem
  • With tls.ca, the connection requires TLS and checks the server against that CA.
  • With tls.cert and tls.key (always together), the component presents a client certificate.
  • A tls:// URL without tls.ca checks the server against the system roots.
  • A wss:// URL goes through the NATS WebSocket port, for networks that only let HTTPS out.

A certificate or credentials the server refuses are logged at the first connection attempt, with the reason, then retried in the background.

Components of the SDKs and the CLI#

Drivers, transports, gateways and connectors (Rust and Python SDKs) read the same settings from their environment; the CLI takes them as options, for its commands that talk to NATS (stellar compile --publish, stellar sim):

EnvironmentCLI optionMeaning
STELLAR_NATS_CA--nats-caCA certificates (PEM): TLS only
STELLAR_NATS_CERT--nats-certClient certificate (PEM), for mTLS; goes with the key
STELLAR_NATS_KEY--nats-keyIts private key (PEM)
STELLAR_NATS_CREDENTIALS--nats-credentialsCredentials file (JWT and seed)

Server side#

The development server requires TLS with NATS_CONF=nats-server-tls.conf docker compose up -d nats, with a CA and certificates generated by dev/nats/setup.sh in dev/nats/secrets/tls/:

text
tls {
  cert_file: /etc/nats/secrets/tls/server.pem
  key_file: /etc/nats/secrets/tls/server-key.pem
  ca_file: /etc/nats/secrets/tls/ca.pem
  verify: false        # true: client certificates required (mTLS)
}

The Nomad deployment requires TLS on every port with NATS_TLS=1 (or mtls), and its nats job takes tls_dir, tls_verify, leafnode_port and websocket_port for a real CA.

Leaf nodes of the benches#

A bench joins the cell of its customer through a NATS leaf node bound to the account of the cell. Leaf node connections use TLS, and can also run over the NATS WebSocket on 443 when the bench network only lets HTTPS out. Cluster routes between NATS servers use TLS too.

HTTPS for the API#

The API itself serves plain HTTP on api.listen: HTTPS and WSS are terminated by a reverse proxy in front of it. The Nomad deployment ships Traefik (jobs/proxy.nomad.hcl), with ACME certificates in production and one subdomain per cell.

The CLI speaks HTTPS and WSS to an https:// API, and checks its certificate against the system roots, or against the CA given with --api-ca (STELLAR_API_CA):

Shell
stellar send sim-1 'tcu[TCU1].ping' --api https://demo.mcs.example.org
STELLAR_API_CA=/etc/stellar/tls/ca.pem stellar alarms --api https://localhost:8443

Encryption at rest#

JetStream#

NATS encrypts its streams, KV buckets and object stores with a key per server (ChaCha20-Poly1305 in the Nomad deployment, encrypt=true and NATS_ENCRYPT=1). The key comes from a secret store, never from a file of the repository.

Rotating the key loses nothing: give the new key together with the old one as prev_key. The server restarts, reads the existing data with the old key and writes with the new one. Once everything is rewritten, drop prev_key. In the Nomad deployment:

  1. set jetstream_prev_key to the current key and jetstream_key to the new one in the Nomad variable nomad/jobs/nats;
  2. let the nats job restart;
  3. remove jetstream_prev_key later.

Everything else#

What JetStream does not hold belongs on encrypted disks (LUKS, or the encrypted volumes of the provider): the configuration repositories and the working copies of the editor, the database of an output connector (TimescaleDB), the data directory of Nomad.

Secrets#

SecretWhereWho reads it
Operator key of NATSOffline, in the directory of stellar-admin between provisioningsstellar-admin
Account signing key of a cellNomad variable nomad/jobs/cell-<name>, readable by the job of the cell only, or VaultThe reconciler (reconciler.signing_key_file) and the API
Credentials of the services and bootstrap credentialsSame variableThe services; the benches get the bootstrap credentials from stellar-admin cell credentials
OIDC key set, issuer, audienceDeployment configurationAPI, executor, editor
Forge token of the editorforge_token of the cell variable, in the variable named by editor.forge.token_envThe editor
JetStream keyNomad variable nomad/jobs/natsNATS
TLS keysFiles on the hosts, or ACMENATS, the reverse proxy

Encryption and authentication of the space link itself (SDLS, CCSDS 355.0) are not a NATS matter: they belong to a transport of the link, which frames the units of the driver for the medium.

Stellar Control · v0.1.0

↑↓ to moveEnter to open