F.62. pg_tde#

F.62. pg_tde

F.62. pg_tde #

pg_tde extension that provides Transparent Data Encryption (TDE) to protect data at rest. pg_tde ensures that the data stored on disk is encrypted, and that no one can read it without the proper encryption keys, even if they gain access to the physical storage media.

F.62.1. Quickstart #

F.62.1.1. Installation #

The extension must be added to the shared_preload_libraries. You also need to restart the server after changing shared_preload_libraries.

  shared_preload_libraries = 'pg_tde'

Then you should install the pg_tde extension.

CREATE EXTENSION pg_tde;

If GOST algorithms are needed (magma or kuznyechik), install the required OpenSSL modules. You can build from source or install via packages.

There is list of known packages for different distributions that contain GOST module for OpenSSL:

  1. libgost-astra - Astra Linux

  2. openssl-gost-engine - AltLinux

  3. libengine-gost-openssl or libengine-gost-openssl1.1 - Debian/Ubuntu

  4. openssl-gost-engine - RedOS

For other distributions you have to build it from source code. Project source code is located in GitHub repository.

When GOST modules installed you might need to update OpenSSL configuration. Some packages (i.e. libgost-astra) do it automatically. For others you might have to update configuration manually.

F.62.1.2. Creating an encrypted table #

Register a key provider:

  SELECT pg_tde_add_database_key_provider_file('file-provider', '/tmp/test-file-provider');

Note

file-provider can only be used for debugging purposes.

Create a primary key:

  SELECT pg_tde_set_key_using_database_key_provider('test-key', 'file-provider');

Create an encrypted table:

  CREATE TABLE enc_tbl(x int) USING tde_heap;

Check if the table is encrypted.

  SELECT pg_tde_is_encrypted('enc_tbl'), pg_tde_get_algorithm('enc_tbl');

Creating an encrypted table with a choice of encryption algorithm:

  CREATE TABLE enc_tbl1(x int) USING tde_heap WITH (encryptor='magma');

F.62.2. What is Transparent Data Encryption (TDE)? #

Transparent Data Encryption (TDE) protects your data at rest by ensuring that even if the underlying storage is compromised, the data remains unreadable without the correct encryption keys. This is especially critical for environments handling sensitive, regulated, or high-value information.

Encryption happens transparently in the background, with minimal impact on database operations.

F.62.2.1. How TDE Works #

To encrypt the data, two types of keys are used:

  • Internal encryption keys to encrypt user data. They are stored internally, near the data that they encrypt.

  • The principal key to encrypt database keys. It is kept separately from the database keys and is managed externally in the key management store.

You have the following options to store and manage principal keys externally:

The encryption process is the following:

When a user creates an encrypted table using pg_tde, a new random key is generated internally for that table and is encrypted using the AES-CBC-256 cipher algorithm. This key is used to encrypt all data the user inserts in that table. Eventually the encrypted data gets stored in the underlying storage.

The internal key itself is encrypted using the principal key. The principal key is stored externally in the key management store.

Similarly when the user queries the encrypted table, the principal key is retrieved from the key store to decrypt the internal key. Then the same unique internal key for that table is used to decrypt the data, and unencrypted data gets returned to the user. So, effectively, every TDE table has a unique key, and each table key is encrypted using the principal key.

F.62.2.2. Encrypted Data Scope #

pg_tde encrypts the following components:

  • User data in tables using the extension, including associated TOAST data. The table metadata (column names, data types, etc.) is not encrypted.

  • Write-Ahead Log (WAL) data for the entire database cluster. This includes WAL data in encrypted and non-encrypted tables.

  • Indexes associated encrypted tables.

F.62.2.3. Table Access Methods and TDE #

A table access method is the way how Tantor SE stores the data in a table. The default table access method is heap. Tantor SE organizes data in a heap structure, meaning there is no particular order to the rows in the table. Each row is stored independently and identified by its unique row identifier (TID).

F.62.2.3.1. How tde_heap works #

The tde_heap access method works on top of the default heap access method and is a marker to point which tables require encryption.

Data in buffers stored unencrypted. Encryption and decryption happens only when read/write request to disk occurs.

Thus, there is no overhead for internal operations.

F.62.2.3.2. Changing the default table access method #

You can change the default table access method so that every table in the entire database cluster is created using the custom access method. For example, you can enable data encryption by default by defining the tde_heap as the default table access method.

However, consider the following before making this change:

  • This is a global setting and applies across the entire database cluster and not just a single database. We recommend setting it with caution because all tables and materialized views created without an explicit access method in their CREATE statement will default to the specified table access method.

  • You must create the pg_tde extension and configure the key provider for all databases before you modify the configuration. Otherwise Tantor SE won’t find the specified access method and will throw an error.

Here is how you can set the new default table access method:

  1. Add the access method to the default_table_access_method parameter:

    Via the SQL statement

    Use the ALTER SYSTEM command. This requires superuser or ALTER SYSTEM privileges.

      ALTER SYSTEM SET default_table_access_method = tde_heap;
      

    Via the configuration file

    Edit the postgresql.conf configuration file and add the value for the default_table_access_method parameter.

      default_table_access_method = 'tde_heap'
      

    Via the SET command

    You can use the SET command to change the default table access method temporarily, for the current session.

    Unlike modifying the postgresql.conf file or using the ALTER SYSTEM command, the changes you make via the SET command don’t persist after the session ends.

    You also don’t need to have the superuser privileges to run the SET command.

    You can run the SET command anytime during the session.

      SET default_table_access_method = tde_heap;
      
  2. Reload the configuration to apply the changes:

      SELECT pg_reload_conf();
      

F.62.2.4. Limitations of pg_tde #

  • Keys in the local keyfile provider are stored unencrypted. It is not recommended for production use. For better security we recommend using another KMS, like HashiCorp Vault or KMIP.

  • System tables are not encrypted. This means that statistics data and database metadata are not encrypted.

  • Temporary files used during work (for sorting or hashing) are not encrypted.

  • Some tools that work with WAL will fail if it is encrypted. They include: pg_rewind, pg_resetwal, pg_upgrade.

  • WAL-G does not support sending WAL deltas if the WAL is encrypted.

  • Tables are encrypted using block cipher. It means that corruption of a single byte in the end of page will invalidate the whole page and after decryption there will be garbage. Tools that expect to work with heap access method can misbehave. For example, pg_amcheck or amcheck might not be able to recover pages because page validation will fail (header is invalid).

F.62.3. Key Rotation #

Currently, only principal key rotation is supported.

Rotation simply involves assigning a new principal key:

SELECT pg_tde_set_key_using_database_key_provider('new-key');

Existing internal keys will be re-encrypted; table data remains unchanged.

Example:

SELECT pg_tde_add_database_key_provider_file('file-provider', '/tmp/test-file-provider');
SELECT pg_tde_set_key_using_database_key_provider('test-key-1', 'file-provider');
CREATE TABLE enc_tbl(x int) USING tde_heap;
INSERT INTO enc_tbl(x) VALUES (1), (2), (3), (4), (5);

-- Rotate the principal key
SELECT pg_tde_set_key_using_database_key_provider('test-key-2');

F.62.3.1. Replication #

Physical replication is fully supported. WAL records related to TDE are replayed on the replica.

Note

For GOST encryption, ensure required modules are installed on the replica.

F.62.3.2. OpenSSL Configuration #

On startup, the extension loads an OpenSSL configuration file. Use pg_tde.openssl_config to specify a custom path. If unset, the system default is used (e.g., /etc/ssl/openssl.cnf on Astra Linux).

Some packages (i.e. libgost-astra on Astra Linux) update configuration automatically.

Note

In our distribution, GOST modules (gost.so, gostprov.so) are located at /opt/tantor/db/{VERSION}/lib.

The pg_tde.preinit_algs parameter specifies algorithms to preload at startup for performance and validation.

