Skip to main content
Version: 2.0-rc2

ADR 028: Interim Encryption of Datasource Credentials at Rest

Date: 2026-02-05

Status: Proposed

Decision Makers: Architecture Board

Context​

ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027 establishes HashiCorp VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage. (or OpenBaoHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage.) as the target architecture for credential management in CIVITAS/CORE data pipelines. However, VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage. integration requires significant implementation effort (deployment, operational procedures, ConfigAdapterConfiguration AdapterThe component that consumes data models on behalf of platform components that cannot consume them directly, and configures the component accordingly. In the secrets management flow, the Configuration Adapter is the sole component that resolves Vault references into concrete credentials. extension, Kubernetes auth setup) that cannot be completed before the upcoming release.

Currently, we don't have a solution for storing external datasource credentials (database passwords, API keys, etc.) other than storing them in plaintext in the CIVITAS/CORE PostgreSQL database (not yet implemented). This is an unacceptable security posture, even for an interim period:

  • Any database backup, dump, or breach exposes all external credentials.
  • Compliance and audit requirements (particularly in the German municipal context) demand encryption of sensitive data at rest.
  • Platform operators with database access can read credentials for systems they are not authorized to access.

A lightweight interim solution is needed that:

  • Eliminates plaintext credential storage immediately.
  • Requires minimal implementation effort and no new infrastructure components.
  • Is fully compatible with the existing Spring Boot / JPA / PostgreSQL stack.
  • Provides a clear migration path to VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage. (ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027) in a subsequent release.

Checked Architecture Principles​

  • [partial] Model-centric data flow — Credentials remain co-located with datasource metadata in the CIVITAS database rather than being externalized to a dedicated secrets store. This is a conscious trade-off for implementation speed; ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027 addresses the target state.
  • [full] Distributed architecture with unified user experience — No user-facing changes; encryption is transparent at the persistence layer.
  • [full] Modular design — The encryption logic is encapsulated in a single JPA AttributeConverter, cleanly separated from business logic.
  • [full] Integration capability through defined interfaces — No interface changes; the converter operates transparently below the JPA entity layer.
  • [full] Open source as the default — Spring Security Crypto is part of the Spring Security OSS project.
  • [full] Cloud-native architecture — Master key is injected via Kubernetes Secrets and environment variables, following 12-factor principles.
  • [full] Prefer standard solutions over custom development — AES-256-GCM via Encryptors.text() from Spring Security Crypto; no custom cryptographic code.
  • [full] Self-contained deployment — No external service dependency introduced; only a Kubernetes Secret for the master key.
  • [full] Technological consistency to ensure maintainability — Spring Security is already part of the platform stack (KeycloakKeycloakAn open-source Identity and Access Management (IAM) solution providing SSO and OAuth2/OpenID Connect flows. In CIVITAS/CORE it is used to authenticate users and issue JWTs. integration); the crypto module is included transitively.
  • [full] Multi-tenancy — Encryption is applied uniformly; tenantTenantAn isolated organizational partition that owns Data pools, Datasets, Users, Groups, and Roles. All access rules exist within their Tenant, and the Tenant is the widest Scope of a Role. Currently, one Tenant corresponds to the Platform.-level key separation is not implemented in this interim solution but is also not required at the current stage.
  • [partial] Security by design — Credentials are encrypted at rest (AES-256-GCM), which is a significant improvement over plaintext. However, this approach has inherent limitations compared to VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage.: a single master key protects all credentials, there is no audit log of secret access, no dynamic rotation, and no fine-grained access control. These are accepted trade-offs for the interim period.

Decision​

JPA AttributeConverter with Spring Security Crypto​

All credential fields on datasource entities are annotated with a JPA @Convert annotation pointing to an EncryptedStringConverter. This converter uses Spring Security Crypto's Encryptors.delux() (AES-256-GCM with a random 16-byte IV per encryption operation, key derived via PKCS #5 PBKDF2) to transparently encrypt values before writing to the database and decrypt them upon reading. Encrypted results are returned as hex-encoded strings suitable for storage in standard VARCHAR/TEXT database columns.

