[PATCH v3] block/rbd: Add support for layered encryption

Or Ozeri posted 1 patch 1 year, 5 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20221115122527.2896476-1-oro@il.ibm.com
Maintainers: Ilya Dryomov <idryomov@gmail.com>, Peter Lieven <pl@kamp.de>, Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>, Eric Blake <eblake@redhat.com>, Markus Armbruster <armbru@redhat.com>
There is a newer version of this series
block/rbd.c          | 119 ++++++++++++++++++++++++++++++++++++++++++-
qapi/block-core.json |  35 +++++++++++--
2 files changed, 150 insertions(+), 4 deletions(-)
[PATCH v3] block/rbd: Add support for layered encryption
Posted by Or Ozeri 1 year, 5 months ago
Starting from ceph Reef, RBD has built-in support for layered encryption,
where each ancestor image (in a cloned image setting) can be possibly
encrypted using a unique passphrase.

A new function, rbd_encryption_load2, was added to librbd API.
This new function supports an array of passphrases (via "spec" structs).

This commit extends the qemu rbd driver API to use this new librbd API,
in order to support this new layered encryption feature.

Signed-off-by: Or Ozeri <oro@il.ibm.com>
---
v3: further nit fixes suggested by @idryomov
v2: nit fixes suggested by @idryomov
---
 block/rbd.c          | 119 ++++++++++++++++++++++++++++++++++++++++++-
 qapi/block-core.json |  35 +++++++++++--
 2 files changed, 150 insertions(+), 4 deletions(-)

diff --git a/block/rbd.c b/block/rbd.c
index f826410f40..ce017c29b5 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -71,6 +71,16 @@ static const char rbd_luks2_header_verification[
     'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2
 };
 