Format: comma-separated list (like shared_preload_libraries). Supported: aes, chacha20, kuznyechik, magma.

Example:

pg_tde.preinit_algs = 'aes,chacha20,kuznyechik'

F.62.4. Features #

The following features are available for the extension:

  • Table encryption, including:

    • Data tables

    • Index data for encrypted tables

    • TOAST tables

    • Temporary tables created during database operations

    Metadata of those tables is not encrypted.

  • Global Write-Ahead Log (WAL) encryption for data in both encrypted and non-encrypted tables

  • Multiple Key management options

F.62.5. Configure pg_tde #

Before you can use pg_tde for data encryption, you must enable the extension and configure Tantor SE to load it at startup. This setup ensures that the necessary hooks and shared memory are available for encryption operations.

The pg_tde extension requires additional shared memory. You need to configure Tantor SE to prelaod it at startup.

  1. Configure shared_preload_libraries

    You can configure the shared_preload_libraries parameter in two ways:

    • Add the following line to the shared_preload_libraries file:

      shared_preload_libraries = 'pg_tde'
      
    • Use the ALTER SYSTEM command. Run the following command in psql as a superuser:

      ALTER SYSTEM SET shared_preload_libraries = 'pg_tde';
      
  2. Restart the Tantor SE cluster to apply the configuration.

  3. Create the extension

    After restarting Tantor SE, connect to psql as a superuser or database owner and run:

    CREATE EXTENSION pg_tde;
    

    See CREATE EXTENSION for more details.

    Note

    The pg_tde extension is created only for the current database. To enable it for other databases, you must run the command in each individual database.

  4. (Optional) Enable pg_tde by default

    To automatically have pg_tde enabled for all new databases, modify the template1 database:

    psql -d template1 -c 'CREATE EXTENSION pg_tde;'
    

    Note

    You can use external key providers to manage encryption keys. The recommended approach is to use the Key Management Store (KMS). See the next step on how to configure the KMS.

F.62.5.1. Configure Key Management (KMS) #

In production environments, storing encryption keys locally on the Tantor SE server can introduce security risks. To enhance security, pg_tde supports integration with external Key Management Systems (KMS) through a Global Key Provider interface.

This section describes how you can configure pg_tde to use the local and external key providers. To use an external KMS with pg_tde, follow these two steps:

  1. Configure a Key Provider

  2. Set the Global Principal Key

Note

While keyfiles may be acceptable for local or testing environments, KMS integration is the recommended approach for production deployments.

F.62.5.2. KMIP Configuration #

To use a Key Management Interoperability Protocol (KMIP) server with pg_tde, you must configure it as a global key provider. This setup enables pg_tde to securely fetch and manage encryption keys from a centralized key management appliance.

Note

You need the root certificate of the KMIP server and a client key/certificate pair with permissions to create and read keys on the server.

It is recommended to review the configuration guidelines for the HashiCorp Vault Enterprise KMIP Secrets Engine if you’re using Vault.

For testing purposes, you can use a lightweight PyKMIP server, which enables easy certificate generation and basic KMIP behavior. If you’re using a production-grade KMIP server, ensure you obtain valid, trusted certificates from the key management appliance.

F.62.5.2.1. Example usage #
SELECT pg_tde_add_global_key_provider_kmip(
    'provider-name',
    'kmip-IP',
    5696,
    '/path_to/server_certificate.pem',
    '/path_to/client_cert.pem',
    '/path_to/client_key.pem'
);
F.62.5.2.2. Parameter descriptions #
  • provider-name is the name of the provider. You can specify any name, it’s for you to identify the provider

  • kmip-IP is the IP address of a domain name of the KMIP server

  • port is the port to communicate with the KMIP server. Typically used port is 5696

  • server-certificate is the path to the certificate file for the KMIP server

  • client_cert is the path to the client certificate.

  • client_key (optional) is the path to the client key. If not specified, the certificate key has to contain both the certifcate and the key.

Warning

pg_tde_add_global_key_provider_kmip currently accepts only a combined client key and a client certificate for its final parameter, reffered to as client key.

The following example is for testing purposes only.

SELECT pg_tde_add_global_key_provider_kmip(
    'kmip','127.0.0.1',
    5696,
    '/tmp/server_certificate.pem',
    '/tmp/client_cert_jane_doe.pem',
    '/tmp/client_key_jane_doe.pem'
);

For more information on related functions, see the link below:

Functions

F.62.5.3. Vault Configuration #

You can configure pg_tde to use HashiCorp Vault as a global key provider for managing encryption keys securely.

Note

This guide assumes that your Vault server is already set up and accessible. Vault configuration is outside the scope of this document, see Vault’s official documentation for more information.

F.62.5.3.1. Example usage #
SELECT pg_tde_add_global_key_provider_vault_v2(
    'provider-name',
    'secret_token_path',
    'url',
    'mount',
    'ca_path'
);
F.62.5.3.2. Parameter descriptions #
  • provider-name is the name to identify this key provider

  • secret_token is an access token with read and write access to the above mount point

  • url is the URL of the Vault server

  • mount is the mount point where the keyring should store the keys

  • [optional] ca_path is the path of the CA file used for SSL verification

The following example is for testing purposes only. Use secure tokens and proper SSL validation in production environments:

SELECT pg_tde_add_global_key_provider_vault_v2(
    'my-vault',
    '/path/to/token_file',
    'http://vault.vault.svc.cluster.local:8200',
    'secret/data',
    NULL
);

For more information on related functions, see the link below:

Functions

F.62.5.4. Keyring File Configuration #

This setup is intended for development and stores the keys unencrypted in the specified data file.

Note

While keyfiles may be acceptable for local or testing environments, KMS integration is the recommended approach for production deployments.

SELECT pg_tde_add_global_key_provider_file(
    'provider-name',
    '/path/to/the/keyring/data.file'
);

The following example is used for testing purposes only:

SELECT pg_tde_add_global_key_provider_file(
    'file-keyring',
    '/tmp/pg_tde_test_local_keyring.per'
);

F.62.5.5. Global Principal Key Configuration #

You can configure a default principal key using a global key provider. This key will be used by all databases that do not have their own encryption keys configured.

F.62.5.5.1. Create a default principal key #

Run the following command:

SELECT pg_tde_set_default_key_using_global_key_provider(
    'name-of-the-key',
    'provider-name',
    'ensure_new_key'
);
F.62.5.5.2. Parameter description #
  • name-of-the-key is the name of the principal key. You will use this name to identify the key.

  • provider-name is the name of the key provider you added before. The principal key will be associated with this provider.

  • ensure_new_key defines if a principal key must be unique. The default value true means that you must speficy a unique key during key rotation. The false value allows reusing an existing principal key.

This example is for testing purposes only. Replace the key name and provider name with your values:

SELECT pg_tde_set_key_using_global_key_provider(
    'test-db-master-key',
    'file-vault',
    'ensure_new_key'
);

Note

The key is auto-generated.

After this, all databases that do not have something else configured will use this newly generated principal key.

F.62.6. Validate Encryption with pg_tde #

After enabling the pg_tde extension for a database, you can begin encrypting data using the tde_heap table access method.

F.62.6.1. Creating an encrypted table #

  1. To create a table with encryption enabled, using the tde_heap access method as follows:

    CREATE TABLE <table_name> (<field> <datatype>) USING tde_heap
      [ WITH (encryptor = aes | kuznyechik | grasshopper | magma | chacha20 ) ];
    

    You can also specify the storage parameter 'encryptor', which allows you to select an encryption algorithm.

    Example:

    CREATE TABLE albums (
        album_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
        artist_id INTEGER,
        title TEXT NOT NULL,
        released DATE NOT NULL
    ) USING tde_heap WITH (encryptor = magma);
    
  2. To check if the data is encrypted, run the following function:

    SELECT pg_tde_is_encrypted('table_name');
    

    The function returns t if the table is encrypted and f - if not.

  3. (Optional) Rotate the principal key.

