[PATCH v2 5/7] qemu: add support for librbd layered encryption

Or Ozeri oro at il.ibm.com
Mon Mar 13 09:50:21 UTC 2023


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 at il.ibm.com>
---
 docs/formatstorageencryption.rst              | 11 +++--
 src/conf/schemas/storagecommon.rng            |  4 +-
 src/qemu/qemu_block.c                         | 20 ++++++--
 src/qemu/qemu_domain.c                        | 14 ++++++
 src/qemu/qemu_validate.c                      |  8 ++++
 ...k-rbd-encryption-layering.x86_64-7.2.0.err |  1 +
 ...rbd-encryption-layering.x86_64-latest.args | 39 ++++++++++++++++
 .../disk-network-rbd-encryption-layering.xml  | 41 +++++++++++++++++
 tests/qemuxml2argvtest.c                      |  2 +
 ...-rbd-encryption-layering.x86_64-latest.xml | 46 +++++++++++++++++++
 tests/qemuxml2xmltest.c                       |  1 +
 11 files changed, 178 insertions(+), 9 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-7.2.0.err
 create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args
 create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml
 create mode 100644 tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml

diff --git a/docs/formatstorageencryption.rst b/docs/formatstorageencryption.rst
index 2c19473d6b..3b3e9ea379 100644
--- a/docs/formatstorageencryption.rst
+++ b/docs/formatstorageencryption.rst
@@ -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
 --------
diff --git a/src/conf/schemas/storagecommon.rng b/src/conf/schemas/storagecommon.rng
index 23eff9ecb1..225456f03c 100644
--- a/src/conf/schemas/storagecommon.rng
+++ b/src/conf/schemas/storagecommon.rng
@@ -26,7 +26,9 @@
       </optional>
       <optional>
         <interleave>
-          <ref name="secret"/>
+          <oneOrMore>
+            <ref name="secret"/>
+          </oneOrMore>
           <optional>
             <interleave>
               <element name="cipher">
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 0cc3b82cca..d50cfa20c5 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -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,
-                                  "s:format", encformat,
-                                  "s:key-secret", srcPriv->encinfo[0]->alias,
-                                  NULL) < 0)
-            return NULL;
+        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[i-1]->alias,
+                                      "A:parent", &encrypt,
+                                      NULL) < 0)
+                return NULL;
+
+            encrypt = g_steal_pointer(&new);
+        }
     }
 
     if (virJSONValueObjectAdd(&ret,
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 638788c614..726d0ab0a1 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -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:
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index 0146220d8f..345e3eef45 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -3351,6 +3351,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",
diff --git a/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-7.2.0.err b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-7.2.0.err
new file mode 100644
index 0000000000..73e5b2a1f3
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-7.2.0.err
@@ -0,0 +1 @@
+unsupported configuration: librbd encryption layering is not supported by this QEMU binary
diff --git a/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args
new file mode 100644
index 0000000000..c9e1a0cc72
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args
@@ -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
diff --git a/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml
new file mode 100644
index 0000000000..8c2c008dc3
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml
@@ -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>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index c879fa90e0..94e88f7eab 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1247,6 +1247,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 */
diff --git a/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml
new file mode 100644
index 0000000000..e3a9463e43
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml
@@ -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>
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index a1be43ab34..1e62fbdd19 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -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");
-- 
2.25.1



More information about the libvir-list mailing list