Information Security 10 min read

How to Encrypt Ceph RBD Images with LUKS2 for Secure VM Storage

This guide explains why and how to use LUKS2 encryption on Ceph RBD images, covering background requirements, LUKS versions, formatting images, defining secrets, updating libvirt XML, launching VMs, and the limitations when cloning encrypted images.

Ops Development Stories
Ops Development Stories
Ops Development Stories
How to Encrypt Ceph RBD Images with LUKS2 for Secure VM Storage

Background

Many industries, such as finance, increasingly require tenant‑provided encryption keys for per‑volume data protection, driven by regulations and security concerns. Ceph RBD does not natively support this, so an encryption layer (e.g., QEMU LUKS or DM‑Crypt) is added before libRBD. Placing encryption at the libRBD level enables flexible use of Ceph RBD cloning.

About LUKS encryption

LUKS has two versions: LUKS2 (with header‑damage resilience, default Argon2, 4 KB block size) and LUKS1 (PBKDF2, 512‑byte block size). libvirt supports both formats, but QEMU 4.5 dropped qcow disk encryption. Encryption tags can specify the engine (qemu or librbd); librbd requires QEMU ≥ 6.1.0 and Ceph ≥ 16.1.0 and works only with RBD network disks. Only LUKS2 supports the librbd engine.

Our test environment uses Ubuntu 20.04 with QEMU 4.2 and libvirt 6.0, which only supports LUKS1. Upgrading to QEMU ≥ 6.1 and libvirt ≥ 7.9 (available via source compilation) is needed for LUKS2; Ubuntu 22.04 provides libvirt 8.0 and QEMU 6.2, which we use for verification.

<code># virsh version
Compiled against library: libvirt 8.0.0
Using library: libvirt 8.0.0
Using API: QEMU 8.0.0
Running hypervisor: QEMU 6.2.0
</code>

Libvirt added support for the librbd encryption engine starting with version 7.9 (released 2021‑11‑01).

Encryption format

By default RBD images are unencrypted. To encrypt, format the image as LUKS1 or LUKS2, which writes encryption metadata (format, version, cipher, key‑slot information) into the image.

Only formatting is supported; cloning an encrypted image reuses the same format and passphrase. Data written before formatting becomes unreadable. Currently only AES‑128 and AES‑256 are supported, with XTS‑plain64 as the sole mode.

Existing non‑RBD LUKS images can be imported.

Using LUKS2 format to encrypt an RBD image

The following steps demonstrate encrypting a Ceph RBD image with LUKS2.

Format the image

<code>rbd encryption format {pool-name}/{image-name} {luks1|luks2} {passphrase-file} [--cipher-alg {aes-128 | aes-256}]
# Example
rbd encryption format libvirt-pool/288 luks2 passphrase-file
cat passphrase-file
123456
</code>

Formatting creates a LUKS header at the image start, adds a key slot protected by the passphrase, and defaults to AES‑256 in XTS‑plain64 mode (AES‑128 can be selected).

Define a secret

<code># vim secret.xml
<secret ephemeral='no' private='yes'>
  <uuid>548dff81-a251-4702-90c0-0fc7d0c7754e</uuid>
</secret>
# virsh secret-define secret.xml
Secret 548dff81-a251-4702-90c0-0fc7d0c7754e created
</code>

Set the secret value (password)

<code>virsh secret-set-value 548dff81-a251-4702-90c0-0fc7d0c7754e --interactive
# Enter the same passphrase used for formatting
Enter new value for secret:
Secret value set
</code>

Edit the VM’s disk XML to add encryption

<code>&lt;disk type='network' device='disk'&gt;
  &lt;driver name='qemu' type='raw'/&gt;
  &lt;auth username='libvirt'&gt;
    &lt;secret type='ceph' uuid='0b7e7ce4-15bb-48a9-8106-c9ec1d7681a0'/&gt;
  &lt;/auth&gt;
  &lt;source protocol='rbd' name='libvirt-pool/288'&gt;
    &lt;host name='node1' port='6789'/&gt;
    &lt;host name='node2' port='6789'/&gt;
    &lt;host name='node3' port='6789'/&gt;
  &lt;/source&gt;
  &lt;target dev='vdc' bus='virtio'/&gt;
  &lt;encryption format='luks2' engine='librbd'&gt;
    &lt;secret type='passphrase' uuid='548dff81-a251-4702-90c0-0fc7d0c7754e'/&gt;
  &lt;/encryption&gt;
  &lt;address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/&gt;
&lt;/disk&gt;
</code>

Start the virtual machine

<code>virsh start vm
</code>

Clone the encrypted RBD image and mount

<code># rbd snap create libvirt-pool/288@snapshot_61
Creating snap: 100% complete...done.
# rbd snap protect libvirt-pool/288@snapshot_61
# rbd clone libvirt-pool/288@snapshot_61 libvirt-pool/388
# rbd info libvirt-pool/388
rbd image '388':
  size 100 GiB in 25600 objects
  order 22 (4 MiB objects)
  ...
  parent: libvirt-pool/288@snapshot_61
  overlap: 100 GiB
</code>

When the encryption element is removed from the cloned image’s XML, the image cannot be mounted because the clone inherits the encryption metadata.

Attempting to format the cloned image fails with an “operation not supported” error, confirming that LUKS encryption cannot be applied to a cloned RBD image.

<code># rbd encryption format libvirt-pool/388 luks2 passphrase-file
rbd: encryption format error: 2022-06-01T06:39:32.691+0000 7fc65aba7340 -1 librbd::api::Image: encryption_format: cannot format a cloned image (95) Operation not supported
</code>
EncryptionCephqemulibvirtRBDLUKS2
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.