To re-encrypt the data using a new key, see Principal key management.

F.62.6.2. Encrypt existing table #

You can encrypt an existing table. It requires rewriting the table, so for large tables, it might take a considerable amount of time.

Run the following command:

ALTER TABLE table_name SET ACCESS METHOD tde_heap;

Caution

Using SET ACCESS METHOD drops hint bits which can impact query performance. To restore performance, run:

    SELECT count(*) FROM table_name;

This forces Tantor SE to check every tuple for visibility and reset the hint bits.

Tip

Want to remove encryption later? See how to decrypt your data.

F.62.7. Configure WAL Encryption (tech preview) #

Before turning WAL encryption on, you must follow the steps below to create your first principal key.

F.62.7.1. Create the principal key #

  1. Create the pg_tde extension if it does not exist:

    CREATE EXTENSION IF NOT EXISTS pg_tde;
    
  2. Set up the key provider for WAL encryption

    With KMIP server

    Make sure you have obtained the root certificate for the KMIP server and the keypair for the client. The client key needs permissions to create / read keys on the server. Find the configuration guidelines for the HashiCorp Vault Enterprise KMIP Secrets Engine.

    For testing purposes, you can use the PyKMIP server which enables you to set up required certificates. To use a real KMIP server, make sure to obtain the valid certificates issued by the key management appliance.

    SELECT pg_tde_add_global_key_provider_kmip('provider-name','kmip-addr', 5696, '/path_to/server_certificate.pem', '/path_to/client_cert.pem', '/path_to/client_key.pem');
    

    where:

    • provider-name is the name of the provider. You can specify any name, it’s for you to identify the provider.

    • kmip-addr is the IP address of a domain name of the KMIP server

    • port is the port to communicate with the KMIP server. Typically used port is 5696.

    • server-certificate is the path to the certificate file for the KMIP server.

    • client-cert is the path to the client certificate.

    • client-key is the path to the client key.

    Warning

    This example is for testing purposes only:

    SELECT pg_tde_add_key_using_global_key_provider_kmip('kmip','127.0.0.1', 5696, '/tmp/server_certificate.pem', '/tmp/client_cert_jane_doe.pem', '/tmp/client_key_jane_doe.pem');
    

    With HashiCorp Vault

    SELECT pg_tde_add_global_key_provider_vault_v2('provider-name', 'secret_token_path', 'url', 'mount', 'ca_path');
    

    where:

    • provider-name is the name you define for the key provider

    • url is the URL of the Vault server

    • mount is the mount point where the keyring should store the keys

    • secret_token_path is a path to the file that contains an access token with read and write access to the above mount point

    • [optional] ca_path is the path of the CA file used for SSL verification

    With keyring file

    This setup is not recommended, as it is intended for development. The keys are stored unencrypted in the specified data file.

    SELECT pg_tde_add_global_key_provider_file('provider-name','/path/to/the/keyring/data.file');
    
  3. Create principal key

    SELECT pg_tde_set_server_key_using_global_key_provider('key', 'provider-name');
    
  4. Enable WAL level encryption using the ALTER SYSTEM command. You need the privileges of the superuser to run this command:

    ALTER SYSTEM SET pg_tde.wal_encrypt = on;
    
  5. Restart the server to apply the changes.

Now WAL files start to be encrypted for both encrypted and unencrypted tables.

For more technical references related to architecture, variables or functions, see Technical Reference.

F.62.8. Technical Reference #

This section covers the internal components and tools that power pg_tde.

Use it to understand how encryption is implemented, fine-tune a configuration, leverage advanced CLI tools and functions for diagnostics and customization.

F.62.9. Architecture #

pg_tde is a customizable, complete, data at rest encryption extension for Tantor SE.

Let’s break down what it means.

Customizable means that pg_tde aims to support many different use cases:

  • Encrypting either every table in every database or only some tables in some databases

  • Encryption keys can be stored on various external key storage servers including Hashicorp Vault, KMIP servers.

  • Using one key for everything or different keys for different databases

  • Storing every key on the same key storage, or using different storages for different databases

  • Handling permissions: who can manage database specific or global permissions, who can create encrypted or not encrypted tables

Complete means that pg_tde aims to encrypt data at rest.

Data at rest means everything written to the disk. This includes the following:

  • Table data files

  • Indexes

  • Sequences

  • Temporary tables

  • Write Ahead Log (WAL)

F.62.9.1. Encryption architecture #

F.62.9.1.1. Two-key hierarchy #

pg_tde uses two kinds of keys for encryption:

  • Internal keys to encrypt the data. They are stored in Tantor SE’s data directory under $PGDATA/pg_tde.

  • Higher-level keys to encrypt internal keys. These keys are called principal keys. They are stored externally, in a Key Management System (KMS) using the key provider API.

pg_tde uses one principal key per database. Every internal key for the given database is encrypted using this principal key.

Internal keys are used for specific database files: each file with a different Object Identifier (OID) has a different internal key.

This means that, for example, a table with 4 indexes will have at least 5 internal keys - one for the table, and one for each index.

If a table has additional associated relations, such as sequences or a TOAST table, those relations will also have separate keys.

F.62.9.1.2. Encryption algorithm #

For encrypting relations pg_tde uses the following encryption algorithms:

  • AES-CBC-256

  • ChaCha20

  • GOST 28147-89 Magma

  • GOST R 34.12-2015 Kuznyechik

For encrypting WAL AES-CTR-256 is used.

For encrypting internal keys AES-GCM-256. It provides data authenticity (integrity) so it is impossible in practice to spoof keys.

F.62.9.1.3. Encryption workflow #

pg_tde makes it possible to encrypt everything or only some tables in some databases.

To support this without metadata changes, encrypted tables are labeled with a tde_heap access method marker.

The tde_heap access method is the same as the heap one. It uses the same functions internally without any changes, but with the different name and ID. In such a way pg_tde knows that tde_heap tables are encrypted and heap tables are not.

The initial decision what to encrypt is made using the postgres event trigger mechanism: if a CREATE TABLE or ALTER TABLE statement uses the tde_heap clause, the newly created data files are marked as encrypted. Then file operations encrypt or decrypt the data.

Later decisions are made when a database file is re-created with a different relfilenode as a result of a TRUNCATE or a VACUUM FULL command, the newly created file inherits the encryption information and is either encrypted or not.

F.62.9.1.4. WAL encryption #

WAL encryption is controlled globally via a global GUC variable, pg_tde.wal_encrypt, that requires a server restart.

WAL keys also contain the LSN of the first WAL write after key creation. This allows pg_tde to know which WAL ranges are encrypted or not and with which key.

The setting only controls writes so that only WAL writes are encrypted when WAL encryption is enabled. This means that WAL files can contain both encrypted and unencrpyted data, depending on what the status of this variable was when writing the data.

pg_tde keeps track of the encryption status of WAL records using internal keys. When the server is restarted it writes a new internal key if WAL encryption is enabled, or if it is disabled and was previously enabled it writes a dummy key signalling that WAL encryption ended.

With this information the WAL reader code can decide if a specific WAL record has to be decrypted or not and which key it should use to decrypt it.

F.62.9.1.5. Encrypting other access methods #

Currently pg_tde only encrypts heap tables and other files such as indexes, TOAST tables, sequences that are related to the heap tables.

F.62.9.2. Key and key provider management #

F.62.9.2.1. Principal key rotation #

You can rotate principal keys to comply with common policies and to handle situations with potentially exposed principal keys.

Rotation means that pg_tde generates a new version of the principal key, and re-encrypts the associated internal keys with the new key. The old principal key is kept as is at the same location, because it may still be needed to decrypt backups or other databases.

