Ondat – Kubernetes Encryption at Rest

6/05/2021
demo

Overview

With the increased market use of Kubernetes as a cloud managed service and strict data compliance regulations that include the Payment Card Industry Standard (PCI DSS) and the Health Insurance Portability and Accountability Act (HIPPA), organisations are dependent on encrypted data as a requirement for Kubernetes workloads using persistent storage.

To accommodate these requirements, Ondat has further improved its core Encryption at Rest functionality in V2.4 with Encryption and Decryption handled by the CPU’s accelerated instruction set instruction set, providing accelerated data at rest for both encryption and decryption for AES-256 with AES-XTS encryption.

Ondat, a market leader in the field of cloud native persistent storage provides a Kubernetes native CSI interface, facilitating high performance, data availability and advanced storage features that historically were only available in hardware-based enterprise class storage arrays.

The following example demonstrates the use of Encryption at Rest in Kubernetes with Ondat –

1. Setup and Configuration

For this setup, we have a standard Kubernetes cluster that has StorageOS installed through the one-shot installation process

$ kubectl get nodes
NAME             STATUS   ROLES                      AGE     VERSION
storageos-k8s1   Ready    controlplane,etcd,worker   6d22h   v1.17.17
storageos-k8s2   Ready    controlplane,etcd,worker   6d22h   v1.17.17
storageos-k8s3   Ready    controlplane,etcd,worker   6d22h   v1.17.17

2. Setup of an Encrypted and Unencrypted Volume

We setup both an encrypted and unencrypted volume to show the variance in configuration and how data at rest applies to storage within the Kubernetes infrastructure –

Creating an Encrypted and UnEncrypted PVC/PV - kubectl apply -f- <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: encrypted
  labels:
    storageos.com/encryption: "true"
spec:
  storageClassName: fast
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: unencrypted
spec:
  storageClassName: fast
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
EOF
persistentvolumeclaim/encrypted created
persistentvolumeclaim/unencrypted created

StorageOS simplifies the setup of encrypted volumes with a single change required to the standard workflow.   The use of the encryption label as seen above in the setup of the first Persistent Volume Claim (PVC).

This simple change provides an encrypted volume along with the associated encryption keys required for encryption when using Ondat.

3. Viewing Encryption Keys

Viewing the volumes with a kubectl describe shows that the encrypted volume has additional annotations relating to the encryption keys used for encryption at rest –

🔍 Showing PVC Annotation - encrypted - kubectl describe pvc/encrypted
Name:          encrypted
Namespace:     default
StorageClass:  fast
Status:        Bound
Volume:        pvc-3fd15067-992e-4d93-b816-f686546025d1
Labels:        storageos.com/encryption=true
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
               storageos.com/encryption-secret-name: storageos-volume-key-2af02d26-4913-4a66-9f4a-0aae9589d8d4
               storageos.com/encryption-secret-namespace: default
               storageos.com/storageclass: 40579749-5ae8-44ae-8ae2-55a3858d4e15
               volume.beta.kubernetes.io/storage-provisioner: csi.storageos.com

Correspondingly, the Kubernetes Secret can be viewed to access the encryption key data specific for this volume-
🔍 Showing Encryption Key Secret for PVC encrypted - kubectl get secret storageos-volume-key-2af02d26-4913-4a66-9f4a-
0aae9589d8d4 -o yaml
apiVersion: v1
data:
  hmac: 9jqbnLGA9SfG+AhXzgZ9e49d+VrfJa3ki9C8eZOhubA=
  iv: JS0Cq1+0C/nnkL7plErbsZx8BHo28ies48sURXm4vAo=
  key: 8GkpcbBMQFAGdR4HeiYVsYa86NzFy56xLo/GgObIblt9ryZCGNRMulNv03+ckJP+tEGBEi1Qh1Dd6rokQhoYWw==
  vuk: XYZ2qitwuEFhaJPLTl3u9KSmsljNOod0+VO4FiLlWlzSuoFj1blZJOAZ27lhXz8x9PncX//5dJE3JkiO9VThSloSwh12uYWeKsCBlNbyXZ0=
kind: Secret
metadata:
creationTimestamp: "2021-04-30T10:03:35Z"
labels:
  app.kubernetes.io/component: storageos-api-manager
  app.kubernetes.io/managed-by: storageos-operator
  app.kubernetes.io/name: storageos
  app.kubernetes.io/part-of: storageos
  storageos.com/pvc: encrypted
 name: storageos-volume-key-2af02d26-4913-4a66-9f4a-0aae9589d8d4
 namespace: default
 resourceVersion: "15729433"
 selfLink: /api/v1/namespaces/default/secrets/storageos-volume-key-2af02d26-4913-4a66-9f4a-0aae9589d8d4
 uid: 4ae921d4-1823-4b79-9261-ca1e075fa3b0
type: Opaque

When using encryption with Ondat, encryption & decryption keys are fully under the control and management of the consumer.  The use of a native Kubernetes construct for key secret management provides the benefit of native integration with Kubernetes KMS providers, therefore providing further compliance and regulation as desired.

Encryption key access, management and availability is a differentiator between StorageOS and other persistent storage services offered by Cloud Providers as each volume has its own individual key whilst being under full control of the end-user.

