Threat model¶
This page makes the threat model explicit so you can audit it against your own threat assumptions. The structure is the standard "adversary → asset → control → out of scope".
Adversaries¶
| Adversary | Capability | Goal |
|---|---|---|
| External attacker | reaches the public Ampora hostname | take over the management plane and pivot to the fleet |
| Compromised agent | controls one collector binary | escalate to other agents or to the server |
| Hostile operator | has UI access with reduced privileges | gain Admin powers |
| Malicious insider | has Admin powers | exfiltrate data, hide tracks |
| Compromised peer | runs a federated Ampora that talks to ours | exfiltrate or tamper with our fleet view |
| Supply-chain attacker | controls a dependency or build pipeline | sneak code into a release |
Assets we protect¶
- Agent control surface — every config push and package transfer.
- CA signing keys — issuance authority for every agent cert.
- Bootstrap tokens — single-use; expire fast; never stored plaintext at rest.
- Audit log — append-only record of every state change.
- OIDC client secret — the application's identity to your IdP.
- GitOps source credentials — PATs and SSH keys for upstream repos.
Controls¶
Transport¶
- mTLS between agents and Ampora after bootstrap. The server identifies the agent by certificate fingerprint, not
instance_uid(which is agent-supplied and untrusted). - TLS on the operator path. Server TLS is your responsibility on the reverse proxy; Ampora always honours
X-Forwarded-Proto. - mTLS + shared secret for federation peers. Both factors required.
AuthN / AuthZ¶
- OIDC for operator login. PKCE on the Authorization Code flow.
- RBAC with three roles (
Admin,Operator,Viewer). All state-changing endpoints require an explicit role; not "any authenticated user". - Bootstrap tokens are short-lived, single-use, hashed at rest.
- Capability gating for OpAMP: server-to-agent pushes only happen when the agent has signalled the matching capability.
Encryption at rest¶
IKeyProtectorwraps every secret column with AES-GCM-256 by default. The same abstraction transparently delegates to AWS KMS, Azure Key Vault, GCP KMS, PKCS#11 or Vault Transit when configured.- Master key never lives in the database. It is read at startup from a secret you control.
KeyProtection:PreviousMasterKeyallows a one-cycle rotation without downtime.
Audit and tamper-evidence¶
- Append-only audit table. Every state change is recorded with actor, entity, before/after JSON snapshot, and timestamp.
- No mutation API for audit rows.
- RFC-3161 timestamping is opt-in for compliance-driven deployments.
Hard isolation between tenants¶
- PostgreSQL Row-Level Security policies on every multi-tenant table. Database-layer enforcement, not just application-layer.
- Per-tenant connection roles so even a SQL injection survives tenant-isolation.
- Federation does not cross tenants — federated reads only see the caller's own tenant; tenant IDs never leave the local server.
Defense in depth on agent control¶
- Capabilities are opt-in.
AcceptsPackagesandAcceptsConnectionSettingsare off by default in agents we ship templates for; turning them on is an explicit choice. - Package signatures with cosign; agents reject unsigned binaries.
- Health gates auto-pause rollouts on apply-failure rates above configurable thresholds.
Defense in depth on operator actions¶
- Custom policies can require approvals (four-eyes) before publication.
- Policy DSL evaluator runs fail-closed on a 50 ms budget so a pathological expression cannot DoS the rollout engine.
Out of scope¶
We do not claim to defend against:
- Compromised PostgreSQL. If an attacker has SQL-level write access to the Ampora database, they own the management plane. Protect the DB the way you protect any other crown-jewel store.
- Compromised secret manager. If an attacker can read the master key, every encrypted-at-rest column becomes plaintext. Same logic.
- Hardware-level attacks on the Ampora host. The HSM/KMS adapter raises this bar, but a sufficiently determined physical attacker always wins.
- Side-channel timing attacks at sub-millisecond resolution. We use constant-time compares for secrets but make no claims for cache timing on shared physical hardware.
Specific scenarios and the relevant controls¶
"An attacker steals an agent's mTLS cert."¶
- They can connect to Ampora as that agent until the cert is revoked.
- Control: revoke via the Identities UI; CRL/OCSP propagation is ≤ 15 minutes.
- Detection: an unexpected source IP for a known agent fingerprint shows up in audit; the Bound agent column on Identities flags it.
"An attacker steals a bootstrap token before redemption."¶
- They can register one fake agent as that identity.
- Control: revoke the token (UI). New first-connect redemptions fail.
- Pool case: rotate the pool admin secret if a pool-issued token leaked.
"An attacker steals the OIDC client secret."¶
- They can mint OIDC tokens that will not be accepted by Ampora unless they also compromise the IdP — Ampora validates the issuer signature against the IdP's JWKS.
- Control: rotate the secret in the IdP and in Ampora's
Authentication__Oidc__ClientSecret.
"An attacker compromises one Ampora instance in HA."¶
- The dispatch backplane gives them the ability to send dispatches to any agent.
- Control: every dispatch carries a fencing token tied to the instance's lease — when the next leader is elected, the previous leader's fencing token becomes invalid.
- Detection: every dispatch is audited with the originating instance ID.
"A federated peer turns hostile."¶
- They can return any JSON for
GET /api/federation/agents. - Control: federation is read-only; the caller never trusts the data for write decisions. The peer's name is shown explicitly in the UI ("from
peer:eu-region") so an operator never confuses remote data with local. - Detection: outbound federation calls are audited; abnormal response shapes get an inline warning chip.