ADR-0006 Zero-Shared-Secrets Edge
Status
Section titled “Status”Accepted — implemented and live. (Recorded in the repository as ADR-011.)
Context
Section titled “Context”An edge node (OpenSIPS + FreeSWITCH + rtpengine + node agent + call engine) runs
on the platform’s most exposed surface — public SIP/RTP — and in the multi-edge
future may sit on a customer-controlled VPS. Originally the edge .env held
plaintext credentials to shared, cross-tenant infrastructure: the OpenSIPS
auth DB URL, the call engine’s DB_*, the shared REDIS_PASSWORD, and an
unauthenticated NATS_URL. Any single leak was a full multi-tenant breach —
every tenant’s subscribers, CDRs, call state and events. Postgres row-level
security did not help (the get2dial role bypassed RLS), and NATS had no
authentication at all.
Decision
Section titled “Decision”Remove every cross-tenant secret from the edge; the edge holds only scoped, per-node / per-tenant credentials:
- Node identity is a hashed API key (issued at registration, matched
against
nodes.api_key_hash) rather than a shared DB/Redis password — chosen over mTLS for the first iteration. - OpenSIPS reads its
subscribers(and TLS certs) over a local db_http service instead of a direct shared-Postgres connection; usrloc/dialog are in-memory. - The call engine runs with
READ_SOURCE=api,CDR_SINK=apiandDISPOSITION_SINK=api, so all reads and write-backs go through node-authenticated control-plane endpoints — no shared DB handle. - Redis is scoped with a per-tenant ACL user (keys
~t:<tenant>:*) rather than removed. - NATS gains per-tenant accounts whose publish/subscribe is limited to
t.<tenant>.>.
A CI guard (check-edge-no-shared-secrets.sh) asserts the committed edge config
carries no required shared-infra credential.
Consequences
Section titled “Consequences”- A compromised edge can only ever touch its own tenant’s data — the blast radius of a leak drops from “all tenants” to “one tenant”.
- The edge is pool-less: it depends on the control plane’s node API for config, which becomes a runtime dependency for cold reads (mitigated by an edge cache).
- Credential rotation requires the edge
.envto be redeployed to take effect — rotation alone is not enough.