4. Demonstrating Encryption at Rest with ‘The Great Gatsby’

To fully appreciate Kubernetes Encryption at Rest and the capabilities provided with StorageOS, we make use of ‘F. Scott Fitgerald’s’ famous book, the ‘The Great Gatsby’, a text widely considered as a literary masterpiece that was recreated as a Hollywood film.

The container image spurin/gatby:latest available on DockerHub (source available via GitHub), performs the task of downloading the text of the book to /data/gatsby.txt upon initialisation.

To demonstrate encryption at rest in Kubernetes, we use two StatefulSets, unencrypted and encrypted, each utilising the spurin/gatsby:latest image, with a persistent data volume mounted as /data.  When the container starts, the Great Gatsby text will be saved to the StorageOS persistent volumes.

Creating an UnEncrypted and Encrypted StatefulSet - kubectl apply -f- <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: unencrypted
spec:
  selector:
    matchLabels:
      app: unencrypted
  serviceName: unencrypted
  replicas: 1
  template:
    metadata:
      labels:
        app: unencrypted
    spec:
      volumes:
      - name: unencrypted
        persistentVolumeClaim:
          claimName: unencrypted
      containers:
        - name: unencrypted
          image: spurin/gatsby:latest
          imagePullPolicy: Always
          volumeMounts:
          - mountPath: "/data"
            name: unencrypted
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: encrypted
spec:
  selector:
    matchLabels:
      app: encrypted
  serviceName: encrypted
  replicas: 1
  template:
    metadata:
      labels:
        app: encrypted
    spec:
      volumes:
      - name: encrypted
        persistentVolumeClaim:
          claimName: encrypted
      containers:
        - name: encrypted
          image: spurin/gatsby:latest
          imagePullPolicy: Always
          volumeMounts:
          - mountPath: "/data"
            name: encrypted
---
EOF
statefulset.apps/unencrypted created
statefulset.apps/encrypted created

5. Verifying Data

From the Kubernetes consumption viewpoint, the use of Encryption at Rest is transparent and can be visualised by executing commands on the unencrypted and encrypted pods –

🔍 Checking Gatsby MD5 UnEncrypted - kubectl exec -it unencrypted-0 -- md5sum /data/gatsby.txt
c71f692044dbb4d89df2444ce8cccb35  /data/gatsby.txt

🔍 Checking Gatsby MD5 Encrypted - kubectl exec -it encrypted-0 -- md5sum /data/gatsby.txt
c71f692044dbb4d89df2444ce8cccb35  /data/gatsby.txt

🔍 Checking Gatsby Text UnEncrypted - kubectl exec -it unencrypted-0 -- grep -B1 'put together' /data/gatsby.txt
"They're a rotten crowd," I shouted across the lawn. "You're worth the
whole damn bunch put together."

🔍 Checking Gatsby Text Encrypted - kubectl exec -it encrypted-0 -- grep -B1 'put together' /data/gatsby.txt
"They're a rotten crowd," I shouted across the lawn. "You're worth the
whole damn bunch put together."

In both cases they return the same checksum for the data (c71f692044dbb4d89df2444ce8cccb35), they also both match a famous quote from the book.

To truly see the benefits of Encryption at Rest, we need to look behind the scenes.  For this, we’ll require access to the Kubernetes worker nodes where Ondat runs.

6.  Data at Rest on the Kubernetes Nodes

In this example our unencrypted data resides on storageos-k8s2 under /var/lib/storageos/data/dev1/vol.201427.*.blob (with these files, relating to the unencrypted /data filesystem).  If we check for the same text from the book using the unix strings command, we can see the following from a data at rest viewpoint –

root@storageos-k8s2:~# strings /var/lib/storageos/data/dev1/vol.201427.*.blob | grep -B1 -m 1 "put together"
"They're a rotten crowd," I shouted across the lawn. "You're worth the
whole damn bunch put together."

By accessing the Kubernetes worker node, we’re operating at an infrastructure level.  What could be deemed as sensitive information is both stored and easily accessible for those with access to the underlying components.  Should the Kubernetes node be terminated or the infrastructure recycled without due diligence there is a risk of data leakage.

If we were to execute the same command on the volume with encryption at rest (which resides under /var/lib/storageos/data/dev1/vol. 32831.*.blob), the equivalent data is inaccessible, thus safeguarding from data leakage –

root@storageos-k8s2:~# strings /var/lib/storageos/data/dev1/vol.32831.*.blob | grep -B1 -m 1 "put together"
root@storageos-k8s2:~#

Running strings against the entire datastore also demonstrates that encryption is in place –

root@storageos-k8s2:~ # strings /var/lib/storageos/data/dev1/vol.32831.*.blob | head -20
M:d~
p;d|
5SB\9 4_B
T1Ot
#lN]8KX
4Rxc
@'{-
b,'I
p92`
(,^M
ya    Z=
>w` _
^TTA
T^ Y
fdPG
:N]j
Q&Zn
0v/ ]
SnYu
Hg}

Conclusion

For many organisations, data encryption is the barrier that prevents both the use of Kubernetes and/or Cloud adoption for stateful applications.  Ondat solves the problem both of encrypted data, and stateful storage.  

written by:
James Spurin