F.62.9.2.2. Internal key regeneration #

Internal keys for tables, indexes and other data files are fixed once a file is created. There’s no way to re-encrypt a file.

There are workarounds for this, because operations that move the table data to a new file, such as VACUUM FULL or an ALTER TABLE that rewrites the file will create a new key for the new file, essentially rotating the internal key. This however means taking an exclusive lock on the table for the duration of the operation, which might not be desirable for huge tables.

WAL internal keys are also fixed to the respective ranges. To generate a new WAL key you need to restart the database.

F.62.9.2.3. Internal key storage #

Internal keys and pg_tde metadata in general are kept in a single $PGDATA/pg_tde directory. This directory stores separate files for each database, such as:

  • Encrypted internal keys and internal key mapping to tables

  • Information about the key providers

Also, the $PGDATA/pg_tde directory has a special global section marked with the OID 1664, which includes the global key providers and global internal keys.

The global section is used for WAL encryption. Specific databases can use the global section too, for scenarios where users configure individual principal keys for databases but use the same global key provider. For this purpose, you must enable the global provider inheritance.

The global default principal key uses the special OID 1663.

F.62.9.2.4. Key providers (principal key storage) #

Principal keys are stored externally in a Key Management Services (KMS). In pg_tdea KMS is defined as an external key provider.

The following key providers are supported:

  • HashiCorp Vault KV2 secrets engine

  • OpenBao implementation of Vault

  • KMIP compatible servers

  • A local file storage. This storage is intended only for development and testing and is not recommended for production use.

For each key provider pg_tde requires a detailed configuration including the address of the service and the authentication information.

With these details pg_tde does the following based on user operations:

  • Uploads a new principal key to it after this key is created

  • Retrieves the principal key from the service when it is required for decryption

Retreival of the principal key is cached so it only happens when necessary.

F.62.9.2.5. Key provider management #

Key provider configuration or location may change. For example, a service is moved to a new address or the principal key must be moved to a different key provider type. pg_tde supports both these scenarios enabling you to manage principal keys using simple SQL functions.

In certain cases you can’t use SQL functions to manage key providers. For example, if the key provider changed while the server wasn’t running and is therefore unaware of these changes. The startup can fail if it needs to access the encryption keys.

For such situations, pg_tde also provides command line tools to recover the database.

F.62.9.2.6. Sensitive key provider information #

Caution

Authentication details for key providers are sensitive and must be protected.

Do not store these credentials in the $PGDATA directory alongside the database. Instead, ensure they are stored in a secure location with strict file system permissions to prevent unauthorized access.

F.62.9.3. User interface #

F.62.9.3.1. Setting up pg_tde #

To use pg_tde, users are required to:

  • Add pg_tde to the shared_preload_libraries in postgresql.conf

  • Execute CREATE EXTENSION pg_tde in the databases where they want to use encryption

  • Optionally, enable pg_tde.wal_encrypt in postgresql.conf

  • Optionally, disable pg_tde.inherit_global_providers in postgresql.conf (enabled by default)

F.62.9.3.2. Adding providers #

Keyring providers can be added to either the global or to the database specific scope.

If pg_tde.inherit_global_providers is on, global providers are visible for all databases, and can be used. If pg_tde.inherit_global_providers is off, global providers are only used for WAL encryption.

To add a global provider:

pg_tde_add_global_key_provider_<TYPE>('provider_name', ... details ...)

To add a database specific provider:

pg_tde_add_database_key_provider_<TYPE>('provider_name', ... details ...)
F.62.9.3.3. Changing providers #

To change a value of a global provider:

pg_tde_change_global_key_provider_<TYPE>('provider_name', ... details ...)

To change a value of a database specific provider:

pg_tde_change_database_key_provider_<TYPE>('provider_name', ... details ...)

These functions also allow changing the type of a provider.

The functions however do not migrate any data. They are expected to be used during infrastructure migration, for example when the address of a server changes.

Note that in these functions do not verify the parameters. For that, see pg_tde_verify_key.

F.62.9.3.4. Changing providers from the command line #

To change a provider from a command line, pg_tde provides the pg_tde_change_key_provider command line tool.

This tool work similarly to the above functions, with the following syntax:

pg_tde_change_key_provider <dbOid> <providerType> ... details ...

Note that since this tool is expected to be offline, it bypasses all permission checks!

This is also the reason why it requires a dbOid instead of a name, as it has no way to process the catalog and look up names.

F.62.9.3.5. Deleting providers #

Providers can be deleted by using the functions:

pg_tde_delete_database_key_provider(provider_name)
pg_tde_delete_global_key_provider(provider_name)

For database specific providers, the function first checks if the provider is used or not, and the provider is only deleted if it’s not used.

For global providers, the function checks if the provider is used anywhere, WAL or any specific database, and returns an error if it is.

This somewhat goes against the principle that pg_tde should not interact with other databases than the one the user is connected to, but on the other hand, it only does this lookup in the internal pg_tde metadata, not in postgres catalogs, so it is a gray zone. Making this check makes more sense than potentially making some databases inaccessible.

F.62.9.3.6. Listing/querying providers #

pg_tde provides 2 functions to show providers:

  • pg_tde_list_all_database_key_providers()

  • pg_tde_list_all_global_key_providers()

These functions return a list of provider names, type and configuration.

F.62.9.3.7. Provider permissions #

pg_tde implements access control based on execution rights on the administration functions.

For keys and providers administration, it provides two pair of functions:

pg_tde_(grant/revoke)_database_key_management_to_role
F.62.9.3.8. Creating and rotating keys #

Principal keys can be created or rotated using the following functions:

pg_tde_set_key_using_(global/database)_key_provider('key-name', 'provider-name', ensure_new_key)
pg_tde_set_server_key_using_(global/database)_key_provider('key-name', 'provider-name', ensure_new_key)
pg_tde_set_default_key_using_(global/database)_key_provider('key-name', 'provider-name', ensure_new_key)

ensure_new_key is a boolean parameter defaulting to false. If it is true the function might return an error instead of setting the key if it already exists on the provider.

F.62.9.3.9. Default principal key #

With pg_tde.inherit_global_key_providers, it is also possible to set up a default global principal key, which will be used by any database which has the pg_tde extension enabled, but doesn’t have a database specific principal key configured using pg_tde_set_key_using_(global/database)_key_provider.

With this feature, it is possible for the entire database server to easily use the same principal key for all databases, completely disabling multi-tenency.

A default key can be managed with the following function:

pg_tde_set_default_key('key-name', 'provider-name', ensure_new_key)

Changing the default principal key will rotate the encryption of internal keys for all databases using the current default principal key.

F.62.9.3.10. Current key details #

pg_tde_key_info() returns the name of the current principal key, and the provider it uses.

pg_tde_server_key_info() does the same for the server key.

pg_tde_default_key_info() does the same for the default key.

pg_tde_verify_key() checks that the key provider is accessible, that the current principal key can be downloaded from it, and that it is the same as the current key stored in memory - if any of these fail, it reports an appropriate error.

F.62.9.3.11. Key permissions #

Users with management permissions to a specific database (pg_tde_(grant/revoke)_(global/databse)_key_management_to_role) can change the keys for the database, and use the current key functions. This includes creating keys using global providers, if pg_tde.inherit_global_providers is enabled.

Also the pg_tde_(grant/revoke)_database_key_management_to_role function deals with only the specific permission for the above function: it allows a user to change the key for the database, but not to modify the provider configuration.

F.62.9.3.12. Creating encrypted tables #

To create an encrypted table or modify an existing table to be encrypted, simply use USING tde_heap in the CREATE statement.

F.62.9.3.13. Changing the pg_tde.inherit_global_keys setting #

It is possible for users to use pg_tde with inherit_global_keys = on, refer to global keys / keyrings in databases, and then change this setting to off.

