Transparent Data Encryption (TDE) v1

Important

TDE is available only for operands that support it: EPAS versions 15 and newer.

Transparent Data Encryption, or TDE, is a technology used by several database vendors to encrypt data at rest, i.e. database files on disk. TDE does not however encrypt data in use.

TDE is included in EDB Postgres Advanced Server (EPAS), starting with version 15, and it is supported by the EDB Postgres for Kubernetes operator.

Important

Before you proceed, please take some time to familiarize with the TDE feature in the EPAS documentation.

With TDE activated, both WAL files and files for tables will be encrypted. Data encryption/decryption is entirely transparent to the user, as it is managed by the database without requiring any application changes or updated client drivers.

EDB Postgres for Kubernetes provides 3 ways to use TDE:

  • using a secret containing the passphrase
  • using a secret containing a custom passphrase command
  • using a pair of secrets containing custom wrap/unwrap commands

Passphrase secret

The basic approach is to store the passphrase in a Kubernetes secret. Such a passphrase will be used to encrypt the EPAS binary key.

EPAS documentation

Please refer to the EPAS documentation for details on the EPAS encryption key.

Activating TDE on the operator is simple. In the epas section of the manifest, use the tde stanza to enable TDE, and set the Kubernetes secret that will hold the TDE encryption key.

For example:

  []
  postgresql:
    epas:
      tde:
        enabled: true
        secretKeyRef:
          name: tde-key
          key: key

You can find an example in cluster-example-tde.yaml.

Note

This file also contains the definition of the secret to hold the encryption key. Look at the following section for an example on how to create a secret for this purpose.

The key stored in the secret will be used as the pass-phrase to invoke openssl to wrap/unwrap the EPAS encryption key.

How to create the secret containing the passphrase

First choose the passphrase. While it is recommended to use a randomly generated passphrase, in this example we will use PostgresRocks as passphrase, and rely on kubectl to generate for us the secret definition:

kubectl create secret generic -o yaml tde-key \
    --from-literal=key=PostgresRocks

This should return something like this:

apiVersion: v1
data:
  key: UG9zdGdyZXNSb2Nrcw==
kind: Secret
metadata:
  creationTimestamp: "YYYY-MM-DDTHH:MM:SSZ"
  name: tde-key
  namespace: default
  resourceVersion: ....
  uid: ....
type: Opaque

Remember to run kubectl apply or remove the -o yaml option to the create command above to actually create the secret in the cluster.

Custom passphrase command

Instead of the secretKeyRef in the cluster manifest snippet above, it is possible to specify a passphraseCommand stored in a secret. The passphrase command can be run to generate a passphrase to be used with openssl.

  []
  postgresql:
    epas:
      tde:
        enabled: true
        passphraseCommand:
          name: tde-passphrase
          key: command

The passphrase command should write to standard output. For example, we could simply use echo my-passphrase.

The passphrase generated by the command will be used the same way the secretKeyRef was used, i.e. as a passphrase argument for openssl.

Custom wrap/unwrap commands

It is also possible to specify the wrap and unwrap commands, rather than rely on the default invocation of openssl. This can be done by creating secrets containing the custom commands, and declaring those secrets in the tde stanza.

The snippet below shows a cluster with TDE enabled using custom commands.

  []
  postgresql:
    epas:
      tde:
        enabled: true
        wrapCommand:
          name: tde-wrap-command
          key: command
        unwrapCommand:
          name: tde-unwrap-command
          key: command

The custom commands need to obey the following conventions:

  1. The custom wrap command should accept input from standard input, which EPAS will use to feed it the binary key. It should write to a file via an explicit argument (not shell redirections). Moreover, the file argument should be given the string "%p", which is a placeholder EPAS will use to pass the file path of the new, wrapped encryption key file.

  2. The custom unwrap command should write to standard output. It should have an explicit file path argument for input (not shell redirections). Again, the file argument should be given the string "%p", which is the placeholder EPAS will fill in with the wrapped encryption key file path.

For example:

  • wrap command: openssl enc -aes-128-cbc -pass pass:temp-pass -e -out %p
  • unwrap command: openssl enc -aes-128-cbc -pass pass:temp-pass -d -in %p

Example using HashiCorp Vault

The following example shows how to use HashiCorp Vault to store the encryption key and use it to activate TDE. The vault CLI is used to interact with Vault and is included by default in the EDB Postgres Advanced Server (EPAS) image.

First, wherever you have vault running you must enable the Transit secrets engine and create a key:

vault secrets enable transit
vault write -f transit/keys/pg-tde

Then, create a secret containing the custom wrap/unwrap commands. The wrap and unwrap commands will 'wrap' a binary that is in the EPAS image. The binary will interact with the vault API to encrypt/decrypt the EPAS encryption.

The binary needs 4 flags: --host, --secret, --key and --vault-endpoint. The --host flag is in the format of http://vault-host:vault-port and needs to be provided to reach the Vault. The server--secret flag is the name of the Kubernetes secret that contains the vault token and the --key flag is the key in that secret pointing the vault token. The --vault-endpoint flag is the name of the key that was created inside vault; in the example above it is pg-tde.

If running the Vault operator in Kubernetes the root token can be obtained from the following two commands:

kubectl exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json
cat cluster-keys.json | jq -r ".root_token"
kubectl create secret generic -o yaml vault-token \
    --from-literal=wrap="/bin/vault wrap --file %p --host http://vault:8200 --secret vault-token --key token --vault-endpoint pg-tde" \
    --from-literal=unwrap="/bin/vault unwrap --file %p --host http://vault:8200 --secret vault-token --key token --vault-endpoint pg-tde" \
    --from-literal=token="hvs.whatever"

You can now create a Cluster that is referencing the secrets:

apiVersion: postgresql.k8s.enterprisedb.io/v1
kind: Cluster
metadata:
  name: hashicorp-vault-tde
spec:
  instances: 3
  storage:
    size: 1Gi
  postgresql:
    epas:
      tde:
        enabled: true
        wrapCommand:
          name: vault-token
          key: wrap
        unwrapCommand:
          name: vault-token
          key: unwrap