Encryption

Encryption is enabled on a per volume basis using the storageos.com/encryption label (for more information see Encrypted Volumes).

StorageOS encrypts volumes on disk using AES-256 in XTS-AES mode with 512 bit keys as recommended by NIST, with encryption keys being derived using HKDF.

Keys and Initialisation vectors are generated using the crypto/rand package.

Key Size Usage
Volume 512 bits random data Used by XTS-AES to encrypt and decrypt disk blocks
Namespace 256 bits random data Used to encrypt Volume keys
Initialisation Vector 256 bits random data Used as ‘salt’ when encrypting volume keys with namespace keys

The components required to derive the encryption key are stored in a Kubernetes secret. By default these secrets are stored in the namespace that StorageOS is installed into. As Kubernetes secrets are only base64 encoded, it is recommend to encrypt secrets at rest. Alternatively a KMS such as HashiCorp Vault can be used.

Each namespace has its own key that is created when the namespace is initialized. The namespace keys are stored as Kubernetes secrets named ns-key.{Namespace Name}. The namespace key secrets are created in whatever namespace StorageOS is installed into.

In the example below there are two ns-key secrets in the storageos namespace because a StorageOS volume has been provisioned in the default and mongo namespaces. A ns-key is created for a namespace regardless of whether an encrypted volume exists in the namespace or not.

$ kubectl get secrets -n kube-system | grep ns-key
ns-key.default                                 Opaque                                1      4d
ns-key.mongo                                   Opaque                                1      5h

A volume is encrypted with a volume key that is a randomly generated 512 bit key. Rather than storing the volume key, StorageOS stores an encrypted version of the volume key, called the volume user key, which is generated by encrypting the volume key with a 256 bit namespace key and 256 bit initialization vector. Each namespace has a unique key and a unique initialization vector is generated for each volume.

The volume key is discarded to avoid the key that encrypts user data being compromised. Whenever the volume needs to be decrypted the volume key is derived by decrypting the volume user key using the namespace key and initialisation vector that are stored.

In order to check that the volume key has been correctly derived, a key digest of the volume key is stored to verify the derived volume key is identical to the original key.

Ultimately this means that the volume user key, initialisation vector, a digest of the volume key and the namespace key are stored in a Kubernetes secret. See Encrypted Volumes for best practices regarding backing up StorageOS secrets.