How to Encrypt KVM Virtual Machine Disks with LUKS and Ceph RBD
Learn step‑by‑step how to secure KVM virtual machine disks using LUKS encryption, convert qcow2 images, define libvirt secrets, and apply encryption to both local and Ceph RBD storage, including necessary XML configurations, command‑line tools, and best‑practice considerations.
KVM qcow2 Disk Encryption
About LUKS Encryption
LUKS implements a platform‑independent standard disk format for various tools. It encrypts block devices, allowing any filesystem—including swap partitions—to be encrypted. An unencrypted header stores up to 8 (LUKS1) or 32 (LUKS2) key slots, password types, and key sizes. The header distinguishes LUKS from plain dm‑crypt because it enables multiple passphrases and easy key changes. If the header is lost or damaged, the device becomes undecryptable. LUKS provides a standard disk‑encryption format that works across Linux distributions, supports multi‑user/passphrase, and allows password changes without re‑encrypting the whole disk.
Implementing LUKS Encryption for KVM Virtual Machine Disks
Before libvirt 4.5, qcow encryption was also supported. However, qcow encryption has been deprecated since QEMU 2.3.
<code>qemu-img convert -O qcow2 --object secret,id=sec0,data=123456 -o encryption=on,encrypt.key-secret=sec0 -f qcow2 test.qcow2 fw-encry.qcow2</code>The default encryption format and qcow are no longer used for creating encrypted volumes. Below is the main method using LUKS to encrypt an existing VM disk. If you create a new VM with an encrypted disk, the conversion step is unnecessary.
Create a 20G empty LUKS disk.
<code>qemu-img create -f luks --object secret,data=123,id=sec0 -o key-secret=sec0 fw.luks 20G</code>Convert the target qcow2 disk to a LUKS disk (the target must exist).
<code>qemu-img convert --target-image-opts \
--object secret,data=123,id=sec0 -f qcow2 fw.qcow2 -n \
driver=luks,file.filename=fw.luks,key-secret=sec0</code>Create a secret XML file (UUID will be generated automatically if omitted).
<code>vim volume-secret.xml
<secret ephemeral='no' private='yes'>
<uuid>92f35b9e-c845-47e1-b5a6-f2036a706866</uuid>
<description>Super secret name of my first puppy</description>
<usage type='volume'>
<volume>/home/kvm/images/fw.luks</volume>
</usage>
</secret>
</code>Define the secret.
<code>virsh secret-define volume-secret.xml</code>Set the secret's password value.
<code># MYSECRET=`printf %s "123" | base64`
# virsh secret-set-value dc057b2b-6a7d-4dba-b76e-37a458448765 $MYSECRET
Secret value set</code>Edit the VM's
<disk>element to reference the secret.
<code><disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/home/kvm/images/fw.luks'/>
<target dev='vda' bus='virtio'/>
<encryption format='luks'>
<secret type='passphrase' uuid='dc057b2b-6a7d-4dba-b76e-37a458448765'/>
</encryption>
<address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
</disk></code>For volume creation, you can specify the cipher and IV generator for the LUKS volume. The default algorithm used by
qemu-imgis
aes-256-cbcwith
essivIV generation and
sha256hashing.
<code><volume>
<name>twofish.luks</name>
<capacity unit='G'>5</capacity>
<target>
<path>/var/lib/libvirt/images/demo.luks</path>
<format type='raw'/>
<encryption format='luks'>
<secret type='passphrase' uuid='f52a81b2-424e-490c-823d-6bd4235bc572'/>
<cipher name='twofish' size='256' mode='cbc' hash='sha256'/>
<ivgen name='plain64' hash='sha256'/>
</encryption>
</target>
</volume></code>Start the VM after the configuration:
<code>virsh start vm</code>If you copy the encrypted disk to another host, repeat the secret‑definition steps on the new host.
Note: The password set for the secret must match the one defined on the host.
KVM Using Ceph RBD Disk Encryption
Define Secret
Generate a secret on each Ceph node.
<code>cd /etc/libvirt/qemu
cat > secret.xml <<EOF
<secret ephemeral='no' private='no'>
<usage type='ceph'>
<name>client.libvirt secret</name>
</usage>
</secret>
EOF</code>Define the secret and note its UUID.
<code>virsh secret-define --file secret.xml
# Example output: Secret 01a0ba00-f277-48bb-b937-9001ec91f53e created</code>Obtain the Ceph client key and save it.
<code>ceph auth get-key client.libvirt | tee client.libvirt.key</code>Set the secret's password using the base64‑encoded key.
<code>virsh secret-set-value --secret 01a0ba00-f277-48bb-b937-9001ec91f53e --base64 $(cat client.libvirt.key)
Secret value set</code>Edit the VM XML to use the Ceph RBD disk with the secret.
<code><devices>
<disk type='network' device='disk'>
<driver name='qemu' type='raw'/>
<auth username='libvirt'>
<secret type='ceph' uuid='01a0ba00-f277-48bb-b937-9001ec91f53e'/>
</auth>
<source protocol='rbd' name='libvirt-pool/fw-system'>
<host name='node1' port='6789'/>
<host name='node2' port='6789'/>
<host name='node3' port='6789'/>
</source>
<target dev='vdb' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
</devices></code>Define and start the VM.
<code>virsh define vm.xml
virsh start vm</code>After encryption, the disk cannot be mounted directly; it must be mapped via
cryptsetup luksOpenbefore use.
<code>cryptsetup luksOpen /dev/vdb fw</code>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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.