Multi-tenancy¶
One Drupal instance can host several isolated vault realms, called
tenants. Each tenant is a separate vault: its data is partitioned per
(tenant, owner) and it can use its own Master KEK, so compromising one
tenant's key cannot read another's. A person keeps a single Drupal account; if
they appear in two tenants they hold a distinct, separately-encrypted vault in
each.
Every install has one tenant out of the box - the default tenant, created on install - and that is all a single-tenant site ever needs. The tenant controls below stay hidden until you create a second tenant, so multi-tenancy is invisible unless you opt into it.
When to use it¶
Use multiple tenants when one site serves vaults that must not share an encryption boundary - for example several agencies or municipalities behind one Drupal, each wanting its own Master KEK and its own consumers, while citizens keep one login. If you only need one vault, leave the default tenant as is.
How isolation works¶
The key hierarchy gains the tenant as a partition:
- Each owner has a distinct Subject KEK per tenant, so the same person's data in tenant A is encrypted independently of their data in tenant B.
- Each tenant resolves a Master KEK: its own key, or - when left empty - the
site-wide Master KEK from
Configuration -> Personal Data Vault (
/admin/config/pdv/settings). The default tenant ships with no key of its own, so it inherits the site-wide one and behaves exactly as a single-tenant install always did. - Items and subject keys carry their tenant, so a query, a grant, or a purge in one tenant never reaches another's rows.
Managing tenants¶
With the administer pdv tenants permission (restricted; grant only to
trusted roles), manage realms at Configuration -> Personal Data Vault ->
Vault tenants (/admin/config/pdv/tenants).
Each tenant has:
- a machine name and label;
- a Master KEK - select a key from the Key module to give this tenant its own cryptographic boundary, or leave it as Site default to inherit the site-wide Master KEK.
The Master KEK rules from Installation apply per tenant: it must resolve to 32 bytes, it cannot be deleted while it still protects a tenant's data, and pointing a tenant at a different key requires a re-wrap (see Rotating a tenant's Master KEK).
Binding a consumer to a tenant¶
A consumer (an OAuth client, or a same-site module) acts inside exactly one tenant. When more than one tenant exists, the consumer add form shows a Tenant selector; the choice is immutable once the consumer is created (the field is locked on edit, and a change is refused). A consumer can only read and write vaults in its bound tenant - this is what scopes the cross-site API to a realm. With a single tenant the selector is hidden and every consumer belongs to the default tenant.
Shared and tenant-scoped item kinds¶
The item-kind vocabulary is hybrid: a kind is either shared (offered in every tenant, the default) or scoped to one tenant. On the kind form, a Tenant selector appears once more than one tenant exists - leave it Shared (all tenants) or pick a single tenant. The Item kinds list shows a Tenant column so you can see each kind's scope at a glance. Shared kinds and the current tenant's scoped kinds are the ones offered when storing an item.
Rotating a tenant's Master KEK¶
Master KEK rotation is per tenant: changing one tenant's key re-wraps only that tenant's subject keys, and never scans the others. After pointing a tenant at a new key, drain its re-wrap backlog:
- From the UI, at Configuration -> Personal Data Vault -> Vault subjects
(
/admin/config/pdv/subjects), use the per-tenant Rotate to current Master KEK action; it re-wraps that tenant's subject keys in bounded batches and reports progress. - From the command line,
drush pdv:rotate-keysdrains every active tenant, anddrush pdv:rotate-keys <tenant>drains a single tenant.
Cron advances each active tenant's backlog automatically. Once no subject is left on a tenant's old key, that key can be retired.
Resolving the current tenant (developers)¶
Which tenant a request acts in is decided by tenant resolvers: services
tagged pdv.tenant_resolver implementing TenantResolverInterface. Each
resolver returns a tenant id or NULL to defer; the first non-null answer wins,
and if none answer the default tenant is used.
pdv ships one resolver, ConsumerTenantResolver, which resolves the tenant
from the calling consumer's immutable binding - so a cross-site API call lands
in the consumer's tenant automatically. To resolve tenants another way (for
example by domain or path), implement TenantResolverInterface and tag the
service:
services:
my_module.tenant_resolver:
class: Drupal\my_module\MyTenantResolver
tags:
- { name: pdv.tenant_resolver }
A domain-based connector is not shipped yet; see the Roadmap.