+static const char rbd_layered_luks_header_verification[
+        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
+    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 1
+};
+
+static const char rbd_layered_luks2_header_verification[
+        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
+    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 2
+};
+
 typedef enum {
     RBD_AIO_READ,
     RBD_AIO_WRITE,
@@ -470,6 +480,9 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
     size_t passphrase_len;
     rbd_encryption_luks1_format_options_t luks_opts;
     rbd_encryption_luks2_format_options_t luks2_opts;
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
+    rbd_encryption_luks_format_options_t luks_any_opts;
+#endif
     rbd_encryption_format_t format;
     rbd_encryption_options_t opts;
     size_t opts_size;
@@ -505,6 +518,23 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
             luks2_opts.passphrase_size = passphrase_len;
             break;
         }
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
+        case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: {
+            memset(&luks_any_opts, 0, sizeof(luks_any_opts));
+            format = RBD_ENCRYPTION_FORMAT_LUKS;
+            opts = &luks_any_opts;
+            opts_size = sizeof(luks_any_opts);
+            r = qemu_rbd_convert_luks_options(
+                    qapi_RbdEncryptionOptionsLUKSAny_base(&encrypt->u.luks_any),
+                    &passphrase, &passphrase_len, errp);
+            if (r < 0) {
+                return r;
+            }
+            luks_any_opts.passphrase = passphrase;
+            luks_any_opts.passphrase_size = passphrase_len;
+            break;
+        }
+#endif
         default: {
             r = -ENOTSUP;
             error_setg_errno(
@@ -522,6 +552,74 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
 
     return 0;
 }
+
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
+static int qemu_rbd_encryption_load2(rbd_image_t image,
+                                     RbdEncryptionOptions *encrypt,
+                                     Error **errp)
+{
+    int r = 0;
+    int encrypt_count = 1;
+    int i;
+    RbdEncryptionOptions *curr_encrypt;
+    rbd_encryption_spec_t *specs;
+    rbd_encryption_luks_format_options_t* luks_any_opts;
+
+    /* count encryption options */
+    for (curr_encrypt = encrypt; curr_encrypt->has_parent;
+         curr_encrypt = curr_encrypt->parent) {
+        ++encrypt_count;
+    }
+
+    specs = g_new0(rbd_encryption_spec_t, encrypt_count);
+
+    curr_encrypt = encrypt;
+    for (i = 0; i < encrypt_count; ++i) {
+        if (curr_encrypt->format != RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY) {
+            r = -ENOTSUP;
+            error_setg_errno(
+                    errp, -r, "unknown image encryption format: %u",
+                    curr_encrypt->format);
+            goto exit;
+        }
+
+        specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS;
+        specs[i].opts_size = sizeof(rbd_encryption_luks_format_options_t);
+
+        luks_any_opts = g_new0(rbd_encryption_luks_format_options_t, 1);
+        specs[i].opts = luks_any_opts;
+
+        r = qemu_rbd_convert_luks_options(
+                qapi_RbdEncryptionOptionsLUKSAny_base(
+                        &curr_encrypt->u.luks_any),
+                (char**)&luks_any_opts->passphrase,
+                &luks_any_opts->passphrase_size,
+                errp);
+        if (r < 0) {
+            goto exit;
+        }
+
+        curr_encrypt = curr_encrypt->parent;
+    }
+
+    r = rbd_encryption_load2(image, specs, encrypt_count);
+    if (r < 0) {
+        error_setg_errno(errp, -r, "layered encryption load fail");
+        goto exit;
+    }
+
+exit:
+    for (i = 0; i < encrypt_count; ++i) {
+        luks_any_opts = specs[i].opts;
+        if (luks_any_opts) {
+            g_free((char*)luks_any_opts->passphrase);
+            g_free(luks_any_opts);
+        }
+    }
+    g_free(specs);
+    return r;
+}
+#endif
 #endif
 
 /* FIXME Deprecate and remove keypairs or make it available in QMP. */
@@ -993,7 +1091,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
 
     if (opts->has_encrypt) {
 #ifdef LIBRBD_SUPPORTS_ENCRYPTION
-        r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
+        if (opts->encrypt->has_parent) {
+#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
+            r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp);
+#else
+            r = -ENOTSUP;
+            error_setg(errp, "RBD library does not support layered encryption");
+#endif
+        } else {
+            r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
+        }
         if (r < 0) {
             goto failed_post_open;
         }
@@ -1284,6 +1391,16 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
         spec_info->u.rbd.data->encryption_format =
                 RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
         spec_info->u.rbd.data->has_encryption_format = true;
+    } else if (memcmp(buf, rbd_layered_luks_header_verification,
+               RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
+        spec_info->u.rbd.data->encryption_format =
+                RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_LAYERED;
+        spec_info->u.rbd.data->has_encryption_format = true;
+    } else if (memcmp(buf, rbd_layered_luks2_header_verification,
+               RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
+        spec_info->u.rbd.data->encryption_format =
+                RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2_LAYERED;
+        spec_info->u.rbd.data->has_encryption_format = true;
     } else {
         spec_info->u.rbd.data->has_encryption_format = false;
     }
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 882b266532..15715d990e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3753,10 +3753,20 @@
 ##
 # @RbdImageEncryptionFormat:
 #
+# luks
+#
+# luks2
+#
+# luks-any: Used for opening either luks or luks2. (Since 7.2)
+#
+# luks-layered: Layered encryption. Only used for info. (Since 7.2)
+#
+# luks2-layered: Layered encryption. Only used for info. (Since 7.2)
+#
 # Since: 6.1
 ##
 { 'enum': 'RbdImageEncryptionFormat',
-  'data': [ 'luks', 'luks2' ] }
+  'data': [ 'luks', 'luks2', 'luks-any', 'luks-layered', 'luks2-layered' ] }
 
 ##
 # @RbdEncryptionOptionsLUKSBase:
@@ -3798,6 +3808,15 @@
   'base': 'RbdEncryptionOptionsLUKSBase',
   'data': { } }
 
+##
+# @RbdEncryptionOptionsLUKSAny:
+#
+# Since: 7.2
+##
+{ 'struct': 'RbdEncryptionOptionsLUKSAny',
+  'base': 'RbdEncryptionOptionsLUKSBase',
+  'data': { } }
+
 ##
 # @RbdEncryptionCreateOptionsLUKS:
 #
@@ -3819,13 +3838,23 @@
 ##
 # @RbdEncryptionOptions:
 #
+# @format: Encryption format.
+#
+# @parent: Parent image encryption options (for cloned images).
+#          Can be left unspecified if this cloned image is encrypted
+#          using the same format and secret as its parent image (i.e.
+#          not explicitly formatted) or if its parent image is not
+#          encrypted. (Since 7.2)
+#
 # Since: 6.1
 ##
 { 'union': 'RbdEncryptionOptions',
-  'base': { 'format': 'RbdImageEncryptionFormat' },
+  'base': { 'format': 'RbdImageEncryptionFormat',
+            '*parent': 'RbdEncryptionOptions' },
   'discriminator': 'format',
   'data': { 'luks': 'RbdEncryptionOptionsLUKS',
-            'luks2': 'RbdEncryptionOptionsLUKS2' } }
+            'luks2': 'RbdEncryptionOptionsLUKS2',
+            'luks-any': 'RbdEncryptionOptionsLUKSAny'} }
 
 ##
 # @RbdEncryptionCreateOptions:
-- 
2.25.1
Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Daniel P. Berrangé 1 year, 5 months ago
On Tue, Nov 15, 2022 at 06:25:27AM -0600, Or Ozeri wrote:
> Starting from ceph Reef, RBD has built-in support for layered encryption,
> where each ancestor image (in a cloned image setting) can be possibly
> encrypted using a unique passphrase.
> 
> A new function, rbd_encryption_load2, was added to librbd API.
> This new function supports an array of passphrases (via "spec" structs).
> 
> This commit extends the qemu rbd driver API to use this new librbd API,
> in order to support this new layered encryption feature.
> 
> Signed-off-by: Or Ozeri <oro@il.ibm.com>
> ---
> v3: further nit fixes suggested by @idryomov
> v2: nit fixes suggested by @idryomov
> ---
>  block/rbd.c          | 119 ++++++++++++++++++++++++++++++++++++++++++-
>  qapi/block-core.json |  35 +++++++++++--
>  2 files changed, 150 insertions(+), 4 deletions(-)
> 
> diff --git a/block/rbd.c b/block/rbd.c
> index f826410f40..ce017c29b5 100644
> --- a/block/rbd.c
> +++ b/block/rbd.c
> @@ -71,6 +71,16 @@ static const char rbd_luks2_header_verification[
>      'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2
>  };
>  
> +static const char rbd_layered_luks_header_verification[
> +        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
> +    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 1
> +};
> +
> +static const char rbd_layered_luks2_header_verification[
> +        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
> +    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 2
> +};
> +
>  typedef enum {
>      RBD_AIO_READ,
>      RBD_AIO_WRITE,
> @@ -470,6 +480,9 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>      size_t passphrase_len;
>      rbd_encryption_luks1_format_options_t luks_opts;
>      rbd_encryption_luks2_format_options_t luks2_opts;
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +    rbd_encryption_luks_format_options_t luks_any_opts;
> +#endif
>      rbd_encryption_format_t format;
>      rbd_encryption_options_t opts;
>      size_t opts_size;
> @@ -505,6 +518,23 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>              luks2_opts.passphrase_size = passphrase_len;
>              break;
>          }
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +        case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: {
> +            memset(&luks_any_opts, 0, sizeof(luks_any_opts));
> +            format = RBD_ENCRYPTION_FORMAT_LUKS;
> +            opts = &luks_any_opts;
> +            opts_size = sizeof(luks_any_opts);
> +            r = qemu_rbd_convert_luks_options(
> +                    qapi_RbdEncryptionOptionsLUKSAny_base(&encrypt->u.luks_any),
> +                    &passphrase, &passphrase_len, errp);
> +            if (r < 0) {
> +                return r;
> +            }
> +            luks_any_opts.passphrase = passphrase;
> +            luks_any_opts.passphrase_size = passphrase_len;
> +            break;
> +        }
> +#endif

This looks unrelated to support of multiple layers, unless I'm missing
something.

