Security
What Ursula does and does not handle in the security model, and how to deploy it safely.
Ursula does not terminate TLS, authenticate clients, or restrict access to administrative endpoints. Treat the listening port as fully trusted. Run Ursula on a private network behind a reverse proxy that owns TLS termination and request authentication. Do not expose it directly to untrusted clients.
This page is honest about the current security posture so you can make the right deployment decisions. Items called out as "not provided by Ursula" are intentional gaps for v0.x. The project is built to slot behind your existing edge layer rather than to be its own.
What Ursula does
- Quorum-acknowledged writes. An append is acknowledged only after a majority of voters has replicated it.
- Graceful shutdown and health checks.
/healthz,/readyz/local,/readyz/clusterallow upstream proxies and orchestrators to manage rollouts. - Global write backpressure. When the hot tier, flush backlog, or apply lag exceeds configured thresholds, writes get
429 Too Many RequestswithRetry-After. - Startup-time credential validation.
$ENV_VARreferences in the TOML config fail fast if the referenced variable is missing.
What Ursula does not do
The following must be handled outside Ursula:
- TLS / HTTPS. The public HTTP listener serves plain HTTP. There is no built-in
rustlsoraxum-server-tlsintegration. - Inter-node encryption. Peer-to-peer gRPC (Raft vote/append, snapshot transfer, hot-payload rehydrate) runs over h2c (HTTP/2 cleartext). Peers must share a private network.
- API authentication. There is no bearer-token, API-key, or signed-request validation on the public HTTP API. Any caller with network reach to the listener can create buckets, append to streams, read, and delete.
- Authorization / multi-tenancy. There is no per-user, per-bucket, or per-scope ACL. Either every caller has full access, or you enforce isolation upstream.
- Admin endpoint isolation.
/cluster/status,/cluster/add-learner,/cluster/remove-node,/metrics, and the readiness/health endpoints share the same listener as the public API. They are not protected. - Per-client rate limiting. Backpressure is global. A single noisy client cannot be throttled separately; it will trigger backpressure for everyone.
- At-rest encryption. The hot tier (RocksDB), WAL, and snapshots live on disk unencrypted. Use full-disk encryption (LUKS, EBS encryption, etc.) at the host level.
CORS is permissive by default (Access-Control-Allow-Origin: *, all methods, all headers). This matters only for browser clients; native clients are unaffected. If you put browser traffic in front of Ursula, restrict CORS at the proxy.
Recommended deployment
For any deployment beyond a local single-node experiment, run Ursula behind a reverse proxy that owns TLS, authentication, and admin isolation. A typical shape:
Untrusted internet
│
v
┌─────────────────────┐
│ Reverse proxy │ ← TLS, authn, per-client
│ (nginx / Envoy / …) │ rate limiting, CORS
└──────────┬──────────┘
│ plain HTTP, private network
┌────────────┼────────────┐
v v v
┌────────┐ ┌────────┐ ┌────────┐
│ Ursula │ │ Ursula │ │ Ursula │
│ node │ │ node │ │ node │
└────────┘ └────────┘ └────────┘
↕ gRPC h2c on private network
(Raft replication, snapshot transfer)
Checklist
- Bind to the private interface. Set
server.listen = "10.0.0.X:4437"(or use a security group / firewall) so the listener is not reachable from public networks. - Terminate TLS at the proxy. Issue certificates for the public hostname there; Ursula stays plain HTTP on the internal side.
- Authenticate at the proxy. Validate the caller (OAuth2, mTLS, API gateway, signed requests, etc.) and short-circuit unauthenticated requests before they reach Ursula.
- Block admin paths from public traffic. At minimum, deny
/cluster/*and/metricsfrom the public listener; allow them only on an internal listener or for monitoring/ops networks. - Use IAM roles for S3 instead of static keys. Omit the
cold.s3_access_key_id/cold.s3_secret_access_keykeys; the AWS SDK credential chain (IAM instance profile, IRSA,AWS_*env vars) will be used. If you must use static keys, reference them with$ENV_VARso they never live on disk. - Encrypt data volumes. Apply full-disk encryption to the partition backing
server.data_dir. - Keep peer traffic private. Peers communicate over plain gRPC. Put them on a private subnet, security group, or WireGuard mesh. Never route gRPC peer traffic across the public internet.
Reporting vulnerabilities
For non-public vulnerability reports, please open a GitHub Security Advisory on the opendurability/ursula repository rather than a public issue.