Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

EDB Postgres Advanced Server TDE with Cosmian KMS

This guide demonstrates how to configure EDB Postgres Advanced Server 17 Transparent Data Encryption (TDE) to use Cosmian KMS as its key management server via KMIP over mTLS.

Overview

ItemDetails
ProtocolKMIP 2.1 over TCP/TLS with mutual certificate authentication (mTLS)
Port5696 or 15696 (configurable)
Key typeAES-256 symmetric key (CBC mode with PKCS#5 padding)
EDB versionEDB Postgres Advanced Server 15.2+ and 17.x
Clientedb_tde_kmip_client.py (shipped inside the EDB image)
Cosmian KMS featureWorks in non-FIPS mode (PyKMIP uses ssl.wrap_socket)

Architecture

flowchart TB
    pg["EDB Postgres Advanced Server<br/>(initdb --data-encryption)"]
    client["edb_tde_kmip_client.py<br/>(PGDATAKEYWRAPCMD / PGDATAKEYUNWRAPCMD)"]
    kms["Cosmian KMS<br/>(KMIP 2.1, mTLS, port 15696)"]

    pg -- "wrap DEK on initdb" --> client
    pg -- "unwrap DEK on startup" --> client
    client -- "KMIP Encrypt / Decrypt<br/>AES-256-CBC" --> kms

During initdb --data-encryption:

  1. Postgres generates a random cluster DEK.
  2. It invokes PGDATAKEYWRAPCMD — the edb_tde_kmip_client.py encrypt command.
  3. The client calls KMIP Encrypt on the Cosmian KMS, which returns [IV][ciphertext].
  4. The wrapped DEK is written to pg_encryption/key.bin.

On every subsequent server start, Postgres calls PGDATAKEYUNWRAPCMD (edb_tde_kmip_client.py decrypt) to unwrap the DEK before mounting the cluster.


Wire Format

The edb_tde_kmip_client.py script defines the on-disk format for the wrapped DEK:

key.bin = [16-byte IV][AES-256-CBC ciphertext]

This format is assembled client-side; the KMS is unaware of it.

KMIP Variants

The client supports two --variant options that differ in how they call KMIP:

VariantEncryptDecrypt
pykmip (required for Cosmian KMS)Encrypt(data=DEK) → KMS returns IVCounterNonce=IV, Data=CTDecrypt(Data=CT, IVCounterNonce=IV) — standard KMIP
thalesSame as pykmipDecrypt(Data=IV‖CT) — no IVCounterNonce; specific to Thales CipherTrust Manager

Note: Cosmian KMS follows the KMIP mandatory conformance requirement: a Decrypt request without IVCounterNonce returns Invalid_Message. Always use --variant=pykmip when connecting edb_tde_kmip_client.py to Cosmian KMS.


Prerequisites

  • A running Cosmian KMS server with the KMIP socket enabled (listen_address / port).
  • mTLS certificates: CA, server, and client certificate+key.
  • EDB Postgres Advanced Server 15.2+ or 17.x.
  • edb_tde_kmip_client.py installed via the edb-tde-kmip-client package from EDB repositories.
  • Python 3 with PyKMIP (pip3 install PyKMIP).

Configuration

1. KMS Configuration

Add the KMIP socket listener to kms.toml:

[server]
port = 9998

[server.kmip]
server_port = 15696

Generate or reuse mTLS certificates for the KMS and the EDB client. See Certificates for details.

2. Create the Master Key

The edb_tde_kmip_client.py script does not implement key creation. Use create_master_key.py (bundled with the Cosmian KMS test scripts) or any other KMIP client to create an AES-256 key:

# Using the bundled helper script
python3 .mise/scripts/edb_tde/create_master_key.py \
  --pykmip-config-file=/path/to/pykmip.conf
# → prints the key UID, e.g. 0193a4b2-...

The pykmip.conf file must point to the Cosmian KMS KMIP socket:

[client]
host = localhost
port = 15696
keyfile  = /path/to/client.key
certfile = /path/to/client.crt
ca_certs = /path/to/ca.crt

3. Configure PGDATAKEYWRAPCMD / PGDATAKEYUNWRAPCMD

export PGDATAKEYWRAPCMD='python3 /usr/edb/kmip/client/edb_tde_kmip_client.py \
  encrypt \
  --pykmip-config-file=/path/to/pykmip.conf \
  --key-uid=<UID from step 2> \
  --variant=pykmip \
  --out-file=%p'

export PGDATAKEYUNWRAPCMD='python3 /usr/edb/kmip/client/edb_tde_kmip_client.py \
  decrypt \
  --pykmip-config-file=/path/to/pykmip.conf \
  --key-uid=<UID from step 2> \
  --variant=pykmip \
  --in-file=%p'

4. Initialize the Cluster

initdb -D /var/lib/edb/data -y -U enterprisedb \
  --key-wrap-command="$PGDATAKEYWRAPCMD" \
  --key-unwrap-command="$PGDATAKEYUNWRAPCMD"

initdb calls PGDATAKEYWRAPCMD once to seal the freshly generated cluster DEK.

5. Start the Server

pg_ctl -D /var/lib/edb/data start

Verify TDE is active:

SELECT data_encryption_version FROM pg_control_init();
-- Returns 1 if TDE is enabled

Key Rotation

To rotate the master key:

PGDATA=/var/lib/edb/data
OLD_KEY=<old UID>
NEW_KEY=<new UID created via create_master_key.py>
KMIP_CONF=/path/to/pykmip.conf
CLIENT=/usr/edb/kmip/client/edb_tde_kmip_client.py

cd $PGDATA/pg_encryption
cp key.bin key.bin.bak

python3 $CLIENT decrypt --in-file=key.bin \
  --pykmip-config-file=$KMIP_CONF --key-uid=$OLD_KEY --variant=pykmip \
| python3 $CLIENT encrypt --out-file=key.bin \
  --pykmip-config-file=$KMIP_CONF --key-uid=$NEW_KEY --variant=pykmip

sed -i "s/$OLD_KEY/$NEW_KEY/" $PGDATA/postgresql.conf
pg_ctl -D $PGDATA restart

Running the Integration Tests

The Cosmian KMS repository includes a full integration test suite for EDB TDE. It requires an EDB subscription token to pull the edb-postgres-advanced:17 Docker image.

EDB_SUBSCRIPTION_TOKEN=<token> \
  mise run test:edb_tde --variant non-fips

The test suite:

  1. Logs in to docker.enterprisedb.com, starts the EDB container.
  2. Runs initdb with TDE — the real edb_tde_kmip_client.py seals the cluster DEK against Cosmian KMS over KMIP/mTLS.
  3. Verifies data_encryption_version = 1.
  4. Exercises both --variant=pykmip and --variant=thales wrap/unwrap roundtrips.
  5. Tests key rotation (re-wrap DEK with a new master key).

See .mise/scripts/edb_tde/README.md for full test documentation.


Reference