Skip to content

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.