In this case existing references to global providers, or the global default principal key will remain working as before, but new references to the global scope can’t be made.

F.62.9.4. Typical setup scenarios #

F.62.9.4.1. Simple one principal key encryption #
  1. Update configuration file: load extension and enable WAL encryption.

     shared_preload_libraries = 'pg_tde'
     pg_tde.wal_encrypt = on
    

  2. CREATE EXTENSION pg_tde; in template1

  3. Adding a global key provider

  4. Adding a default principal key using the same global provider

  5. Changing the WAL encryption to use the default principal key

  6. Optionally: setting the default_table_access_method to tde_heap so that tables are encrypted by default

Database users don’t need permissions to any of the encryption functions: encryption is managed by the admins, normal users only have to create tables with encryption, which requires no specific permissions.

F.62.9.4.2. One key storage, but different keys per database #
  1. Update configuration file: load extension and enable WAL encryption.

     shared_preload_libraries = 'pg_tde'
     pg_tde.wal_encrypt = on
    

  2. CREATE EXTENSION pg_tde; in template1

  3. Adding a global key provider

  4. Changing the WAL encryption to use the proper global key provider

  5. Giving users that are expected to manage database keys permissions for database specific key management, but not database specific key provider management: specific databases HAVE to use the global key provider

Note

Setting the default_table_access_method to tde_heap is possible, but instead of ALTER SYSTEM only per database using ALTER DATABASE, after a principal key is configured for that specific database.

Alternatively ALTER SYSTEM is possible, but table creation in the database will fail if there’s no principal key for the database, that has to be created first.

F.62.10. pg_tde CLI Tools #

The pg_tde extension introduces new command-line utilities and extends some existing Tantor SE tools to support encrypted WAL and tables. These include:

F.62.10.1. pg_tde_change_key_provider #

A tool for modifying the configuration of a key provider, possibly also changing its type.

This tool edits the configuration files directly, ignoring permissions or running postgres processes.

Its only intended use is to fix servers that can’t start up because of inaccessible key providers.

For example, you restore from an old backup and the address of the key provider changed in the meantime. You can use this tool to correct the configuration, allowing the server to start up.

Warning

Use this tool only when the server is offline. To modify the key provider configuration when the server is up and running, use the pg_tde_change_(global/database)_key_provider_<type> SQL functions.

F.62.10.1.1. Example usage #

To modify the key provider configuration, specify all parameters depending on the provider type in the same way as you do when using the pg_tde_change_(global/database)_key_provider_<type> SQL functions.

The general syntax is as follows:

pg_tde_change_key_provider [-D <datadir>] <dbOid> <provider_name> <new_provider_type> <provider_parameters...>
F.62.10.1.2. Parameter description #
  • [optional] <datadir> is the data directory.pg_tde uses the $PGDATA environment variable if this is not specified

  • <provider_name> is the name you assigned to the key provider

  • <new_provider_type> can be a file, vault or kmip

  • <dbOid>

Depending on the provider type, the additional parameters are:

pg_tde_change_key_provider [-D <datadir>] <dbOid> <provider_name> file <filename>
pg_tde_change_key_provider [-D <datadir>] <dbOid> <provider_name> vault <token_path> <url> <mount_path> [<ca_path>]
pg_tde_change_key_provider [-D <datadir>] <dbOid> <provider_name> kmip <host> <port> <cert_path> <key_path> [<ca_path>]

F.62.10.2. pg_waldump #

pg_waldump is a tool to display a human-readable rendering of the Write-Ahead Log (WAL) of a Tantor SE database cluster.

To read encrypted WAL records, pg_waldump supports the following additional arguments:

  • keyring_path: the directory where keyring configuration files for WAL are stored. These files include:

    • pg_tde.map

    • pg_tde.dat

    • pg_tde_keyrings

Note

pg_waldump will not decrypt WAL unless the keyring_path is set.

F.62.11. GUC Variables #

The pg_tde extension provides GUC variables to configure the behaviour of the extension:

F.62.11.1. pg_tde.wal_encrypt #

Type - boolean
Default - off

A boolean variable that controls if WAL writes are encrypted or not.

Changing this variable requires a server restart, and can only be set at the server level.

WAL encryption is controlled globally. If enabled, all WAL writes are encrypted in the entire Tantor SE cluster.

This variable only controls new writes to the WAL, it doesn’t affect existing WAL records.

pg_tde is always capable of reading existing encrypted WAL records, as long as the keys used for the encryption are still available.

Enabling WAL encryption requires a configured global principal key. Refer to the WAL encryption configuration documentation for more information.

F.62.11.2. pg_tde.enforce_encryption #

Type - boolean
Default - off

A boolean variable that controls if the creation of new, not encrypted tables is allowed.

If enabled, CREATE TABLE statements will fail unless they use the tde_heap access method.

Similarly, ALTER TABLE <x> SET ACCESS METHOD is only allowed, if the access method is tde_heap.

Other DDL operations are still allowed. For example other ALTER commands are allowed on unencrypted tables, as long as the access method isn’t changed.

You can set this variable at the following levels:

  • global - for the entire Tantor SE cluster.

  • database - for specific databases.

  • user - for specific users.

  • session - for the current session.

Setting or changing the value requires superuser permissions.

F.62.11.3. pg_tde.inherit_global_providers #

Type - boolean
Default - on

A boolean variable that controls if databases can use global key providers for storing principal keys.

If disabled, functions that change the key providers can only work with database local key providers.

In this case, the default principal key, if set, is also disabled.

You can set this variable at the following levels:

  • global - for the entire Tantor SE cluster.

  • database - for specific databases.

  • user - for specific users.

  • session - for the current session.

Setting this variable doesn’t affect existing uses of global keys. It only prevents the creation of new principal keys using global providers.

F.62.11.4. pg_tde.default_cipher_algorithm #

Type - string
Default - 'aes'

A string variable that specifies which encryption algorithm is used by default.

It is used when encryptor storage parameter is not used during CREATE TABLE command.

Another use case is changing encryption algorithm for already encrypted table. Set this setting to preferred algorithm and execute ALTER TABLE ... SET ACCESS METHOD tde_heap.

You can set this setting to the value of any supported algorithm:

  • aes - AES-CBC 256-bit.

  • chacha20 - ChaCha20.

  • magma - GOST 28147-89 Magma.

  • kuznyechik - GOST R 34.12-2015 Kuznyechik.

This parameter is local to user and can be changed without admin priviledges or restarting database.

F.62.11.5. pg_tde.preinit_algs #

Type - string
Default - 'aes,chacha20'

A string variable that stores a list of algorithm names separated by comma. These algorithms are loaded and initialized at the server startup.

If some algorithm is not avaiable, then ERROR is thrown and server will not start.

This serves 2 purposes:

  • Increase performance by elimination of neccessity for each backend to load and initialize algorithms themselves.

  • Make sure, that desired algorithms are available for use.

Best practice is to set this setting at start and do not use other algorithms. Otherwise, if desired algorithm is not supported you will have to update OpenSSL configuration and restart server. By using this setting you can be sure that algorithm is avaiable for future use.

F.62.11.6. pg_tde.openssl_config #

Type - string
Default - NULL

A string variable that stores a path to OpenSSL configuration file that used for OpenSSL initialization.

If no path is set, then default configuration file is used. On most Debian-based systems it is /etc/ssl/openssl.cnf.

Using this option you can set custom OpenSSL configuration and do not interfere with system-wide configuration file.

F.62.12. Functions #

The pg_tde extension provides functions for managing different aspects of its operation:

F.62.12.1. Permission management #

By default, pg_tde is restrictive. It doesn’t allow any operations until permissions are granted to the user. Only superusers can create or modify to key providers or modify objects in the global scope. Functions for viewing keys and for setting the principal key in a database local key provider can on the other hand be run by the database owner and be delegated to normal users using the GRANT EXECUTE and REVOKE EXECUTE commands.