>          default: {
>              r = -ENOTSUP;
>              error_setg_errno(
> @@ -522,6 +552,74 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>  
>      return 0;
>  }
> +
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +static int qemu_rbd_encryption_load2(rbd_image_t image,
> +                                     RbdEncryptionOptions *encrypt,
> +                                     Error **errp)
> +{
> +    int r = 0;
> +    int encrypt_count = 1;
> +    int i;
> +    RbdEncryptionOptions *curr_encrypt;
> +    rbd_encryption_spec_t *specs;
> +    rbd_encryption_luks_format_options_t* luks_any_opts;
> +
> +    /* count encryption options */
> +    for (curr_encrypt = encrypt; curr_encrypt->has_parent;
> +         curr_encrypt = curr_encrypt->parent) {
> +        ++encrypt_count;
> +    }
> +
> +    specs = g_new0(rbd_encryption_spec_t, encrypt_count);
> +
> +    curr_encrypt = encrypt;
> +    for (i = 0; i < encrypt_count; ++i) {
> +        if (curr_encrypt->format != RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY) {
> +            r = -ENOTSUP;
> +            error_setg_errno(
> +                    errp, -r, "unknown image encryption format: %u",
> +                    curr_encrypt->format);
> +            goto exit;
> +        }
> +
> +        specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS;
> +        specs[i].opts_size = sizeof(rbd_encryption_luks_format_options_t);
> +
> +        luks_any_opts = g_new0(rbd_encryption_luks_format_options_t, 1);
> +        specs[i].opts = luks_any_opts;
> +
> +        r = qemu_rbd_convert_luks_options(
> +                qapi_RbdEncryptionOptionsLUKSAny_base(
> +                        &curr_encrypt->u.luks_any),
> +                (char**)&luks_any_opts->passphrase,
> +                &luks_any_opts->passphrase_size,
> +                errp);
> +        if (r < 0) {
> +            goto exit;
> +        }
> +
> +        curr_encrypt = curr_encrypt->parent;
> +    }
> +
> +    r = rbd_encryption_load2(image, specs, encrypt_count);
> +    if (r < 0) {
> +        error_setg_errno(errp, -r, "layered encryption load fail");
> +        goto exit;
> +    }
> +
> +exit:
> +    for (i = 0; i < encrypt_count; ++i) {
> +        luks_any_opts = specs[i].opts;
> +        if (luks_any_opts) {
> +            g_free((char*)luks_any_opts->passphrase);
> +            g_free(luks_any_opts);
> +        }
> +    }
> +    g_free(specs);
> +    return r;
> +}
> +#endif
>  #endif
>  
>  /* FIXME Deprecate and remove keypairs or make it available in QMP. */
> @@ -993,7 +1091,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
>  
>      if (opts->has_encrypt) {
>  #ifdef LIBRBD_SUPPORTS_ENCRYPTION
> -        r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
> +        if (opts->encrypt->has_parent) {
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +            r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp);
> +#else
> +            r = -ENOTSUP;
> +            error_setg(errp, "RBD library does not support layered encryption");
> +#endif
> +        } else {
> +            r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp);
> +        }
>          if (r < 0) {
>              goto failed_post_open;
>          }
> @@ -1284,6 +1391,16 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
>          spec_info->u.rbd.data->encryption_format =
>                  RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2;
>          spec_info->u.rbd.data->has_encryption_format = true;
> +    } else if (memcmp(buf, rbd_layered_luks_header_verification,
> +               RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
> +        spec_info->u.rbd.data->encryption_format =
> +                RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_LAYERED;
> +        spec_info->u.rbd.data->has_encryption_format = true;
> +    } else if (memcmp(buf, rbd_layered_luks2_header_verification,
> +               RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) {
> +        spec_info->u.rbd.data->encryption_format =
> +                RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2_LAYERED;
> +        spec_info->u.rbd.data->has_encryption_format = true;
>      } else {
>          spec_info->u.rbd.data->has_encryption_format = false;
>      }
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 882b266532..15715d990e 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -3753,10 +3753,20 @@
>  ##
>  # @RbdImageEncryptionFormat:
>  #
> +# luks
> +#
> +# luks2
> +#
> +# luks-any: Used for opening either luks or luks2. (Since 7.2)
> +#
> +# luks-layered: Layered encryption. Only used for info. (Since 7.2)
> +#
> +# luks2-layered: Layered encryption. Only used for info. (Since 7.2)

I don't think these additions really make sense given what you've
described. The image is still using 'luks' or 'luks2' format for
the encryption. All that's changed is that you give distinct
passphrases for each layer. IOW this isn't a different encryption
format, just a more flexible configuration

> +#
>  # Since: 6.1
>  ##
>  { 'enum': 'RbdImageEncryptionFormat',
> -  'data': [ 'luks', 'luks2' ] }
> +  'data': [ 'luks', 'luks2', 'luks-any', 'luks-layered', 'luks2-layered' ] }
>  
>  ##
>  # @RbdEncryptionOptionsLUKSBase:
> @@ -3798,6 +3808,15 @@
>    'base': 'RbdEncryptionOptionsLUKSBase',
>    'data': { } }
>  
> +##
> +# @RbdEncryptionOptionsLUKSAny:
> +#
> +# Since: 7.2
> +##
> +{ 'struct': 'RbdEncryptionOptionsLUKSAny',
> +  'base': 'RbdEncryptionOptionsLUKSBase',
> +  'data': { } }
> +
>  ##
>  # @RbdEncryptionCreateOptionsLUKS:
>  #
> @@ -3819,13 +3838,23 @@
>  ##
>  # @RbdEncryptionOptions:
>  #
> +# @format: Encryption format.
> +#
> +# @parent: Parent image encryption options (for cloned images).
> +#          Can be left unspecified if this cloned image is encrypted
> +#          using the same format and secret as its parent image (i.e.
> +#          not explicitly formatted) or if its parent image is not
> +#          encrypted. (Since 7.2)
> +#
>  # Since: 6.1
>  ##
>  { 'union': 'RbdEncryptionOptions',
> -  'base': { 'format': 'RbdImageEncryptionFormat' },
> +  'base': { 'format': 'RbdImageEncryptionFormat',
> +            '*parent': 'RbdEncryptionOptions' },
>    'discriminator': 'format',
>    'data': { 'luks': 'RbdEncryptionOptionsLUKS',
> -            'luks2': 'RbdEncryptionOptionsLUKS2' } }
> +            'luks2': 'RbdEncryptionOptionsLUKS2',
> +            'luks-any': 'RbdEncryptionOptionsLUKSAny'} }