Note: Encryptors.text() (AES-256-CBC) must not be used, as CBC mode does not provide authenticated encryption. Encryptors.delux() uses GCM, which guarantees both confidentiality and authenticity.

The converter is stateless from a JPA perspective. Existing application code that reads or writes credential fields requires no changes — encryption and decryption are fully transparent at the persistence layer.

Master Key Management​

A symmetric master key and salt are required by Encryptors.delux(). These are provided as environment variables (CIVITAS_MASTER_KEY, CIVITAS_MASTER_SALT), sourced from a Kubernetes Secret. The salt can be generated using Spring Security's KeyGenerators.string().generateKey(), which produces a cryptographically secure random 8-byte hex-encoded string. The master key must be:

  • Generated using a cryptographically secure random generator (minimum 256 bits, hex-encoded) during deployment.
  • Stored exclusively in a Kubernetes Secret (civitas-encryption-key), never in source code, configuration files, or the CIVITAS database.
  • Consistent across all pods that access encrypted credential fields (i.e., the portal backend and the ConfigAdapterConfiguration AdapterThe component that consumes data models on behalf of platform components that cannot consume them directly, and configures the component accordingly. In the secrets management flow, the Configuration Adapter is the sole component that resolves Vault references into concrete credentials. must share the same key).
  • Backed up securely and separately from database backups. Loss of the master key means loss of all encrypted credentials.

Scope of Encrypted Fields​

All fields on datasource-related entities that contain authentication material must be annotated. This includes, but is not limited to: password, apiKey, clientSecret, sshPrivateKey, and any similar fields identified during implementation.

Explicit Interim Status​

This solution is explicitly designated as an interim measure. It must be superseded by VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage. integration (ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027) in a subsequent release. To ensure this, the following constraints apply:

  • The EncryptedStringConverter class must carry a prominent @Deprecated annotation and Javadoc comment referencing ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027.
  • A technical debt ticket must be created to track the migration to VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage..

Consequences​

  • Portal backend must be extended with the EncryptedStringConverter and the @Convert annotations on all relevant entity fields. This is a low-effort change (estimated: < 1 day).
  • ConfigAdapterConfiguration AdapterThe component that consumes data models on behalf of platform components that cannot consume them directly, and configures the component accordingly. In the secrets management flow, the Configuration Adapter is the sole component that resolves Vault references into concrete credentials. must have access to the same master key to decrypt credentials when assembling pipeline configurations. This is already the case if both services read from the same Kubernetes Secret.
  • Database backups no longer expose plaintext credentials. However, they are only as secure as the master key — if both the backup and the master key are compromised, all credentials are recoverable.
  • Key rotation of the master key requires re-encrypting all stored credentials (decrypt with old key, encrypt with new key). This is a manual operational procedure in the interim solution.
  • Performance impact is negligible — AES-256-GCM encryption/decryption of short strings (passwords, API keys) is sub-millisecond.

Alternatives​

  • VaultHashiCorp VaultA secrets management system used as the platform's secrets backend. CIVITAS/CORE supports HashiCorp Vault (BSL) and its API-compatible open-source fork OpenBao. The platform does not implement its own encryption or secret storage. immediately (ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027): The target architecture. Discarded for this release due to insufficient implementation time. ADRArchitecture Decision RecordA document capturing an architecture decision. Each ADR has a stable identifier and short title and is managed through four lifecycle states: Proposed, Accepted, Deprecated and Superseded. 027 remains the agreed-upon target state.
  • PostgreSQL pgcrypto extension: Database-level encryption using pgp_sym_encrypt/pgp_sym_decrypt. Discarded because it couples encryption logic to SQL queries, leaks key material into query strings/logs, and is harder to migrate away from than an application-layer converter.
  • Jasypt (Java Simplified Encryption): A popular library for property/field encryption in Spring. Discarded because Spring Security Crypto is already available in the dependency tree and provides equivalent functionality with stronger defaults (AES-256-GCM vs. Jasypt's older PBE defaults).
  • No action (accept plaintext): Discarded. Plaintext credential storage is not acceptable.

See also​