Encryption & compliance

Vaks PM · Encryption, key management & DORA · June 2026

Purpose of this guide. This document is the cross-cutting reference for how Vaks PM protects data with encryption, how its cryptographic keys are managed, and how those controls map to regulatory expectations. It is written for a system administrator who is new to the product and assumes no prior knowledge of it. It explains the model, the policy and the concepts that apply whichever container orchestrator is used. The orchestrator-specific installation steps live in the deployment guides (Docker Swarm deployment and Kubernetes deployment), linked from each relevant section rather than repeated here. Every acronym is defined at first use and collected in the glossary below.

Overview & shared responsibility

Vaks PM is a project-management web application designed to run entirely on infrastructure the organization controls (on-premise: on its own servers rather than on a third-party online service). Encryption is applied at four independent layers, each protecting a different surface:

Shared-responsibility model. The product supplies the controls; the operator configures and operates the parts that depend on the surrounding infrastructure. Concretely:
  • The product provides, on by default: field-level envelope encryption of secrets, the public-edge TLS termination at the reverse proxy, the append-only tamper-evident audit log, and the data-protection (GDPR) features.
  • The product provides, opt-in / configurable: internal TLS between services, the choice of key-custody provider (local key ring, cloud KMS, on-prem HSM), and key rotation controls.
  • The operator must provide from the infrastructure: at-rest volume encryption of the data disks, secure custody and escrow of long-lived keys off-cluster, and (on Kubernetes) datastore-secret encryption and pod-network encryption.
Field-level encryption protects only the listed secrets. Protecting the full dataset at rest is the job of the volume-encryption layer; see Volume encryption at rest.

Glossary

Acronyms and terms used throughout this guide, defined once here for reference.

TermMeaning
DORADigital Operational Resilience Act: EU Regulation 2022/2554, which requires regulated financial entities to manage their information-and-communications-technology (ICT) risk, including encryption of data at rest and in transit and a documented key-management policy.
GDPRGeneral Data Protection Regulation: EU Regulation 2016/679 on the protection of personal data.
KEKKey Encryption Key: a master key whose only job is to encrypt ("wrap") other keys; it never touches the data itself.
DEKData Encryption Key: the key that actually encrypts the data. Vaks PM uses one DEK per tenant.
Envelope encryptionA two-layer scheme in which a DEK encrypts the data and a KEK encrypts the DEK. Changing the KEK re-wraps the small DEK without touching the bulk data.
AES-GCMAdvanced Encryption Standard, Galois/Counter Mode: an authenticated symmetric cipher, used here as AES-256-GCM (256-bit key) for field and key data.
AES-XTSThe AES mode designed for disk-sector (block-device) encryption; used by LUKS as aes-xts-plain64 with a 512-bit key.
KMSKey Management Service: a managed (typically cloud) service that holds keys and performs encrypt/decrypt without exporting the key.
HSMHardware Security Module: a tamper-resistant hardware device that holds keys and performs cryptographic operations on-premise.
PKCS#11A vendor-neutral standard programming interface for talking to an HSM or smart card.
LUKSLinux Unified Key Setup: the standard Linux full-volume disk-encryption format; version 2 (LUKS2) is used here.
ClevisA Linux framework that automatically unlocks an encrypted volume at boot by fetching key material from a network policy, with no human passphrase entry.
TangA small stateless network server that participates in Clevis "network-bound disk encryption": a volume can unlock only while it can reach the Tang server(s).
SSSShamir's Secret Sharing: a scheme that splits a secret into shares so that any threshold number of them reconstructs it. Clevis uses it to require, say, 1-of-N reachable Tang servers.
mTLSmutual TLS: Transport Layer Security where both ends present a certificate and verify the other's.
TLSTransport Layer Security: the encryption layer behind HTTPS and behind encrypted service-to-service connections.
CACertificate Authority: the entity that issues and signs X.509 certificates; its certificate is the trust anchor used to verify peers.
CSRCertificate Signing Request: a file holding a public key and requested attributes, submitted to a CA to be signed into a certificate.
SANSubject Alternative Name: the certificate field listing the IP addresses and DNS names the certificate is valid for.
ageA modern file-encryption tool using asymmetric keys: a public "recipient" encrypts, a matching private key decrypts. Used for backup artifacts.
RPORecovery Point Objective: the maximum acceptable amount of data loss, measured as time, after an incident.
PIIPersonally Identifiable Information: personal data such as names and email addresses.
TenantOne isolated organization served by the instance. Each tenant has its own database and its own DEK.