AFAICT, supporting layered encryption shouldn't require anything other
than the 'parent' addition.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
RE: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Or Ozeri 1 year, 5 months ago
> -----Original Message-----
> From: Daniel P. Berrangé <berrange@redhat.com>
> Sent: 15 November 2022 19:47
> To: Or Ozeri <ORO@il.ibm.com>
> Cc: qemu-devel@nongnu.org; qemu-block@nongnu.org; Danny Harnik
> <DANNYH@il.ibm.com>; idryomov@gmail.com
> Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> encryption
> 
> AFAICT, supporting layered encryption shouldn't require anything other than
> the 'parent' addition.
> 

Since the layered encryption API is new in librbd, we don't have to support "luks" and "luks2" at all.
In librbd we are actually deprecating the use of "luks" and "luks2", and instead ask users to use "luks-any".
If we don't add "luks-any" here, we will need to implement explicit cases for "luks" and "luks2" in the qemu_rbd_encryption_load2. This looks like a kind of wasteful coding that won't be actually used by users of the rbd driver in qemu.
Anyhow, we need the "luks-any" option for our use-case, so if you insist, I will first submit a patch to add "luks-any", before this patch.
Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Daniel P. Berrangé 1 year, 5 months ago
On Wed, Nov 16, 2022 at 09:03:31AM +0000, Or Ozeri wrote:
> > -----Original Message-----
> > From: Daniel P. Berrangé <berrange@redhat.com>
> > Sent: 15 November 2022 19:47
> > To: Or Ozeri <ORO@il.ibm.com>
> > Cc: qemu-devel@nongnu.org; qemu-block@nongnu.org; Danny Harnik
> > <DANNYH@il.ibm.com>; idryomov@gmail.com
> > Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> > encryption
> > 
> > AFAICT, supporting layered encryption shouldn't require anything other than
> > the 'parent' addition.
> > 
> 
> Since the layered encryption API is new in librbd, we don't have to
> support "luks" and "luks2" at all.
> In librbd we are actually deprecating the use of "luks" and "luks2",
> and instead ask users to use "luks-any".

Deprecating that is a bad idea. The security characteristics and
feature set of LUKSv1 and LUKSv2 can be quite different. If a mgmt
app is expecting the volume to be protected with LUKSv2, it should
be stating that explicitly and not permit a silent downgrade if
the volume was unexpectedly using LUKSv1.

> If we don't add "luks-any" here, we will need to implement
> explicit cases for "luks" and "luks2" in the qemu_rbd_encryption_load2.
> This looks like a kind of wasteful coding that won't be actually used
> by users of the rbd driver in qemu.

It isn't wasteful - supporting the formats explicitly is desirable
to prevent format downgrades.

> Anyhow, we need the "luks-any" option for our use-case, so if you
> insist, I will first submit a patch to add "luks-any", before this
> patch.

I'm pretty wary of any kind of automatic encryption format detection
in QEMU. The automatic block driver format probing has been a long
standing source of CVEs in QEMU and every single mgmt app above QEMU.

