Security model¶
This document describes what guarantees that one customer does not see another's data, that secrets stay secret, and that backups cannot be erased by whatever wrote them.
Isolation in the database¶
Isolation is not a filter written into every query: it is enforced by PostgreSQL itself, at row level (row-level security).
Every isolated table carries a policy that bounds any query to the current user's customer. The scope is set on the session at the start of each transaction, and limited to it — a connection returned to the pool cannot carry another customer's scope away with it.
What follows from that:
- An endpoint that forgot the filter returns nothing, instead of returning everything. That is the point of the measure: it protects against oversight, not only against malice.
- An empty scope returns nothing either. The function that reads the scope fails closed in both directions: scope absent, no rows; scope unreadable, the query fails.
Two indispensable conditions¶
FORCE ROW LEVEL SECURITY. Without it, the owner of the tables works around
its own policies without knowing.
An unprivileged application role. The role used day to day must be
NOSUPERUSER and NOBYPASSRLS: a superuser works around isolation even with
FORCE. That is the reason for the two PostgreSQL roles created by
backup init — an owner, and an application role.
GET /ready refuses to declare the platform ready if an expected table is not
under a policy. If a customer account sees other customers, that is the first
place to look.
The customer table is itself isolated¶
It is isolated on its primary key, for want of a tenant_id column. Without that
policy, a customer account could enumerate the provider's other customers —
which, for a managed service, discloses the commercial portfolio.
The audit log is append-only¶
The application role holds neither the update right nor the delete right on the log table. The guarantee holds at the level of privileges, not through the discipline of the code: it is verifiable by an auditor, and impossible to work around inadvertently.
Encryption¶
Three levels of key:
| Key | Where | Protects |
|---|---|---|
Master key (BKP_MASTER_KEY) |
deployment configuration | the customer keys |
| Repository key, one per customer | in the database, encrypted under the master | that customer's backups |
| Credential secrets | in the database, encrypted under the customer key | access to the devices |
The master key must never be stored with the database. Whoever holds both holds everything. A database backup carried off together with the environment file cancels the encryption.
Credential secrets exist in clear only in memory, at the moment they are handed
to an agent for the duration of a job. They are never written to the agent's
disk; an SSH private key, which OpenSSH requires as a file, is created in
memory (/dev/shm, permissions 0600) for the length of the transfer and then
erased.
No secret is returned by the API, whatever the role.
Storage isolation¶
Each customer has:
- its prefix in the bucket, with a random suffix;
- its service account, restricted to that prefix and deprived of any delete permission;
- its deduplicated repository, encrypted under its key.
Deduplication never operates across customers. Shared, it would constitute a side channel: writing a piece of content and observing whether it is really stored or merely referenced would confirm that another customer holds it.
A customer with no service account is flagged by Settings → Storage compliance — otherwise its agents write with the platform credentials, and this section's first two lines stop being true for that customer. See The service account.
Immutability¶
Two independent layers:
| Layer | Effect |
|---|---|
| Agent permissions | append only: an agent cannot delete |
| Object lock | no identity can delete before the deadline |
The purge runs from the platform, under a distinct identity that is never present on an agent. A compromised agent cannot erase what it wrote yesterday.
See Retention.
Identity of the parties¶
Three distinct verifications, in three directions:
| Who verifies whom | Mechanism |
|---|---|
| The agent verifies the platform | certificate authority supplied at enrolment |
| The platform verifies the agent | mTLS client certificate, revocable individually |
| The agent verifies the device | TLS fingerprint or SSH host key, according to the posture |
The third point is detailed in Device identity. It completes the first two: without it, the platform authenticates its agents but has them hand a customer's administration credentials to whoever turns up first.
Roles and scope¶
See Accounts and identity federation for the complete matrix.
The essentials: the provider's staff have access to every customer — that is the managed-service model, and restricting it would break operations. Its counterpart is the systematic logging of every view, read-only ones included, with the customer concerned.
Customer accounts are each restricted to one customer, with no identity federation.
Read-only restoration¶
The accounts used on devices need no write permission, and by default the platform never pushes a configuration back. A compromise of the platform therefore cannot translate into a change to a firewall's configuration.
One exception exists and is off by default: a Junos configuration can be
applied back to its device (beta). It only becomes possible where an operator has
enabled it at platform, customer and device level, it needs the config:restore
permission that no read-only or operator role carries, and every request is
logged with the version applied. Where it is left off — which is everywhere until
someone acts — the paragraph above holds unchanged.
See Restoring.
What remains to be done¶
Two points are specified and not yet implemented:
- exporting the encryption keys under dual control, needed for a complete instance migration;
- periodic verification that a sample of snapshots is restorable, with a trace usable in an audit.
A third point is worth knowing: text configurations are kept in clear in the database, including the password hashes and SNMP communities they contain. Encryption of that field is not yet in place. The database is protected by the measures above, but that is not the same thing as encrypting the content itself at rest.