qemu: add support for librbd layered encryption

This commit enables libvirt users to use layered encryption
of RBD images, using the librbd encryption engine.
This allows opening of an encrypted cloned image
whose parent is encrypted with a possibly different encryption key.
To open such images, multiple encryption secrets are expected
to be defined under the encryption XML tag.

Signed-off-by: Or Ozeri <oro@il.ibm.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
Or Ozeri 2023-03-13 04:50:21 -05:00 committed by Peter Krempa
parent 1992ae40fa
commit 77c9663d72
11 changed files with 178 additions and 9 deletions

View File

@ -28,7 +28,10 @@ network disks. If the engine tag is not specified, the ``qemu`` engine will be
used by default (assuming the qemu driver is used). Note that ``librbd`` engine
is currently only supported by the qemu VM driver, and is not supported by the
storage driver. Furthermore, the storage driver currently ignores the ``engine``
tag.
tag. :since:`since 9.3.0` RBD layered encryption is supported. Layered
encryption requires a secret per each encrypted layer. The first secret
corresponds to the (child) image itself, the second secret to the parent image,
and so forth.
The ``encryption`` tag can currently contain a sequence of ``secret`` tags, each
with mandatory attributes ``type`` and either ``uuid`` or ``usage`` (
@ -55,7 +58,8 @@ added to libvirt.
The ``luks`` format is specific to a luks encrypted volume and the secret is
used in order to either encrypt during volume creation or decrypt the volume for
usage by the domain. A single ``<secret type='passphrase'...>`` element is
expected. :since:`Since 2.1.0` .
expected (except for the case of RBD layered encryption mentioned above).
:since:`Since 2.1.0` .
For volume creation, it is possible to specify the encryption algorithm used to
encrypt the luks volume. The following two optional elements may be provided for
@ -102,7 +106,8 @@ can only be applied to RBD network disks (RBD images). Since the ``librbd``
engine is currently not supported by the libvirt storage driver, you cannot use
it to control such disks. However, pre-formatted RBD luks2 disks can be loaded
to a qemu VM using the qemu VM driver. A single
``<secret type='passphrase'...>`` element is expected.
``<secret type='passphrase'...>`` element is expected (except for the case of
RBD layered encryption mentioned above).
Examples
--------

View File

@ -26,7 +26,9 @@
</optional>
<optional>
<interleave>
<oneOrMore>
<ref name="secret"/>
</oneOrMore>
<optional>
<interleave>
<element name="cipher">

View File

@ -564,6 +564,8 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
if (src->encryption &&
src->encryption->engine == VIR_STORAGE_ENCRYPTION_ENGINE_LIBRBD) {
size_t i;
switch ((virStorageEncryptionFormatType) src->encryption->format) {
case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS:
encformat = "luks";
@ -580,11 +582,19 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
break;
}
if (virJSONValueObjectAdd(&encrypt,
for (i = src->encryption->nsecrets; i > 0; --i) {
g_autoptr(virJSONValue) new = NULL;
/* we consume the lower layer 'encrypt' into a new object */
if (virJSONValueObjectAdd(&new,
"s:format", encformat,
"s:key-secret", srcPriv->encinfo[0]->alias,
"s:key-secret", srcPriv->encinfo[i-1]->alias,
"A:parent", &encrypt,
NULL) < 0)
return NULL;
encrypt = g_steal_pointer(&new);
}
}
if (virJSONValueObjectAdd(&ret,

View File

@ -5198,6 +5198,12 @@ qemuDomainValidateStorageSource(virStorageSource *src,
return -1;
}
if (src->encryption->nsecrets > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("qemu encryption engine expects only a single secret"));
return -1;
}
break;
case VIR_STORAGE_ENCRYPTION_ENGINE_LIBRBD:
@ -5213,6 +5219,14 @@ qemuDomainValidateStorageSource(virStorageSource *src,
_("librbd encryption is supported only with RBD backed disks"));
return -1;
}
if (src->encryption->nsecrets > 1) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_RBD_ENCRYPTION_LAYERING)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("librbd encryption layering is not supported by this QEMU binary"));
return -1;
}
}
break;
case VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT:

View File

@ -3346,6 +3346,14 @@ qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk,
return -1;
}
if (disk->bus == VIR_DOMAIN_DISK_BUS_SD &&
disk->src && disk->src->encryption && disk->src->encryption->nsecrets > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("sd card '%s' does not support multiple encryption secrets"),
disk->dst);
return -1;
}
if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_BLK)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",

View File

@ -0,0 +1 @@
unsupported configuration: librbd encryption layering is not supported by this QEMU binary

View File

