Encrypting Postgres Data at Rest in Kubernetes

Jonathan S. Katz

4 min read

Encrypting data at rest is often an important compliance task when working on securing your database system. While there are a lot of elements that go into securing a PostgreSQL database, encrypting data at rest helps to protect your data from various offline attacks including the stealing of a disk or tampering. Disk encryption is a popular feature among public database-as-a-service providers, including Crunchy Bridge, to protect data in a multi-tenant environment.

There is ongoing work in the PostgreSQL community to natively support transparent data encryption (TDE), which lets you control encryption at rest from Postgres. While there are options such as Crunchy Hardened PostgreSQL that offer TDE solutions, you can still encrypt your PostgreSQL data at rest today by doing so at the disk level. In a Kubernetes environment, this is done by using a storage class that supports encryption.

Let's look at an example of how we can encrypt Postgres data at rest using PGO, the open source Postgres Operator from Crunchy Data

Creating Encrypted Volumes

For this example, we are going to use AWS EBS volumes to store our data on EKS. AWS EBS volumes contain a type called gp2 which provide SSD volumes as the underlying storage.

In the case of the gp2 volumes, we need to set up a Kubernetes StorageClass that specifies for them to be encrypted. You can do this with the following manifest:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp2-enc
provisioner: kubernetes.io/aws-ebs
parameters:
  encrypted: 'true'
  fsType: ext4
  type: gp2
volumeBindingMode: WaitForFirstConsumer

Because storage classes are cluster-wide objects, you will need to ensure you have appropriate permissions in your Kubernetes cluster to create a new StorageClass.

As an added bonus, let's set the encrypted storage class to be the default storage class. You will first have to unset the current default storage class, which is likely a gp2 StorageClass:

kubectl patch storageclass gp2 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

You can then set the gp2-enc StorageClass to become the default:

kubectl patch storageclass gp2-enc -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

With the ability to provision encrypted volumes in place, let's look at how to create encrypted PostgreSQL volumes with PGO!

Encrypting Postgres Data at Rest

Using the Postgres Operator, if you set the gp2-enc StorageClass to be the default class, creating a Postgres cluster with encrypted volumes is as simple as using the following manifest:

apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: keycloakdb
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.0-0
  postgresVersion: 14
  instances:
    - dataVolumeClaimSpec:
        accessModes:
          - 'ReadWriteOnce'
        resources:
          requests:
            storage: 1Gi
  backups:
    pgbackrest:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.35-0
      repos:
        - name: repo1
          volume:
            volumeClaimSpec:
              accessModes:
                - 'ReadWriteOnce'
              resources:
                requests:
                  storage: 1Gi

However, if you want to specify the gp2-enc storage class, you would do the following:

apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: keycloakdb
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.0-0
  postgresVersion: 14
  instances:
    - dataVolumeClaimSpec:
        storageClassName: gp2-enc
        accessModes:
          - 'ReadWriteOnce'
        resources:
          requests:
            storage: 1Gi
  backups:
    pgbackrest:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.35-0
      repos:
        - name: repo1
          volume:
            volumeClaimSpec:
              storageClassName: gp2-enc
              accessModes:
                - 'ReadWriteOnce'
              resources:
                requests:
                  storage: 1Gi

In other words, you need to add the storageClassName to each volume claim specification.

Notice that we encrypt both the PostgreSQL data directory and the pgBackRest volume. Why do we encrypt the backup volume as well? If you are encrypting data at rest, you need to encrypt it on each volume you store any transformation of your data on, otherwise you create a potential vector for someone to access your unencrypted data!

Next Steps

Kubernetes offers a variety of storage classes, many of which support transparent data encryption. If you are using hostpath or NFS storage, you can encrypt your volumes using a variety of disk utilities.

There are a variety of ways to encrypt data at rest. Disk level encryption is a proven and efficient method and allows you to focus on other aspects of your applications!

(Interested in seeing PGO in action? Join us for a webinar on Wednesday, Nov 17th.)

Avatar for Jonathan S. Katz

Written by

Jonathan S. Katz

October 25, 2021 More by this author