What is the problem with specifying the desired LUKS format explicitly.
The mgmt app should know what formats it wants to be using. It should
be possible to query format for existing volumes too.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Daniel P. Berrangé 1 year, 5 months ago
On Wed, Nov 16, 2022 at 10:23:52AM +0000, Daniel P. Berrangé wrote:
> On Wed, Nov 16, 2022 at 09:03:31AM +0000, Or Ozeri wrote:
> > > -----Original Message-----
> > > From: Daniel P. Berrangé <berrange@redhat.com>
> > > Sent: 15 November 2022 19:47
> > > To: Or Ozeri <ORO@il.ibm.com>
> > > Cc: qemu-devel@nongnu.org; qemu-block@nongnu.org; Danny Harnik
> > > <DANNYH@il.ibm.com>; idryomov@gmail.com
> > > Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> > > encryption
> > > 
> > > AFAICT, supporting layered encryption shouldn't require anything other than
> > > the 'parent' addition.
> > > 
> > 
> > Since the layered encryption API is new in librbd, we don't have to
> > support "luks" and "luks2" at all.
> > In librbd we are actually deprecating the use of "luks" and "luks2",
> > and instead ask users to use "luks-any".
> 
> Deprecating that is a bad idea. The security characteristics and
> feature set of LUKSv1 and LUKSv2 can be quite different. If a mgmt
> app is expecting the volume to be protected with LUKSv2, it should
> be stating that explicitly and not permit a silent downgrade if
> the volume was unexpectedly using LUKSv1.
> 
> > If we don't add "luks-any" here, we will need to implement
> > explicit cases for "luks" and "luks2" in the qemu_rbd_encryption_load2.
> > This looks like a kind of wasteful coding that won't be actually used
> > by users of the rbd driver in qemu.
> 
> It isn't wasteful - supporting the formats explicitly is desirable
> to prevent format downgrades.
> 
> > Anyhow, we need the "luks-any" option for our use-case, so if you
> > insist, I will first submit a patch to add "luks-any", before this
> > patch.
> 
> I'm pretty wary of any kind of automatic encryption format detection
> in QEMU. The automatic block driver format probing has been a long
> standing source of CVEs in QEMU and every single mgmt app above QEMU.

Having said that, normal linux LUKS tools like cryptsetup or systemd
LUKS integration will auto-detect  luks1 vs luks2. All cryptsetup
commands also have an option to explicitly specify the format version.

So with that precedent I guess it is ok to add 'luks-any'.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


RE: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Or Ozeri 1 year, 5 months ago

> -----Original Message-----
> From: Daniel P. Berrangé <berrange@redhat.com>
> Sent: 16 November 2022 13:15
> To: Or Ozeri <ORO@il.ibm.com>; qemu-devel@nongnu.org; qemu-
> block@nongnu.org; Danny Harnik <DANNYH@il.ibm.com>;
> idryomov@gmail.com
> Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> encryption
> 
> On Wed, Nov 16, 2022 at 10:23:52AM +0000, Daniel P. Berrangé wrote:
> 
> So with that precedent I guess it is ok to add 'luks-any'.
> 

So is it OK to leave the "luks-any" changes to this commit?
If not, is it ok to split this to 2 commits, in the same "patch-series"?

BTW is there a possibility this will actually make it to 7.2?
For me personally, the highest priority for me is just getting it merged (not necessarily to the 7.2 release),
so that I can submit a follow-up patch to libvirt.
Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Daniel P. Berrangé 1 year, 5 months ago
On Thu, Nov 17, 2022 at 12:42:04PM +0000, Or Ozeri wrote:
> 
> 
> > -----Original Message-----
> > From: Daniel P. Berrangé <berrange@redhat.com>
> > Sent: 16 November 2022 13:15
> > To: Or Ozeri <ORO@il.ibm.com>; qemu-devel@nongnu.org; qemu-
> > block@nongnu.org; Danny Harnik <DANNYH@il.ibm.com>;
> > idryomov@gmail.com
> > Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> > encryption
> > 
> > On Wed, Nov 16, 2022 at 10:23:52AM +0000, Daniel P. Berrangé wrote:
> > 
> > So with that precedent I guess it is ok to add 'luks-any'.
> > 
> 
> So is it OK to leave the "luks-any" changes to this commit?
> If not, is it ok to split this to 2 commits, in the same "patch-series"?

It is conceptually separate from the layered encryption so
should be a separate commit, in a series.

> BTW is there a possibility this will actually make it to 7.2?