@ -0,0 +1,39 @@
LC_ALL=C \
PATH=/bin \
HOME=/var/lib/libvirt/qemu/domain--1-encryptdisk \
USER=test \
LOGNAME=test \
XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-encryptdisk/.local/share \
XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-encryptdisk/.cache \
XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-encryptdisk/.config \
/usr/bin/qemu-system-x86_64 \
-name guest=encryptdisk,debug-threads=on \
-S \
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-encryptdisk/master-key.aes"}' \
-machine pc-i440fx-2.1,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
-accel tcg \
-cpu qemu64 \
-m 1024 \
-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":1073741824}' \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-object '{"qom-type":"secret","id":"libvirt-1-format-encryption-secret0","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
-object '{"qom-type":"secret","id":"libvirt-1-format-encryption-secret1","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
-object '{"qom-type":"secret","id":"libvirt-1-format-encryption-secret2","data":"9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1","keyid":"masterKey0","iv":"AAECAwQFBgcICQoLDA0ODw==","format":"base64"}' \
-blockdev '{"driver":"rbd","pool":"pool","image":"image","server":[{"host":"mon1.example.org","port":"6321"},{"host":"mon2.example.org","port":"6322"},{"host":"mon3.example.org","port":"6322"}],"encrypt":{"format":"luks","key-secret":"libvirt-1-format-encryption-secret0","parent":{"format":"luks","key-secret":"libvirt-1-format-encryption-secret1","parent":{"format":"luks","key-secret":"libvirt-1-format-encryption-secret2"}}},"node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1}' \
-audiodev '{"id":"audio1","driver":"none"}' \
-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x3"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on

View File

@ -0,0 +1,41 @@
<domain type='qemu'>
<name>encryptdisk</name>
<uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>524288</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type>
<boot dev='hd'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='network' device='disk'>
<driver name='qemu' type='raw'/>
<source protocol='rbd' name='pool/image'>
<host name='mon1.example.org' port='6321'/>
<host name='mon2.example.org' port='6322'/>
<host name='mon3.example.org' port='6322'/>
<encryption format='luks' engine='librbd'>
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80fb0'/>
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
<secret type='passphrase' uuid='f52a81b2-424e-490c-823d-6bd4235bc572'/>
</encryption>
</source>
<target dev='vda' bus='virtio'/>
</disk>
<controller type='usb' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</memballoon>
</devices>
</domain>

View File

@ -1249,6 +1249,8 @@ mymain(void)
DO_TEST_CAPS_LATEST("disk-network-rbd");
DO_TEST_CAPS_VER_PARSE_ERROR("disk-network-rbd-encryption", "6.0.0");
DO_TEST_CAPS_LATEST("disk-network-rbd-encryption");
DO_TEST_CAPS_VER_PARSE_ERROR("disk-network-rbd-encryption-layering", "7.2.0");
DO_TEST_CAPS_LATEST("disk-network-rbd-encryption-layering");
DO_TEST_CAPS_LATEST_PARSE_ERROR("disk-encryption-wrong");
DO_TEST_CAPS_LATEST("disk-network-rbd-no-colon");
/* qemu-6.0 is the last qemu version supporting sheepdog */

View File

@ -0,0 +1,46 @@
<domain type='qemu'>
<name>encryptdisk</name>
<uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>524288</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type>
<boot dev='hd'/>
</os>
<cpu mode='custom' match='exact' check='none'>
<model fallback='forbid'>qemu64</model>
</cpu>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='network' device='disk'>
<driver name='qemu' type='raw'/>
<source protocol='rbd' name='pool/image'>
<host name='mon1.example.org' port='6321'/>
<host name='mon2.example.org' port='6322'/>
<host name='mon3.example.org' port='6322'/>
<encryption format='luks' engine='librbd'>
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80fb0'/>
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
<secret type='passphrase' uuid='f52a81b2-424e-490c-823d-6bd4235bc572'/>
</encryption>
</source>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</disk>
<controller type='usb' index='0' model='piix3-uhci'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<audio id='1' type='none'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</memballoon>
</devices>
</domain>

View File

@ -325,6 +325,7 @@ mymain(void)
DO_TEST_NOCAPS("disk-network-gluster");
DO_TEST_NOCAPS("disk-network-rbd");
DO_TEST_CAPS_LATEST("disk-network-rbd-encryption");
DO_TEST_CAPS_LATEST("disk-network-rbd-encryption-layering");
DO_TEST_NOCAPS("disk-network-source-auth");
DO_TEST_NOCAPS("disk-network-sheepdog");
DO_TEST_NOCAPS("disk-network-vxhs");