Threat model

Explicit statement of what customsso + customsso-manager defend against, what they don't, and what assumptions the security model rests on. Written for community / security review.

Assets being protected

Listed roughly by sensitivity, highest first.

  1. The GPG signing key (046E8CA0EE6A755B) — controls what code every PBX trusts as "VoIP Stuff". A leak = ability to publish malicious updates to every paired PBX.
  2. The manager's master AES key (/etc/customsso-manager/master.key) — decrypts every per-PBX HMAC secret and SSH private key stored in the manager DB. A leak + DB access = fleet-wide control.
  3. Per-PBX SSH private key — the only auth channel between manager and PBX. Command-exec-only (no-pty,no-X11,…), host-key-pinned on the manager side. AES-256-GCM encrypted at rest on the manager under the master key.
  4. Per-PBX HMAC secret — ability to mint valid SSO landing tokens for that PBX. Since v1.0.0, tokens carry a variant claim (admin or ucp) that selects which session-mint path runs on the PBX, so this secret can be used to become either admin OR any UCP-loginable user.
  5. Manager admin credentials — bcrypt-hashed; one human's compromised account = ability to use whatever role they have.
  6. The audit log — read-only forensic record; integrity matters more than confidentiality.

Threat actors considered

Actor Capabilities assumed In scope?
Unauthenticated internet attacker Can reach manager + PBX network ports ✅ Yes — primary threat
Authenticated low-privilege user on the manager (operator / readonly) Valid login, restricted role ✅ Yes — least-privilege model
Attacker who obtains the SSH root password at the moment of pair (window: seconds, then wiped) Same as legitimate operator during pair window ✅ Yes — race-to-pair concern
Compromised single PBX Root on one paired PBX ✅ Per-PBX-isolation — that PBX's keys leak; others stay safe
Compromised manager OS (root) Full root on the manager host ❌ Out of scope — root has the master key
Compromised admin workstation Keylogger / session hijack on the operator's laptop ❌ Out of scope — has whatever the operator has
Insider with legitimate superuser access Acting maliciously ⚠️ Partial — every action audited, but actions are not prevented
Sangoma/FreePBX itself going hostile Upstream framework backdoors the agent's host environment ❌ Out of scope — we run inside FreePBX
Nation-state with cryptographic novel attacks Breaks AES-256-GCM, ed25519, SHA-256, bcrypt ❌ Out of scope

Specific attack scenarios + our defenses

1. Public-internet attacker probes the manager UI

2. Public-internet attacker probes the PBX at network level

3. Network attacker MITM's manager → PBX traffic

4. SSH root password intercepted during pair

5. Compromised single PBX tries to attack the manager

6. SQL injection on the manager web app

7. XSS on the manager web app

8. CSRF against the manager

9. Compromised SSH key for one PBX

10. Malicious admin with superuser role

11. Malicious or compromised admin abuses UCP impersonation (v1.0.0+)

12. Attacker steals an operator's browser session mid-UCP-impersonation

Cryptographic primitives + threat assumptions

Primitive Used for Assumed safe against
AES-256-GCM Master-key encryption of *_ct columns Brute force, known attacks; AEAD provides tampering detection
HMAC-SHA256 SSO landing token signing Forgery without the secret
ed25519 Per-PBX SSH keys Key recovery without the private key; signature forgery
SHA-256 Module sig hashes Preimage / second-preimage attacks
bcrypt (PASSWORD_DEFAULT) Admin password hashes Offline dictionary attacks at well-chosen cost factor
GPG (RSA) Module signing key 046E8CA0EE6A755B Algorithm-level break (would catastrophically break Sangoma + every distro signing infrastructure too)
random_bytes() Nonces, ed25519 keys, master keys CSPRNG output unpredictability
hash_equals() Token signature comparison Timing-attack-based discovery of HMAC bits

We do not roll our own crypto. Every primitive used is the language-standard implementation (openssl_*, random_bytes, password_*, OpenSSH's ssh-keygen, system GPG).

Known limitations

Things a fleet operator MUST know before deploying, ranked by likelihood of biting real users:

  1. HTTPS is mandatory end-to-end. Cross-origin iframe cookies over plain HTTP are blocked by every modern browser (RFC 6265bis). The Native UI and UCP-impersonation iframe features simply won't work in HTTP mode. Use self-signed certs during install; certbot on prod.
  2. SSO click for variant=admin lands on the PBX as user admin. Sso::DEFAULT_TARGET_USER = 'admin' is the fallback for the admin variant, so FreePBX-side audit shows admin for anything a manager operator did through admin SSO. The v1.0.0 UCP variant is different — it always lands as the picked user, so UCP-side audit is per-user. Per-user attribution for the admin variant is still on the roadmap.
  3. Native UI + UCP iframes reload on tab switch. No cross-tab DOM persistence. Every switch back mints a fresh SSO token + reloads the iframe. Working as intended given the "each tab is a page nav" architecture.
  4. UCP session teardown deferred to v1.0.1. After Switch-user / tab close, the row in asterisk.ucp_sessions persists until UCP's own TTL. IP-pinned to the operator's browser, but a walk-away workstation risk exists. A signed logout_ucp teardown endpoint + JS beforeunload sendBeacon is queued for v1.0.1.
  5. Boot-token race window. Between install.sh printing the setup URL and the operator claiming it, anyone with local read on /etc/customsso-manager/first-run.token could hijack the setup. Mitigation: claim the URL immediately after install, don't leave the box in setup-pending state.
  6. Master key on disk alongside the encrypted data — limited defense against full-disk theft. See LUKS tradeoffs in SECURITY.md. Manager BACKUPS must include the master key or all secrets are unrecoverable.
  7. Root SSH password used exactly once at pair time. A network capture during the pair TCP session (SSH is encrypted, so this needs a broken SSH cipher, MITM against the manager's Paramiko host-key check, or shoulder surfing on the operator) could yield the pre-wipe password. In practice: seconds of exposure, then destroyed on the PBX.
  8. No source-IP restriction on the SSH authorized_keys line since v0.6.4. Dropped so DR-restore-to-a-different-IP works; host-key pinning (Paramiko known_hosts) + no-pty,no-X11,… restrictions + AES-256-GCM-encrypted private key replace that defense. A leaked key alone is useless without the manager's master key + DB access.
  9. No signed audit log — append-only by convention only. A root attacker on either host could doctor the audit table. Detect via off-host shipping (syslog forwarding, not bundled).
  10. The sidecar's SSH command allowlist for bulk operations is edited via the UI (Settings → Custom commands) — admins can define arbitrary shell templates. The templates run as root on every selected PBX. Treat the admin role as equivalent to fleet-wide root, per role docs. Operator-role users can only dispatch commands from the built-in CMD_MAP, not define new ones.
  11. 2FA is email-code only. TOTP (authenticator apps) remains a roadmap item.
  12. Master-key rotation is single-transaction (best-effort atomicity). The re-encrypt runs inside one DB transaction; a mid-rotation crash rolls back cleanly. However, atomic two-phase rotation (concurrent old-key retention with a grace window for in-flight decrypts) is deferred (H10 audit finding). Rotate during a maintenance window.

Reporting issues

If you find a vulnerability — please don't open a public issue. Email signing@voip-stuff.net or DM via the project's communication channels.