QEMU is in freeze now, which means we only take bug fixes, not
new features, until 8.0 opens up after release. It is fine to
send patches, as the maintainer can queue them until the tree
opens.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Ilya Dryomov 1 year, 5 months ago
On Wed, Nov 16, 2022 at 12:15 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Wed, Nov 16, 2022 at 10:23:52AM +0000, Daniel P. Berrangé wrote:
> > On Wed, Nov 16, 2022 at 09:03:31AM +0000, Or Ozeri wrote:
> > > > -----Original Message-----
> > > > From: Daniel P. Berrangé <berrange@redhat.com>
> > > > Sent: 15 November 2022 19:47
> > > > To: Or Ozeri <ORO@il.ibm.com>
> > > > Cc: qemu-devel@nongnu.org; qemu-block@nongnu.org; Danny Harnik
> > > > <DANNYH@il.ibm.com>; idryomov@gmail.com
> > > > Subject: [EXTERNAL] Re: [PATCH v3] block/rbd: Add support for layered
> > > > encryption
> > > >
> > > > AFAICT, supporting layered encryption shouldn't require anything other than
> > > > the 'parent' addition.
> > > >
> > >
> > > Since the layered encryption API is new in librbd, we don't have to
> > > support "luks" and "luks2" at all.
> > > In librbd we are actually deprecating the use of "luks" and "luks2",
> > > and instead ask users to use "luks-any".
> >
> > Deprecating that is a bad idea. The security characteristics and
> > feature set of LUKSv1 and LUKSv2 can be quite different. If a mgmt
> > app is expecting the volume to be protected with LUKSv2, it should
> > be stating that explicitly and not permit a silent downgrade if
> > the volume was unexpectedly using LUKSv1.
> >
> > > If we don't add "luks-any" here, we will need to implement
> > > explicit cases for "luks" and "luks2" in the qemu_rbd_encryption_load2.
> > > This looks like a kind of wasteful coding that won't be actually used
> > > by users of the rbd driver in qemu.
> >
> > It isn't wasteful - supporting the formats explicitly is desirable
> > to prevent format downgrades.
> >
> > > Anyhow, we need the "luks-any" option for our use-case, so if you
> > > insist, I will first submit a patch to add "luks-any", before this
> > > patch.
> >
> > I'm pretty wary of any kind of automatic encryption format detection
> > in QEMU. The automatic block driver format probing has been a long
> > standing source of CVEs in QEMU and every single mgmt app above QEMU.
>
> Having said that, normal linux LUKS tools like cryptsetup or systemd
> LUKS integration will auto-detect  luks1 vs luks2. All cryptsetup
> commands also have an option to explicitly specify the format version.
>
> So with that precedent I guess it is ok to add 'luks-any'.

Yeah, I think we may need to reconsider the intent to deprecate
LUKS1 and LUKS2 options for loading encryption in librbd in favor
of a generic LUKS(-ANY) option.  But, just on its own, LUKS(-ANY)
is definitely a thing and having it exposed in QEMU seems natural.

Thanks,

                Ilya