DORA control mapping

DORA (the Digital Operational Resilience Act, EU Regulation 2022/2554) and its supporting technical standards are risk-based rather than prescriptive of any specific product. They require encryption of data at rest and in transit commensurate with data classification, plus a key-management policy covering the key lifecycle, rotation and separation of duties. A third-party HSM or KMS is not mandatory for compliance.

Who is responsible. DORA compliance is the obligation of the regulated entity (the customer). Vaks PM, as an ICT provider, supplies the controls that make it achievable; the customer operates them under its own ICT-risk framework. The table below maps each DORA expectation to the control that addresses it.
DORA expectationHow Vaks PM addresses itStatus
Encryption of data at rest: application secrets AES-256-GCM envelope encryption: a per-tenant DEK encrypts the sensitive fields and is itself wrapped by a KEK. Single code seam in the application. Built-in
Encryption of data at rest: the whole data store LUKS2 (aes-xts-plain64, 512-bit) volumes for the database and the object store, with network-bound key release (Clevis + Tang). On Kubernetes: an encrypted StorageClass delegated to the platform. Operator-enabled
Encryption in transit: external TLS at the reverse proxy for all client traffic (HTTPS). Built-in
Encryption in transit: internal App↔database TLS; coordination-store mutual TLS; encrypted overlay/pod network covering the cache, object store and internal HTTP. Operator-enabled
Key management: lifecycle & rotation Versioned KEK key ring; lazy KEK re-wrap and admin-triggered rotation; DEK rotation re-encrypts all sensitive columns. See rotation and the runbook. Built-in
Key management: separation of duties The crypto:manage permission gates all key operations and every operation is audited. Hard custody (the KEK held outside the application) is available by moving the KEK to a cloud KMS or an on-prem HSM (PKCS#11); see Key custody providers. Control + KMS/HSM
Encrypted backups Backup artifacts are encrypted with age (asymmetric) at creation; the host holds only the public recipient. Operator-enabled
Integrity / tamper-evidence of logs Append-only audit log with a daily chained integrity anchor and a verification endpoint. Built-in

“Built-in” = active in every deployment. “Operator-enabled” = a control the operator turns on/provides from the infrastructure. “KMS/HSM” = external KEK custody, opt-in.

Data classification & encryption layers

Not all data is protected the same way. The application encrypts a small set of secrets at the field level; the bulk of the data (business data and personal data) relies on volume and transit encryption, because it must stay queryable. The table below shows which data sits at which classification and which layer protects it.

DataClassificationLayer(s)
Multi-factor (TOTP) seeds, email-relay (SMTP) password, single-sign-on (OIDC/SAML) client secrets, external ticketing-connector secrets, integration-provisioning secretsSecretField-level (envelope) + volume + in-transit
API token materialSecretStored as a SHA-256 hash (never reversible) + volume
User passwordsSecretArgon2 hash (never reversible) + volume
Personal data (names, emails, time entries, comments, attachments)ConfidentialVolume (at rest) + in-transit; attachments in the encrypted object store
Project / business dataConfidentialVolume + in-transit
Audit logConfidentialVolume + in-transit; integrity-anchored (tamper-evident)
Field-level encryption is for secrets, not for the whole dataset. Only the items classified Secret are field-encrypted (or one-way hashed). Business and personal data are deliberately not field-encrypted, since they must remain searchable and reportable, so their at-rest protection comes entirely from volume encryption. Personal data can also be pseudonymized on request (see GDPR).

Envelope encryption (KEK → DEK → data)

The application encrypts its sensitive fields with envelope encryption, a two-layer key scheme:

To read a secret, the application unwraps the tenant's DEK with the KEK, then decrypts the field with the DEK. To write one, it does the reverse. The diagram shows the relationship.

KEK master key · AES-256-GCM wraps ONE DEK PER TENANT DEK · tenant A AES-256-GCM · 32 bytes DEK · tenant B stored wrapped in DB DEK · tenant C plaintext only in memory encrypts Ciphertext fields SMTP password MFA seed · SSO secret in the tenant database
Envelope encryption: the KEK (held outside the data store) wraps each tenant's DEK; the DEK encrypts the sensitive fields. Rotating the KEK re-wraps the small DEKs; the bulk ciphertext is never touched.
Why two layers. Changing the KEK means re-wrapping a handful of tiny DEKs. The data is never re-encrypted, so rotation is fast and causes no downtime. Changing a DEK (the heavy operation that does re-encrypt data) is then only needed when a DEK is actually exposed, and affects one tenant at a time. The per-tenant DEK also enables crypto-shredding: destroying a single DEK makes all of that tenant's secrets permanently unrecoverable.

Step-by-step operational detail of the local key ring (generating and activating a KEK, the mounted key material and rotation) lives in the deployment guides: Docker Swarm and Kubernetes.

Key custody providers

The KEK is supplied by a key provider, selected by the CRYPTO_KEY_PROVIDER setting. Three providers exist; each stored key records which provider wrapped it, so providers can coexist during a migration. The default, the local key ring, is fully DORA-compliant on its own; the external providers add hard separation of duties, where no application administrator can export the key.

ProviderWhere the KEK livesSeparation of dutiesWhen to use
Local key ring default A mounted key file on the hosts (kek_v<N>, base64, 32 bytes), delivered as a Docker secret (Swarm) or a Kubernetes Secret. Soft: gated by crypto:manage and audited; a global admin can still reach key operations. The default. No external dependency; fully compliant.
Cloud KMS A managed cloud key service (AWS KMS / GCP KMS / Azure Key Vault or compatible). The KEK never leaves the KMS; every wrap/unwrap is performed by the service. Hard: the application cannot export the key. The deployment runs in or alongside a cloud with a managed KMS.
On-prem HSM A PKCS#11 hardware device, reached through an internal key-broker sidecar that holds the HSM PIN. The KEK is a non-extractable hardware key. Hard: the key is non-extractable hardware. Procurement requires hardware key custody, or an HSM is already operated.
Separation of duties. Within a single application a global administrator can still trigger key operations, so application-level access control (crypto:manage) is a control hook, not hard custody separation. Hard separation, where the custodian holds the KEK outside the application and it can no longer be exported, means moving the KEK to a cloud KMS or an on-prem HSM. Switching provider is an online re-wrap: no data is re-encrypted.

The provider configuration variables (CRYPTO_KMS_*, CRYPTO_HSM_*), the HSM key-broker sidecar, and the per-orchestrator placement of these settings are configured in the deployment guides: Kubernetes and Docker Swarm.

Migrating custody

Because each stored key records the provider that wrapped it, a migration between providers is online and reversible: no downtime, no data re-encryption. The outline:

  1. Configure the new provider and set CRYPTO_KEY_PROVIDER to kms or hsm, then roll the services. New DEKs are now wrapped by the new provider; existing ones still unwrap via their current provider, so a mix is expected and correct.
  2. In Admin → Security & Compliance → Encryption keys, click Rotate KEK. Every DEK is unwrapped with its current provider and re-wrapped under the new one. Repeat per tenant.
  3. Watch the data-key distribution until every key shows the new provider's scheme, then run a decrypt check (sign in, or open a settings page that reads an encrypted value).
  4. Reversible: keep the previous provider's key material available until verified. Rollback is setting CRYPTO_KEY_PROVIDER back and re-running Rotate KEK.

Key rotation

Two distinct rotations exist, with very different costs. Both are driven from Admin → Security & Compliance → Encryption keys and require the crypto:manage permission; both are written to the audit log.

OperationWhat it doesCostWhen
Rotate KEK (lazy re-wrap) Stages a new KEK version, activates it, and re-wraps every per-tenant DEK under it. The DEK is unchanged; no field data is re-encrypted. Fast, no downtime: only tiny DEKs are touched. On a schedule (annually is a reasonable default) or immediately on suspected KEK compromise.
Rotate DEK (re-encryption) Mints a new active DEK for the tenant and re-encrypts every sensitive column under it, then retires the old DEK. The operation is resumable: if interrupted, running it again completes the migration. Heavier; touches all of the tenant's encrypted fields. On suspected exposure of a data key, or per policy.
Never delete or unmount a KEK that still wraps a live DEK. If a KEK is removed while any DEK is still wrapped by it, that DEK can no longer be unwrapped and every secret it protects is permanently lost. This applies to retained backups too: a database backup contains only wrapped DEKs, so a KEK must be kept at least as long as the longest retention of any backup made while it was active. Verify with the status endpoint (or the admin panel) that no DEK and no retained backup references a KEK before retiring it.

Rotation & incident runbook

The response to a suspected key compromise depends on which key is involved. Record every incident; the audit log evidences each key operation with actor, time and outcome.

CompromiseResponse
KEK exposed Rotate the KEK immediately (re-wrap), then rotate the DEKs so the new key material is independent of the exposed KEK. Do not delete the old KEK until every backup predating the rotation has aged out.
DEK exposed Rotate the affected tenant's DEK (re-encrypt). The previous DEK is retired and purged once re-encryption completes with zero failures.
TLS / JWT secret exposed Rotate the secret. Rotating the session-signing (JWT) secret invalidates live sessions, so users re-authenticate. For internal TLS, revoke the certificate at the CA, issue a new one and re-apply the TLS procedure.
Backup age key exposed Generate a new age key pair; subsequent backups use the new recipient. Older artifacts remain encrypted to the old key, so destroy them per retention.

Volume encryption at rest

Field-level encryption protects only the secrets. To protect the entire dataset on disk (the database files and the object-store files) the data volumes themselves are encrypted. This is the layer that the operator provides; the approach differs by orchestrator.

Docker Swarm: LUKS2 with network-bound unlock (Clevis + Tang)

On the self-managed Swarm topology, the bootstrap provisions LUKS2 file-backed volumes for the data stores. Two volumes are created:

Each volume is formatted with cipher aes-xts-plain64 and a 512-bit key. Only the data volumes are encrypted, not the host OS disk and not other workloads.

The challenge with at-rest encryption is unlocking the volume at boot without a human typing a passphrase. Vaks PM solves this with network-bound disk encryption using Clevis and Tang:

Why network-bound. Because the unlock key is released only by a reachable peer Tang server, a stolen disk on its own cannot be decrypted: it is not on the network with its peers. There is no static passphrase baked into the host. This satisfies at-rest protection while keeping unattended reboots automatic.
Recovery passphrase. When each volume is created, a random recovery passphrase is also installed (in a separate LUKS key slot) and written to a file on the host. This is the manual fallback for the case where no peer Tang server is reachable (for example a full-cluster cold start, or a site with Tang unavailable). This passphrase must be exfiltrated to an off-cluster vault and then deleted from the host, otherwise it sits next to the data it protects. Without it, a volume cannot be unlocked when Tang is unreachable.

The exact bootstrap commands (installing Tang, building the Clevis SSS policy, formatting and binding each volume, and the ordering relative to the database start-up) are in Docker Swarm deployment. This section describes what those steps do and the policy behind them.

Kubernetes: encrypted StorageClass

On Kubernetes the equivalent is an encrypted StorageClass, delegated to the platform: the database, cache and object-store persistent volumes are provisioned from a class backed by the cloud provider's key-managed block storage, a Container Storage Interface (CSI) driver with encryption, or nodes with encrypted disks. When the dependencies are external/managed (for example a managed database service), use that provider's own at-rest encryption instead. This is an install-time choice: changing it on a running cluster means migrating volume data. See Kubernetes deployment and the encryption-layer detail it links.

Internal TLS & custom CA

Encryption covers the public edge (HTTPS at the reverse proxy) and the product's internal traffic as well. Three internal flows are encrypted:

FlowProtectionCertificate
App ↔ PostgreSQL (and replication between database nodes)PostgreSQL TLSServer certificate
Cluster coordination store (consensus + failover-manager access)mutual TLS (mTLS)Server + client certificate
Container overlay / pod network (cache, object store, internal HTTP)Symmetric-key encryption managed by the orchestratorNone (no certificate)

A fresh install runs internal TLS out of the box with self-signed certificates: traffic is encrypted, but the peer's identity is not verified. Replacing those with certificates issued by the organization's own CA (Certificate Authority) adds identity verification, which protects against an active man-in-the-middle.

Certificate specification (the key points)

From CSR to deployment

Produce a CSR (Certificate Signing Request) carrying the SAN and the dual EKU, have the internal CA sign it (requiring that it preserves the SAN and EKU and returns the chain), and deploy three PEM files: the leaf certificate, its private key (unencrypted, 600 permissions, owned by the service user), and the CA certificate used as the trust anchor. On Swarm the files go on each host under /etc/vaks/ and the trust anchors are re-pointed at the CA one node at a time, to preserve availability. On Kubernetes the certificate is supplied as a Secret (or issued by cert-manager) and the application verifies the database with PG_TLS / PG_TLS_CA / PG_TLS_MODE.

Apply node by node. Never replace internal certificates on all coordination-store / database nodes at once. Handle replicas first, then switch the primary role to an already-updated node, then the former primary, verifying cluster health between each step. Re-pointing the coordination store's trust anchor incorrectly, or applying everywhere simultaneously, can take the database cluster offline.

The per-orchestrator file placement, the supply of a custom Certificate Authority and the verification commands are covered in the deployment guides: Docker Swarm and Kubernetes.

GDPR: erasure & portability

Encryption protects data confidentiality; GDPR (the EU General Data Protection Regulation) governs how personal data is handled. Vaks PM implements two relevant data-subject rights as product features:

These features and their administration are described in the product guide: see Product: GDPR (anonymization & portability).

Encrypted backups

Backup artifacts are encrypted at creation with age, an asymmetric (public-key) scheme: a public recipient key sits on the host and seals each artifact, while the matching private key is held in escrow off-cluster. The host can therefore create encrypted backups but cannot, on its own, decrypt them.

Because the KEK lives outside the database, a database backup contains only the wrapped per-tenant DEKs plus ciphertext. Restoring it works only if the key ring still holds the KEK that wrapped those DEKs. Two coordination rules follow:

A database snapshot is self-consistent even mid-rotation: the active and retired DEKs, and every ciphertext blob (each carrying its own DEK reference), live in the same database. The backup tiers, the age recipient configuration and the restore procedure are in Operations.

Lock-out recovery & escrow

There is no backdoor; escrow every long-lived key off-cluster. If the active KEK is lost, the wrapped DEKs (and therefore the field-encrypted secrets) cannot be recovered. Likewise, losing the LUKS recovery passphrase while Tang is unreachable, or losing the backup age private key, renders the corresponding data unrecoverable. This is by design. The mitigation is escrow: keep a copy of every long-lived key in an off-cluster vault with audited access and retention ≥ the data it protects.

The escrow inventory every operator must maintain off-cluster:

KeyProtectsWhy it must be escrowed
KEK material (every kek_v<N> still referenced, plus the legacy master key)The wrapped DEKs → all field-encrypted secretsThe KEK lives outside the database; a database backup cannot be decrypted without it. A from-scratch DR cluster generates fresh secrets, so the original KEK must be transplanted to the DR site.
LUKS recovery passphrase (per data volume)The at-rest data volumesThe only way to unlock a volume when no peer Tang server is reachable (cold start / Tang outage).
Backup age private keyThe encrypted backup artifactsThe host holds only the public recipient; without the private key the backups cannot be decrypted at restore time.
Internal TLS private key + CAInternal in-transit verificationNeeded to re-establish verified internal TLS on a rebuilt cluster.
Disaster recovery: transplant the keys, not just the data. Restoring business data onto a fresh cluster is not enough, because the restored DEKs reference the original KEK. Copy the original KEK material (and the age private key) to the DR site before declaring the restore complete; otherwise business data restores but the encrypted secrets (MFA, SMTP, SSO) are unrecoverable. Rehearse a full DR restore on a test site at least annually.

Verification checklist

Confirm each layer is active before relying on it:


Related: The application · Technical architecture · Docker Swarm deployment · Kubernetes deployment · Operations · Demo