The following functions are also provided for easier management of functionality groups:

F.62.12.1.1. Database local key management #

Use these functions to grant or revoke permissions to manage the key of the current database. They enable or disable all functions related to the key of the current database:

  • pg_tde_grant_database_key_management_to_role(role)

  • pg_tde_revoke_database_key_management_from_role(role)

F.62.12.1.2. Global scope key management #

Managment of the global scope is restricted to superusers only.

F.62.12.1.3. Inspections #

Use these functions to grant or revoke the use of query functions, which do not modify the encryption settings:

  • pg_tde_grant_key_viewer_to_role(role)

  • pg_tde_revoke_key_viewer_from_role(role)

F.62.12.2. Key provider management #

A key provider is a system or service responsible for managing encryption keys. pg_tde supports the following key providers:

  • local file (not for production use)

  • Hashicorp Vault / OpenBao

  • KMIP compatible providers

Key provider management includes the following operations:

  • creating a new key provider,

  • changing an existing key provider,

  • deleting a key provider,

  • listing key providers.

F.62.12.2.1. Add a provider #

You can add a new key provider using the provided functions, which are implemented for each provider type.

There are two functions to add a key provider: one function adds it for the current database and another one - for the global scope.

  • pg_tde_add_database_key_provider_<type>('provider-name', <provider specific parameters>)

  • pg_tde_add_global_key_provider_<type>('provider-name', <provider specific parameters>)

When you add a new provider, the provider name must be unqiue in the scope. But a local database provider and a global provider can have the same name.

F.62.12.2.2. Change an existing provider #

You can change an existing key provider using the provided functions, which are implemented for each provider type.

There are two functions to change existing providers: one to change a provider in the current database, and another one to change a provider in the global scope.

  • pg_tde_change_database_key_provider_<type>('provider-name', <provider specific parameters>)

  • pg_tde_change_global_key_provider_<type>('provider-name', <provider specific parameters>)

When you change a provider, the referred name must exist in the database local or a global scope.

The change functions require the same parameters as the add functions. They overwrite the setting for every parameter except for the name, which can’t be changed.

Provider specific parameters differ for each implementation. Refer to the respective subsection for details.

Some provider specific parameters contain sensitive information, such as passwords. Never specify these directly, use the remote configuration option instead.

F.62.12.2.2.1. Adding or modifying Vault providers #

The Vault provider connects to a HashiCorp Vault or an OpenBao server, and stores the keys on a key-value store version 2.

Use the following functions to add the Vault provider:

SELECT pg_tde_add_database_key_provider_vault_v2(
  'provider-name',
  'secret_token_path',
  'url','mount',
  'ca_path'
);

SELECT pg_tde_add_global_key_provider_vault_v2(
  'provider-name',
  'secret_token_path',
  'url','mount',
  'ca_path'
);

These functions change the Vault provider:

SELECT pg_tde_change_database_key_provider_vault_v2(
  'provider-name',
  'secret_token_path',
  'url',
  'mount',
  'ca_path'
);

SELECT pg_tde_change_global_key_provider_vault_v2(
  'provider-name',
  'secret_token_path',
  'url',
  'mount',
  'ca_path'
);

where:

  • provider-name is the name of the key provider

  • url is the URL of the Vault server

  • mount is the mount point on the Vault server where the key provider should store the keys

  • secret_token_path is a path to the file that contains an access token with read and write access to the above mount point

  • [optional] ca_path is the path of the CA file used for SSL verification

F.62.12.2.2.2. Adding or modifying KMIP providers #

The KMIP provider uses a remote KMIP server.

Use these functions to add a KMIP provider:

SELECT pg_tde_add_database_key_provider_kmip(
  'provider-name',
  'kmip-addr',
  'port',
  '/path_to/server_certificate.pem',
  '/path_to/client_cert.pem',
  '/path_to/client_key.pem'
);
SELECT pg_tde_add_global_key_provider_kmip(
  'provider-name',
  'kmip-addr',
  'port',
  '/path_to/server_certificate.pem',
  '/path_to/client_certificate.pem',
  '/path_to/client_key.pem'
);

These functions change the KMIP provider:

SELECT pg_tde_change_database_key_provider_kmip(
  'provider-name',
  'kmip-addr',
  'port',
  '/path_to/server_certificate.pem',
  '/path_to/client_cert.pem',
  '/path_to/client_key.pem'
);
SELECT pg_tde_change_global_key_provider_kmip(
  'provider-name',
  'kmip-addr',
  'port',
  '/path_to/server_certificate.pem',
  '/path_to/client_certificate.pem',
  '/path_to/client_key.pem'
);

where:

  • provider-name is the name of the provider

  • kmip-addr is the IP address or domain name of the KMIP server

  • port is the port to communicate with the KMIP server. Most KMIP servers use port 5696.

  • server-certificate is the path to the certificate file for the KMIP server.

  • client-certificate is the path to the client certificate.

  • client-key is the path to the client key.

Note

The specified access parameters require permission to read and write keys at the server.

F.62.12.2.3. Adding or modifying local keyfile providers #

This provider manages database keys using a local keyfile.

This function is intended for development or quick testing, and stores the keys unencrypted in the specified data file.

Caution

Local keyfile providers are not recommended for production environments, they lack the security and manageability of external key management systems.

Add a local keyfile provider:

SELECT pg_tde_add_database_key_provider_file(
  'provider-name',
  '/path/to/the/key/provider/data.file'
);

SELECT pg_tde_add_global_key_provider_file(
  'provider-name',
  '/path/to/the/key/provider/data.file'
);

Change a local keyfile provider:

SELECT pg_tde_change_database_key_provider_file(
  'provider-name',
  '/path/to/the/key/provider/data.file'
);

SELECT pg_tde_change_global_key_provider_file(
  'provider-name',
  '/path/to/the/key/provider/data.file'
);

where:

  • provider-name is the name of the provider. You can specify any name, it’s for you to identify the provider.

  • /path/to/the/key/provider/data.file is the path to the key provider file.

F.62.12.2.4. Delete a provider #

These functions delete an existing provider in the current database or in the global scope:

  • pg_tde_delete_database_key_provider('provider-name')

  • pg_tde_delete_global_key_provider('provider-name')

You can only delete key providers that are not currently in use. An error is returned if the current principal key is using the provider you are trying to delete.

If the use of global key providers is enabled via the pg_tde.inherit_global GUC, you can delete a global key provider only if it isn’t used anywhere, including any databases. If it is used in any database, an error is returned instead.

F.62.12.2.5. List key providers #

These functions list the details of all key providers for the current database or for the global scope, including all configuration values:

  • pg_tde_list_all_database_key_providers()

  • pg_tde_list_all_global_key_providers()

Caution

All configuration values include possibly sensitive values, such as passwords. Never specify these directly, use the remote configuration option instead.

F.62.12.3. Principal key management #

Use these functions to create a new principal key for a specific scope such as a current database, a global or default scope. You can also use them to start using a different existing key for a specific scope.

Princial keys are stored on key providers by the name specified in this function - for example, when using the Vault provider, after creating a key named foo, a key named foo will be visible on the Vault server at the specified mount point.

F.62.12.3.1. pg_tde_set_key_using_database_key_provider #

Creates or rotates the principal key for the current database using the specified database key provider and key name.

SELECT pg_tde_set_key_using_database_key_provider(
  'name-of-the-key',
  'provider-name',
  'ensure_new_key'
);

The ensure_new_key parameter instructs the function how to handle a principal key during key rotation:

  • If set to true (default), a new key must be unique. If the provider already stores a key by that name, the function returns an error.

  • If set to false, an existing principal key may be reused.

F.62.12.3.2. pg_tde_set_key_using_global_key_provider #