Re: [PATCH v3] block/rbd: Add support for layered encryption
Posted by Ilya Dryomov 1 year, 5 months ago
On Tue, Nov 15, 2022 at 1:25 PM Or Ozeri <oro@il.ibm.com> wrote:
>
> Starting from ceph Reef, RBD has built-in support for layered encryption,
> where each ancestor image (in a cloned image setting) can be possibly
> encrypted using a unique passphrase.
>
> A new function, rbd_encryption_load2, was added to librbd API.
> This new function supports an array of passphrases (via "spec" structs).
>
> This commit extends the qemu rbd driver API to use this new librbd API,
> in order to support this new layered encryption feature.
>
> Signed-off-by: Or Ozeri <oro@il.ibm.com>
> ---
> v3: further nit fixes suggested by @idryomov
> v2: nit fixes suggested by @idryomov
> ---
>  block/rbd.c          | 119 ++++++++++++++++++++++++++++++++++++++++++-
>  qapi/block-core.json |  35 +++++++++++--
>  2 files changed, 150 insertions(+), 4 deletions(-)
>
> diff --git a/block/rbd.c b/block/rbd.c
> index f826410f40..ce017c29b5 100644
> --- a/block/rbd.c
> +++ b/block/rbd.c
> @@ -71,6 +71,16 @@ static const char rbd_luks2_header_verification[
>      'L', 'U', 'K', 'S', 0xBA, 0xBE, 0, 2
>  };
>
> +static const char rbd_layered_luks_header_verification[
> +        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
> +    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 1
> +};
> +
> +static const char rbd_layered_luks2_header_verification[
> +        RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {
> +    'R', 'B', 'D', 'L', 0xBA, 0xBE, 0, 2
> +};
> +
>  typedef enum {
>      RBD_AIO_READ,
>      RBD_AIO_WRITE,
> @@ -470,6 +480,9 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>      size_t passphrase_len;
>      rbd_encryption_luks1_format_options_t luks_opts;
>      rbd_encryption_luks2_format_options_t luks2_opts;
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +    rbd_encryption_luks_format_options_t luks_any_opts;
> +#endif
>      rbd_encryption_format_t format;
>      rbd_encryption_options_t opts;
>      size_t opts_size;
> @@ -505,6 +518,23 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>              luks2_opts.passphrase_size = passphrase_len;
>              break;
>          }
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +        case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: {
> +            memset(&luks_any_opts, 0, sizeof(luks_any_opts));
> +            format = RBD_ENCRYPTION_FORMAT_LUKS;
> +            opts = &luks_any_opts;
> +            opts_size = sizeof(luks_any_opts);
> +            r = qemu_rbd_convert_luks_options(
> +                    qapi_RbdEncryptionOptionsLUKSAny_base(&encrypt->u.luks_any),
> +                    &passphrase, &passphrase_len, errp);
> +            if (r < 0) {
> +                return r;
> +            }
> +            luks_any_opts.passphrase = passphrase;
> +            luks_any_opts.passphrase_size = passphrase_len;
> +            break;
> +        }
> +#endif
>          default: {
>              r = -ENOTSUP;
>              error_setg_errno(
> @@ -522,6 +552,74 @@ static int qemu_rbd_encryption_load(rbd_image_t image,
>
>      return 0;
>  }
> +
> +#ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2
> +static int qemu_rbd_encryption_load2(rbd_image_t image,
> +                                     RbdEncryptionOptions *encrypt,
> +                                     Error **errp)
> +{
> +    int r = 0;
> +    int encrypt_count = 1;
> +    int i;
> +    RbdEncryptionOptions *curr_encrypt;
> +    rbd_encryption_spec_t *specs;
> +    rbd_encryption_luks_format_options_t* luks_any_opts;
> +
> +    /* count encryption options */
> +    for (curr_encrypt = encrypt; curr_encrypt->has_parent;
> +         curr_encrypt = curr_encrypt->parent) {
> +        ++encrypt_count;
> +    }
> +
> +    specs = g_new0(rbd_encryption_spec_t, encrypt_count);
> +
> +    curr_encrypt = encrypt;
> +    for (i = 0; i < encrypt_count; ++i) {
> +        if (curr_encrypt->format != RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY) {
> +            r = -ENOTSUP;
> +            error_setg_errno(
> +                    errp, -r, "unknown image encryption format: %u",
> +                    curr_encrypt->format);
> +            goto exit;
> +        }
> +
> +        specs[i].format = RBD_ENCRYPTION_FORMAT_LUKS;
> +        specs[i].opts_size = sizeof(rbd_encryption_luks_format_options_t);
> +
> +        luks_any_opts = g_new0(rbd_encryption_luks_format_options_t, 1);
> +        specs[i].opts = luks_any_opts;
> +
> +        r = qemu_rbd_convert_luks_options(
> +                qapi_RbdEncryptionOptionsLUKSAny_base(
> +                        &curr_encrypt->u.luks_any),
> +                (char**)&luks_any_opts->passphrase,

Nit: I would change qemu_rbd_convert_luks_options() to take
const char **passphrase and eliminate this cast.  It's a trivial
fixup so it can be folded into this patch with no explanation.

> +                &luks_any_opts->passphrase_size,
> +                errp);
> +        if (r < 0) {
> +            goto exit;
> +        }
> +
> +        curr_encrypt = curr_encrypt->parent;
> +    }
> +
> +    r = rbd_encryption_load2(image, specs, encrypt_count);
> +    if (r < 0) {
> +        error_setg_errno(errp, -r, "layered encryption load fail");
> +        goto exit;
> +    }
> +
> +exit:
> +    for (i = 0; i < encrypt_count; ++i) {
> +        luks_any_opts = specs[i].opts;
> +        if (luks_any_opts) {
> +            g_free((char*)luks_any_opts->passphrase);

Nit: when resorting to a cast, cast to the actual expected type.
In case of free(), that's void *.

free() should have been specified to take const void * but that
ship has sailed.  Too bad GLib didn't fix this for g_free()...

Thanks,

                Ilya