Encrypt data at rest with LUKS
Many security standards, ISO 27001 among them, require that data at rest is encrypted. On Syself Autopilot you meet that on the hardware you own: you encrypt the disks in your bare metal servers, and only you hold the key that can read them.
Encryption at rest stores the data on a disk as ciphertext. Without the key it is just noise, worthless to anyone who ends up with the physical disk. Hetzner already controls who can enter its data centers and wipes every disk before it is reused, so this is not about a disk walking out the door. It is a control you own and can point to in an audit, and it holds even once a disk leaves your hands.
You do this with LUKS, the standard way Linux encrypts a disk. You do not need to be a storage expert. You encrypt each data disk once, and from then on it unlocks automatically every time the server starts.
Warning
You hold the key, and only you. If you lose it, the data on those disks is gone for good. There is no recovery, and nothing Syself can do to bring it back. Before you rely on this, keep a backup of anything you cannot lose on a separate system, and try the full encrypt-and-unlock cycle once on a test server before you use it in production.
Disk encryption as a built-in Syself Autopilot feature is planned for a future release. Until then you set it up yourself with the steps below, and the Syself Consulting team can design it with you, set it up, and operate it as a full service.
How it works, in short#
There are two steps, and only the second one keeps happening.
- You encrypt each disk once. You run a couple of commands to turn a data disk into an encrypted disk. You do this a single time per disk, when the server joins.
- A DaemonSet you run unlocks it at startup. Each time a server starts, a small privileged DaemonSet on it finds the encrypted disks and opens them with your key, before your storage comes up. Nobody types a password at a console, and your apps use the disk exactly as before.
The encrypting and the unlocking are simple and the same on every server. The one thing you decide and control is the key.
Encryption sits at the very bottom, directly on the physical disk. On top of the unlocked disk you build the storage that TopoLVM hands out as local volumes. TopoLVM never sees the encryption. It works with the disk as usual, and everything written to it is already encrypted underneath. Because encryption is the bottom layer, it protects every volume at once.
Picture it as layers, from the disk at the bottom up to your app:
- The physical disk holds a locked container. This is the LUKS part. Locked, it is unreadable.
- Unlocking it opens a readable version of the disk at
/dev/mapper/encrypted-nvme. - LVM builds its volume group and thin pool on that unlocked version, not on the raw disk.
- TopoLVM serves
local-nvme,local-ssd, andlocal-hddvolumes to your pods from it.
A pod that uses a local volume sees none of this and works exactly as before.
The key is the part you own#
Everything else is reliable and the same on every server. The one decision that is yours is the key: where it comes from, and how far you trust the path that brings it to a server at boot. The recommendation is to bring your own key and manage it yourself, so control stays with you.
You also choose how many keys to run. A LUKS disk holds its own passphrase, so the granularity is yours: one key for the whole cluster, a key per server, or a key per disk, whatever your policy asks. More keys means a single leaked key unlocks less, at the cost of more keys to look after. To keep things simple, start with one key for the whole cluster and all its disks, and split it finer later if your security policy asks for it.
Important
The servers have no secure chip (a TPM) to hold the key, so the key arrives on the server as plain text at the moment it unlocks the disk. Your encryption is therefore only as safe as the way you store and deliver the key. Keep the key in a store you control, limit who can read it, and change it from time to time. Getting the key path right is the real work here; the disk commands are the easy part.
The DaemonSet reads the key from a file that comes from a Kubernetes Secret. It does not care how the key got there, so any delivery you trust works.
A plain Secret is fine only for a first test:
apiVersion: v1
kind: Secret
metadata:
name: disk-encryption-key
namespace: kube-system
type: Opaque
stringData:
key: "your-passphrase-here"
For real use, do not keep the passphrase in a plain Secret. Keep the key of record in a system you own and audit, your own key manager (KMS) or Vault, and copy it into this Secret automatically with the External Secrets Operator, or bring it to the server through another mechanism you trust. The real key stays with you, and the cluster only ever holds the copy it needs to unlock the disk.
A LUKS disk can hold several keys at once, so you can change a key without taking anything offline. Find the disk by its hardware id, then add a new passphrase, update your key store, check the new one works, and remove the old one:
$ ls -l /dev/disk/by-id/ | grep eui
$ DISK=/dev/disk/by-id/nvme-eui.0025388b01b5c3e8
$ cryptsetup luksAddKey "$DISK"
$ cryptsetup luksOpen --test-passphrase "$DISK"
$ cryptsetup luksRemoveKey "$DISK"
If you would rather not build the key handling yourself, the Syself Consulting team can design it with you and set the whole thing up.
Encrypt a disk once#
Do this once per data disk, when you first set the server up, in place of the plain disk-preparation step. First open a shell on the server, with a privileged debug pod (kubectl debug node/<node-name> -it --image=busybox --profile=sysadmin -- chroot /host) or the Hetzner rescue system. Then run these. Adjust the disk name and volume group to match the disk type, the same way the TopoLVM setup does.
Lock the disk with a key #
Find the data disk's stable id first. Kernel names like /dev/nvme1n1 can swap between disks when a node is reprovisioned, so address the disk by its hardware id instead:
$ ls -l /dev/disk/by-id/ | grep eui
Each id is a symlink to a kernel name. Check those names against lsblk: the OS disk is the one carrying / and /boot, and it must never be encrypted. Take the id of the other disk.
Turn that disk into an encrypted container, protected by a passphrase you choose:
$ cryptsetup luksFormat /dev/disk/by-id/nvme-eui.0025388b01b5c3e8
Caution
This erases everything on the disk, so make sure it is a data disk and never the OS disk. Save the passphrase somewhere safe that you will still have after the server is gone. Without it, the data cannot be recovered.
Unlock it #
Open the encrypted disk into a new device you can build storage on:
$ cryptsetup luksOpen /dev/disk/by-id/nvme-eui.0025388b01b5c3e8 encrypted-nvme
Build the storage on the unlocked device #
Set up LVM on the unlocked device, /dev/mapper/encrypted-nvme, not the raw disk. This is the point of the whole exercise: build on the unlocked device and everything underneath stays encrypted; build on the raw disk and the data is written in the clear.
$ pvcreate /dev/mapper/encrypted-nvme
$ vgcreate vg-nvme /dev/mapper/encrypted-nvme
$ lvcreate --thinpool pool-nvme --extents 100%FREE vg-nvme
Unlock the disks at startup#
After a restart the disk is locked again, so something on the server has to unlock it before your storage can use it. The way to automate this is a small privileged DaemonSet that you run on your bare metal nodes: at startup it finds the encrypted disks and opens them with your key, before TopoLVM comes up. You build and run this yourself; you deploy it once and point it at the key, and it behaves the same on every server. If you would rather not, the Syself Consulting team can set it up for you.
Run it as a privileged DaemonSet on each bare metal node. It scans the disks for LUKS containers and runs cryptsetup open on each one into a /dev/mapper/encrypted-<disk> device, so the unlocked devices are back before TopoLVM serves volumes. Have it check whether a disk is already open, so a restart is safe to repeat, and no one is needed at the console.
The node's operating system does the rest. It ships cryptsetup and the dm_crypt kernel module, and it activates a volume group the moment its device appears, a plain disk or an unlocked encrypted disk alike. So once a disk is unlocked, its volume group comes up on its own, lvmd finds it, and TopoLVM mounts volumes into pods exactly as it does for an unencrypted disk. There is nothing extra to mount.
Cloud volumes are not encrypted for you#
Cloud volumes, the standard class, are not encrypted by default, and Hetzner offers no switch to turn it on. The CSI driver can do it instead, when you give a StorageClass a Secret holding an encryption-passphrase. It then puts LUKS on the volume the first time it mounts it and opens it on every mount after that:
apiVersion: v1
kind: Secret
metadata:
name: encryption-secret
namespace: kube-system
type: Opaque
stringData:
encryption-passphrase: "your-passphrase-here"
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard-encrypted
provisioner: csi.hetzner.cloud
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
csi.storage.k8s.io/node-publish-secret-name: encryption-secret
csi.storage.k8s.io/node-publish-secret-namespace: kube-system
Claims on standard-encrypted get a LUKS layer you hold the key to; claims on standard do not. There is no need for a DaemonSet and nothing to unlock at boot, because the driver opens the volume as part of mounting it. A cloud server has no secure chip either, so the passphrase still arrives as plain text, and everything above about where the key comes from applies here too.
What it costs#
Scrambling and unscrambling the data uses some CPU on every read and write. Modern processors do this in hardware, so the cost is usually small, but it is not zero. On a disk that is already the busy part of a database, measure with and without encryption on your own hardware so you know the real effect.
Related: Set up local NVMe with TopoLVM for the storage this sits under, Encrypt data disks for the threat this closes and the key-custody model, and Tune storage performance if the extra CPU starts to show.