Creates or rotates the global principal key using the specified global key provider and the key name. This key is used for global settings like WAL encryption.

SELECT pg_tde_set_key_using_global_key_provider(
  'name-of-the-key',
  'provider-name',
  'ensure_new_key'
);

The ensure_new_key parameter instructs the function how to handle a principal key during key rotation:

  • If set to true, a new key must be unique. If the provider already stores a key by that name, the function returns an error.

  • If set to false (default), an existing principal key may be reused.

F.62.12.3.3. pg_tde_set_server_key_using_global_key_provider #

Creates or rotates the server principal key using the specified global key provider. Use this function to set a principal key for WAL encryption.

SELECT pg_tde_set_server_key_using_global_key_provider(
  'name-of-the-key',
  'provider-name',
  'ensure_new_key'
);

The ensure_new_key parameter instructs the function how to handle a principal key during key rotation:

  • If set to true, a new key must be unique. If the provider already stores a key by that name, the function returns an error.

  • If set to false (default), an existing principal key may be reused.

F.62.12.3.4. pg_tde_set_default_key_using_global_key_provider #

Creates or rotates the default principal key for the server using the specified global key provider.

The default key is automatically used as a principal key by any database that doesn’t have an individual key provider and key configuration.

SELECT pg_tde_set_default_key_using_global_key_provider(
  'name-of-the-key',
  'provider-name',
  'ensure_new_key'
);

The ensure_new_key parameter instructs the function how to handle a principal key during key rotation:

  • If set to true, a new key must be unique. If the provider already stores a key by that name, the function returns an error.

  • If set to false (default), an existing principal key may be reused.

F.62.12.4. Encryption status check #

F.62.12.4.1. pg_tde_is_encrypted #

Tells if a relation is encrypted using the pg_tde extension or not. Returns NULL if a relation lacks storage like views, foreign tables, and partitioned tables and indexes.

To verify that a table is encrypted, run the following statement:

SELECT pg_tde_is_encrypted(
  'table_name'
);

You can also verify if the table in a custom schema is encrypted. Pass the schema name for the function as follows:

SELECT pg_tde_is_encrypted(
  'schema.table_name'
);

This can additionally be used to verify that indexes and sequences are encrypted.

F.62.12.4.2. pg_tde_get_algorithm #

Tells which algorithm is used for relation encryption. Returns NULL if a relation is not encrypted.

To get encryption algorithm for table in current schema just run:

SELECT pg_tde_get_algorithm(
  'table_name'
);

This also works for relations in custom schema:

SELECT pg_tde_get_algorithm(
  'schema.table_name'
);

Note that encryption algorithm for indexes and TOASTs is equal to encryption algorithm that is used for parent relation.

F.62.12.4.3. pg_tde_key_info #

Displays information about the principal key for the current database, if it exists.

SELECT pg_tde_key_info();
F.62.12.4.4. pg_tde_server_key_info #

Displays information about the principal key for the server scope, if exists.

SELECT pg_tde_server_key_info();
F.62.12.4.5. pg_tde_default_key_info #

Displays the information about the default principal key, if it exists.

SELECT pg_tde_default_key_info();
F.62.12.4.6. pg_tde_verify_key #

This function checks that the current database has a properly functional encryption setup, which means:

  • A key provider is configured

  • The key provider is accessible using the specified configuration

  • There is a principal key for the database

  • The principal key can be retrieved from the remote key provider

  • The principal key returned from the key provider is the same as cached in the server memory

If any of the above checks fail, the function reports an error.

SELECT pg_tde_verify_key();
F.62.12.4.7. pg_tde_verify_server_key #

This function checks that the server scope has a properly functional encryption setup, which means:

  • A key provider is configured

  • The key provider is accessible using the specified configuration

  • There is a principal key for the global scope

  • The principal key can be retrieved from the remote key provider

  • The principal key returned from the key provider is the same as cached in the server memory

If any of the above checks fail, the function reports an error.

SELECT pg_tde_verify_server_key();
F.62.12.4.8. pg_tde_verify_default_key #

This function checks that the default key is properly configured, which means:

  • A key provider is configured

  • The key provider is accessible using the specified configuration

  • There is a principal key that can be used for any scope

  • The principal key can be retrieved from the remote key provider

  • The principal key returned from the key provider is the same as cached in the server memory

If any of the above checks fail, the function reports an error.

SELECT pg_tde_verify_default_key();

F.62.13. How to #

F.62.13.1. Remove Encryption from an Encrypted Table #

F.62.13.1.1. Method 1. Change the access method #

If you encrypted a table with the tde_heap access method and need to remove the encryption from it, run the following command against the desired table (mytable in the example below):

ALTER TABLE mytable SET ACCESS METHOD heap;

Note that the SET ACCESS METHOD command drops hint bits and this may affect performance. Running a plain SELECT count(*) or VACUUM command on the entire table will check every tuple for visibility and set its hint bits. Therefore, after executing the ALTER TABLE command, run a simple count(*) on your tables:

SELECT count(*) FROM mytable;

Check that the table is not encrypted:

SELECT pg_tde_is_encrypted('mytable');

The output returns f meaning that the table is no longer encrypted.

This also can be implemented using pg_tde_get_algorithm function. Non-NULL output means that table is encrypted:

SELECT pg_tde_get_algorithm('mytable');
F.62.13.1.2. Method 2. Create a new not encrypted table on the base of the encrypted one #

Alternatively, you can create a new not encrypted table with the same structure and data as the initial table. For example, the original encrypted table is EncryptedCustomers. Use the following command to create a new table Customers:

CREATE TABLE Customers AS
SELECT * FROM EncryptedCustomers;

The new table Customers inherits the structure and the data from EncryptedCustomers.

(Optional) If you no longer need the EncryptedCustomers table, you can delete it.

DROP TABLE EncryptedCustomers;

F.62.14. FAQ #

F.62.14.1. Why do I need TDE? #

Using TDE provides the following benefits:

  • Compliance to security and legal regulations like General Data Protection Regulation (GDPR), Payment Card Industry Data Security Standard (PCI DSS), Federal Law No. 420-FZ of 30.11.2024 and others.

  • Encryption of backups. Even when an authorized person gets physical access to a backup, encryption ensures that the data remains unreadable and secure.

  • Granular encryption of specific data sets and reducing the performance overhead that encryption brings.

  • Additional layer of security to existing security measures

F.62.14.2. When and how should I use TDE? #

If you are dealing with Personally Identifiable Information (PII), data encryption is crucial. Especially if you are involved in areas with strict regulations like:

  • financial services where TDE helps to comply with PCI DSS

  • healthcare and insurance - compliance with HIPAA, HITECH, CCPA

  • telecommunications, government and education to ensure data confidentiality.

Using TDE helps you avoid the following risks:

  • Data breaches

  • Identity theft that may lead to financial fraud and other crimes

  • Reputation damage leading to loss of customer trust and business

  • Legal consequences and financial losses for non-compliance with data protection regulations

  • Internal threats by misusing unencrypted sensitive data

If to translate sensitive data to files stored in your database, these are user data in tables, temporary files, WAL files. TDE has you covered encrypting all these files.

pg_tde does not encrypt system catalogs and temporary files. This means that statistics data and database metadata are not encrypted.

F.62.14.3. I use disk-level encryption. Why should I care about TDE? #

Encrypting a hard drive encrypts all data, including system, application, and temporary files.

Full disk encryption protects your data from people who have physical access to your device and even if it is lost or stolen. However, it doesn’t protect the data after system boot-up: the data is automatically decrypted when the system runs or when an authorized user requests it.

