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

Or Ozeri oro at il.ibm.com
Mon Mar 6 12:53:12 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                         | 23 +++++++---
 src/qemu/qemu_domain.c                        | 14 ++++++
 ...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  | 40 +++++++++++++++++
 tests/qemuxml2argvtest.c                      |  2 +
 ...-rbd-encryption-layering.x86_64-latest.xml | 45 +++++++++++++++++++
 tests/qemuxml2xmltest.c                       |  1 +
 10 files changed, 169 insertions(+), 11 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 4d6e646c9a..ff24ae9548 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 f6d21d2040..e0f355874f 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -543,7 +543,8 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
     qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src);
     g_autoptr(virJSONValue) servers = NULL;
     virJSONValue *ret = NULL;
-    g_autoptr(virJSONValue) encrypt = NULL;
+    g_autolist(virJSONValue) encrypts = NULL;
+    virJSONValue *null_encrypt = NULL;
     const char *encformat = NULL;
     const char *username = NULL;
     g_autoptr(virJSONValue) authmodes = NULL;
@@ -563,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";
@@ -579,11 +582,17 @@ 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) {
+            virJSONValue *encrypt = NULL;
+            if (virJSONValueObjectAdd(&encrypt,
+                                      "s:format", encformat,
+                                      "s:key-secret", srcPriv->encinfo[i-1]->alias,
+                                      "A:parent", encrypts ? (virJSONValue **)&encrypts->data : &null_encrypt,
+                                      NULL) < 0)
+                return NULL;
+
+            encrypts = g_list_prepend(encrypts, encrypt);
+        }
     }
 
     if (virJSONValueObjectAdd(&ret,
@@ -592,7 +601,7 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
                               "S:snapshot", src->snapshot,
                               "S:conf", src->configFile,
                               "A:server", &servers,
-                              "A:encrypt", &encrypt,
+                              "A:encrypt", encrypts ? (virJSONValue **)&encrypts->data : &null_encrypt,
                               "S:user", username,
                               "A:auth-client-required", &authmodes,
                               "S:key-secret", keysecret,
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index ffe29dc832..e336273588 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -5195,6 +5195,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:
@@ -5210,6 +5216,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/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..c260eda5e8
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args
@@ -0,0 +1,39 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-encryptdisk \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-encryptdisk/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-encryptdisk/.cache \
+XDG_CONFIG_HOME=/tmp/lib/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":"/tmp/lib/domain--1-encryptdisk/master-key.aes"}' \
+-machine pc-i440fx-2.1,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-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 \
+-no-acpi \
+-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"}' \
+-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"}},"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..cbde665958
--- /dev/null
+++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml
@@ -0,0 +1,40 @@
+<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'/>
+        </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 f46fc29f32..08c912f588 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -1277,6 +1277,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..03c07c2527
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml
@@ -0,0 +1,45 @@
+<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'/>
+        </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 69bff80376..95a2261b7d 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -329,6 +329,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