Another point to consider is PCI DSS compliance for Personal Account Numbers (PAN) encryption.

  • PCI DSS 3.4.1 standards might consider disk encryption sufficient for compliance if you meet these requirements:

    • Separate the logical data access from the operating system authentication.

    • Ensure the decryption key is not linked to user accounts.

    Note that PCI DSS 3.4.1 is retiring on March 31, 2025. Therefore, consider switching to PCI DSS 4.0.

  • PCI DSS 4.0 standards consider using only disk and partition-level encryption not enough to ensure PAN protection. It requires an additional layer of security that pg_tde can provide.

pg_tde focuses specifically on data files and offers more granular control over encrypted data. The data remains encrypted on disk during runtime and when you move it to another directory, another system or storage. An example of such data is backups. They remain encrypted when moved to the backup storage.

Thus, to protect your sensitive data, consider using TDE to encrypt it at the table level. Then use disk-level encryption to encrypt a specific volume where this data is stored, or the entire disk.

F.62.14.4. Is TDE enough to ensure data security? #

No. Transparent Data Encryption (TDE) adds an extra layer of security for data at rest. You should also consider implementing the following additional security features:

  • Access control and authentication

  • Strong network security like TLS

  • Disk encryption

  • Regular monitoring and auditing

  • Additional data protection for sensitive fields (e.g., application-layer encryption)

F.62.14.5. How does pg_tde make my data safe? #

pg_tde uses two keys to encrypt data:

  • Internal encryption keys to encrypt the data. These keys are stored internally in an encrypted format, in a single $PGDATA/pg_tde directory.

  • Principal keys to encrypt internal encryption keys. These keys are stored externally, in the Key Management System (KMS).

You can use the following KMSs:

  • HashiCorp Vault. pg_tde supports the KV secrets engine v2 of Vault.

  • OpenBao implementation of Vault

  • KMIP-compatible server. KMIP is a standardized protocol for handling cryptographic workloads and secrets management

HashiCorp Vault can also act as the KMIP server, managing cryptographic keys for clients that use the KMIP protocol.

Let’s break the encryption into two parts:

F.62.14.5.1. Encryption of data files #

Data files are encrypted with internal keys. Each file that relates to encrypted table has it's own internal key. For example, a table with 4 indexes will have 5 internal keys - one for the table and one for each index.

The initial decision on what file to encrypt is based on the table access method in Tantor SE. When you run a CREATE or ALTER TABLE statement with the USING tde_heap clause, the newly created data files are marked as encrypted, and then file operations encrypt or decrypt the data. Later, if an initial file is re-created as a result of a TRUNCATE or VACUUM FULL command, the newly created file inherits the encryption information and is either encrypted (i.e. for TRUNCATE) or not (i.e. ALTER TABLE SET ACCESS METHOD heap).

The principal key is used to encrypt the internal keys. It is stored in the key management store. When you query the table, the principal key is retrieved from the key store to decrypt the table. Then the internal key for that table is used to decrypt the data.

Principal key is retrieved from key management store only once, when it first needed, then it is cached in memory.

F.62.14.5.2. WAL encryption #

WAL encryption is done globally for the entire database cluster. All modifications to any database within a Tantor SE cluster are written to the same WAL to maintain data consistency and integrity and ensure that Tantor SE cluster can be restored to a consistent state. Therefore, WAL is encrypted globally.

When you turn on WAL encryption, pg_tde encrypts entire WAL files starting from the first WAL write after the server was started with the encryption turned on.

The same 2-key approach is used with WAL as with the table data: WAL pages are first encrypted with the internal key. Then the internal key is encrypted with the global principal key.

You can turn WAL encryption on and off so WAL can contain both encrypted and unencrypted data. The WAL encryption GUC variable influences only writes.

Whenever the WAL is being read (by the recovery process or tools), the decision on what should be decrypted is based solely on the metadata of WAL encryption keys.

F.62.14.6. Should I encrypt all my data? #

It depends on your business requirements and the sensitivity of your data. Encrypting all data is a good practice but it can have a performance impact.

Consider encrypting only tables that store sensitive data. You can decide what tables to encrypt and with what key.

We advise encrypting the whole database only if all your data is sensitive, like PII, or if there is no other way to comply with data safety requirements.

F.62.14.7. What cipher mechanisms are used by pg_tde? #

pg_tde can use 4 different encryption algorithms: AES-CBC-256, ChaCha20, Magma and Kuznyechik. First the internal keys in the datafile are encrypted using the principal key with AES-CBC-256, then the file data itself is again encrypted using one of above algorithms with the internal key.

For WAL encryption, AES-CTR-256 is used.

Note

OpenSSL is used for encryption/decryption, so the availability of algorithm support in database depends on its support in OpenSSL itself.

F.62.14.8. Is post-quantum encryption supported? #

No, it’s not yet supported. In our implementation we reply on OpenSSL libraries that don’t yet support post-quantum encryption. But all used encryption algorithms works only with 256-bit keys, so this allows to achieve sufficient level of security.

F.62.14.9. Can I encrypt an existing table? #

Yes, you can encrypt an existing table. Run the ALTER TABLE command as follows:

ALTER TABLE table_name SET ACCESS METHOD tde_heap;

Since the SET ACCESS METHOD command drops hint bits and this may affect the performance, we recommend to run the SELECT count(*) command. It checks every tuple for visibility and sets its hint bits. Read more in the Changing existing table section.

Note that ALTER TABLE SET ACCESS METHOD do not allow setting storage parameters in WITH clause, then you might want to change Section F.62.11.4 for custom encryption algorithm.

F.62.14.10. Do I have to restart the database to encrypt the data? #

You must restart the database in the following cases to apply the changes:

  • after you enabled the pg_tde extension

  • to turn on / off the WAL encryption

    to let the database make use of new algorithms in OpenSSL after you have made changes in it

After that, no database restart is required. When you create or alter the table using the tde_heap access method, the files are marked as those that require encryption.

F.62.14.11. What happens to my data if I lose a principal key? #

If you lose encryption keys, especially, the principal key, the data is lost. That’s why it’s critical to back up your encryption keys securely and use the Key Management service for key management.

F.62.14.12. Are my backups safe? Can I restore from them? #

pg_tde encrypts data at rest. This means that data is stored on disk in an encrypted form. During a backup, already encrypted data files are copied from disk onto the storage. This ensures the data safety in backups.

Since the encryption happens on the database level, it makes no difference for your tools and applications. They work with the data in the same way.

To restore from an encrypted backup, you must have the same principal encryption key, which was used to encrypt files in your backup. So try to hold them as long as possible in order to be able to recover from the worst case scenario when your backup is too old.

F.62.15. Uninstall pg_tde #

If you no longer wish to use TDE in your deployment, you can remove the pg_tde extension. To do so, your user must have the superuser privileges, or a database owner privileges in case you only want to remove it from a single database.

Here’s how to do it:

  1. Drop the extension using the DROP EXTENSION command:

    DROP EXTENSION pg_tde;
    

    This command will fail if there are still encrypted tables in the database.

    In this case, you must drop the dependent objects manually. Alternatively, you can run the DROP EXTENSION ... CASCADE command to drop all dependent objects automatically.

    Note that the DROP EXTENSION command does not delete the pg_tde data files related to the database.

  2. Run the DROP EXTENSION command against every database where you have enabled the pg_tde extension, if the goal is to completely remove the extension. This also includes the template databases, in case pg_tde was previously enabled there.

  3. Remove any reference to pg_tde GUC variables from the Tantor SE configuration file.

  4. Modify the shared_preload_libraries and remove the ‘pg_tde’ from it. Use the ALTER SYSTEM command for this purpose, or edit the configuration file.

    Warning

    Once pg_tde is removed from the shared_preload_libraries, reading any leftover encrypted files will fail. Removing the extension from the shared_preload_libraries is also possible if the extension is still installed in some databases.

    Make sure to do this only if the server has no encrypted files in its data directory.

  5. Start or restart the Tantor SE cluster to apply the changes.