:p
atchew
Login
Starting from Ceph 0f93f745 (unreleased 18.0.0) and qemu 0f385a24 (unreleased 8.0.0), qemu and librbd users can use encrypted RBD cloned images, where the parent image is encrypted using a different scheme (e.g. different passphrase). Opening such image require supplying of multiple secrets. This patch series allows libvirt users to supply multiple secrets necessary for using such RBD images. For example: <encryption format='luks' engine='librbd'> <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80fb0'/> <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> </encryption> Note that unlike the qemu and libvirt API, we don't allow the user to specify the format of the parent image, but just the passphrase. We do so to minimize the changes made in libvirt. To still be able to support RBD images where the parent is encrypted using a different format (e.g. LUKS2 cloned image of a LUKS parent), an additional patch series allowing for LUKS* (luks-any) format will be submitted. In high-level, this patch series does the following: - change the qemuBlockStorageSourceAttachData struct to support multiple secrets - change the qemuDomainStorageSourcePrivate struct to support multiple secrets - translate multiple secrets from virStorageEncryption to qemu private data I manually patched the qemu 8.0.0 replies file to reflect relevant qemu support, to allow my tests to run. Note that any build qemu will not support this feature, unless compiled while having a librbd that has this feature bundled. Or Ozeri (7): tests: qemucapabilitiesdata: Add rbd encryption layering qemu: capabilities: Introduce QEMU_CAPS_RBD_ENCRYPTION_LAYERING capability qemu: add support for multiple secret aliases qemu: add multi-secret support in qemuBlockStorageSourceAttachData qemu: add multi-secret support in _qemuDomainStorageSourcePrivate qemu: support pass-on of multiple secrets to _qemuDomainStorageSourcePrivate qemu: add support for librbd layered encryption docs/formatstorageencryption.rst | 11 +- src/conf/schemas/storagecommon.rng | 4 +- src/qemu/qemu_alias.c | 8 +- src/qemu/qemu_alias.h | 3 +- src/qemu/qemu_block.c | 70 ++++++++---- src/qemu/qemu_block.h | 5 +- src/qemu/qemu_blockjob.c | 6 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 31 +++-- src/qemu/qemu_domain.c | 106 ++++++++++++++---- src/qemu/qemu_domain.h | 3 +- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration_params.c | 2 +- tests/qemublocktest.c | 7 +- .../caps_8.0.0.x86_64.replies | 5 + .../caps_8.0.0.x86_64.xml | 1 + ...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 + 23 files changed, 332 insertions(+), 63 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 -- 2.25.1
RBD encryption layering support was added to qemu in 0f385a24. Signed-off-by: Or Ozeri<oro@il.ibm.com> --- tests/qemucapabilitiesdata/caps_8.0.0.x86_64.replies | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.replies b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.replies index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.replies +++ b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.replies @@ -XXX,XX +XXX,XX @@ { "name": "format", "type": "666" + }, + { + "name": "parent", + "default": null, + "type": "545" } ], "meta-type": "object" -- 2.25.1
This capability represents that qemu supports the layered encryption of RBD images, where a cloned image is encrypted with a possible different encryption than its parent image. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml | 1 + 3 files changed, 4 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(virQEMUCaps, /* 445 */ "netdev.stream.reconnect", /* QEMU_CAPS_NETDEV_STREAM_RECONNECT */ + "rbd-encryption-layering", /* QEMU_CAPS_RBD_ENCRYPTION_LAYERING */ ); @@ -XXX,XX +XXX,XX @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "blockdev-add/arg-type/+nvme", QEMU_CAPS_DRIVE_NVME }, { "blockdev-add/arg-type/+file/aio/^io_uring", QEMU_CAPS_AIO_IO_URING }, { "blockdev-add/arg-type/+rbd/encrypt", QEMU_CAPS_RBD_ENCRYPTION }, + { "blockdev-add/arg-type/+rbd/encrypt/parent", QEMU_CAPS_RBD_ENCRYPTION_LAYERING }, { "blockdev-add/arg-type/+nbd/tls-hostname", QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME }, { "blockdev-snapshot/$allow-write-only-overlay", QEMU_CAPS_BLOCKDEV_SNAPSHOT_ALLOW_WRITE_ONLY }, { "chardev-add/arg-type/backend/+socket/data/reconnect", QEMU_CAPS_CHARDEV_RECONNECT }, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -XXX,XX +XXX,XX @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 445 */ QEMU_CAPS_NETDEV_STREAM_RECONNECT, /* -netdev stream supports reconnect */ + QEMU_CAPS_RBD_ENCRYPTION_LAYERING, /* layered encryption support for Ceph RBD */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='cryptodev-backend-lkcf'/> <flag name='pvpanic-pci'/> <flag name='netdev.stream.reconnect'/> + <flag name='rbd-encryption-layering'/> <version>7002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100244</microcodeVersion> -- 2.25.1
Change secret aliases from %s-%s-secret0 to %s-%s-secret%lu, which will later be used for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_alias.c | 8 +++++--- src/qemu/qemu_alias.h | 3 ++- src/qemu/qemu_domain.c | 14 ++++++++------ src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration_params.c | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -XXX,XX +XXX,XX @@ qemuDomainGetMasterKeyAlias(void) /* qemuAliasForSecret: * @parentalias: alias of the parent object * @obj: optional sub-object of the parent device the secret is for + * @secret_idx: secret index number (0 in the case of a single secret) * * Generate alias for a secret object used by @parentalias device or one of * the dependencies of the device described by @obj. */ char * qemuAliasForSecret(const char *parentalias, - const char *obj) + const char *obj, + size_t secret_idx) { if (obj) - return g_strdup_printf("%s-%s-secret0", parentalias, obj); - return g_strdup_printf("%s-secret0", parentalias); + return g_strdup_printf("%s-%s-secret%lu", parentalias, obj, secret_idx); + return g_strdup_printf("%s-secret%lu", parentalias, secret_idx); } /* qemuAliasTLSObjFromSrcAlias diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -XXX,XX +XXX,XX @@ char *qemuAliasFromHostdev(const virDomainHostdevDef *hostdev); char *qemuDomainGetMasterKeyAlias(void); char *qemuAliasForSecret(const char *parentalias, - const char *obj); + const char *obj, + size_t secret_idx); char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) ATTRIBUTE_NONNULL(1); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainSecretInfoSetup(qemuDomainObjPrivate *priv, * @priv: pointer to domain private object * @srcalias: Alias of the disk/hostdev used to generate the secret alias * @secretuse: specific usage for the secret (may be NULL if main object is using it) + * @secret_idx: secret index number (0 in the case of a single secret) * @usageType: The virSecretUsageType * @username: username to use for authentication (may be NULL) * @seclookupdef: Pointer to seclookupdef data @@ -XXX,XX +XXX,XX @@ static qemuDomainSecretInfo * qemuDomainSecretInfoSetupFromSecret(qemuDomainObjPrivate *priv, const char *srcalias, const char *secretuse, + size_t secret_idx, virSecretUsageType usageType, const char *username, virSecretLookupTypeDef *seclookupdef) { qemuDomainSecretInfo *secinfo; - g_autofree char *alias = qemuAliasForSecret(srcalias, secretuse); + g_autofree char *alias = qemuAliasForSecret(srcalias, secretuse, secret_idx); g_autofree uint8_t *secret = NULL; size_t secretlen = 0; VIR_IDENTITY_AUTORESTORE virIdentity *oldident = virIdentityElevateCurrent(); @@ -XXX,XX +XXX,XX @@ qemuDomainSecretInfoTLSNew(qemuDomainObjPrivate *priv, } seclookupdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; - return qemuDomainSecretInfoSetupFromSecret(priv, srcAlias, NULL, + return qemuDomainSecretInfoSetupFromSecret(priv, srcAlias, NULL, 0, VIR_SECRET_USAGE_TYPE_TLS, NULL, &seclookupdef); } @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepareCookies(qemuDomainObjPrivate *priv, virStorageSource *src, const char *aliasprotocol) { - g_autofree char *secretalias = qemuAliasForSecret(aliasprotocol, "httpcookie"); + g_autofree char *secretalias = qemuAliasForSecret(aliasprotocol, "httpcookie", 0); g_autofree char *cookies = qemuBlockStorageSourceGetCookieString(src); return qemuDomainSecretInfoSetup(priv, secretalias, NULL, @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, usageType = VIR_SECRET_USAGE_TYPE_CEPH; if (!(srcPriv->secinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasprotocol, - "auth", + "auth", 0, usageType, src->auth->username, &src->auth->seclookupdef))) @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, if (hasEnc) { if (!(srcPriv->encinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, - "encryption", + "encryption", 0, VIR_SECRET_USAGE_TYPE_VOLUME, NULL, &src->encryption->secrets[0]->seclookupdef))) @@ -XXX,XX +XXX,XX @@ qemuDomainPrepareHostdev(virDomainHostdevDef *hostdev, if (!(srcPriv->secinfo = qemuDomainSecretInfoSetupFromSecret(priv, backendalias, - NULL, + NULL, 0, usageType, src->auth->username, &src->auth->seclookupdef))) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -XXX,XX +XXX,XX @@ qemuDomainDelChardevTLSObjects(virQEMUDriver *driver, * secret UUID and we have a serial TCP chardev, then formulate a * secAlias which we'll attempt to destroy. */ if (cfg->chardevTLSx509secretUUID && - !(secAlias = qemuAliasForSecret(inAlias, NULL))) + !(secAlias = qemuAliasForSecret(inAlias, NULL, 0))) return -1; qemuDomainObjEnterMonitor(vm); diff --git a/src/qemu/qemu_migration_params.c b/src/qemu/qemu_migration_params.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_migration_params.c +++ b/src/qemu/qemu_migration_params.c @@ -XXX,XX +XXX,XX @@ qemuMigrationParamsResetTLS(virDomainObj *vm, return; tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE); - secAlias = qemuAliasForSecret(QEMU_MIGRATION_TLS_ALIAS_BASE, NULL); + secAlias = qemuAliasForSecret(QEMU_MIGRATION_TLS_ALIAS_BASE, NULL, 0); qemuDomainDelTLSObjects(vm, asyncJob, secAlias, tlsAlias); g_clear_pointer(&QEMU_DOMAIN_PRIVATE(vm)->migSecinfo, qemuDomainSecretInfoFree); -- 2.25.1
This commit changes the qemuBlockStorageSourceAttachData struct to support multiple secrets (instead of a single one before this commit). This will useful for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_block.c | 35 ++++++++++++++++++++++++++--------- src/qemu/qemu_block.h | 5 +++-- src/qemu/qemu_blockjob.c | 6 ++++++ src/qemu/qemu_command.c | 21 +++++++++++++++++---- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetBlockdevStorageSliceProps(virStorageSource *src) void qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachData *data) { + size_t i; if (!data) return; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachData *data) virJSONValueFree(data->prmgrProps); virJSONValueFree(data->authsecretProps); virJSONValueFree(data->httpcookiesecretProps); - virJSONValueFree(data->encryptsecretProps); + for (i = 0; i < data->encryptsecretCount; ++i) { + virJSONValueFree(data->encryptsecretProps[i]); + g_free(data->encryptsecretAlias[i]); + } virJSONValueFree(data->tlsProps); virJSONValueFree(data->tlsKeySecretProps); g_free(data->tlsAlias); g_free(data->tlsKeySecretAlias); g_free(data->authsecretAlias); + g_free(data->encryptsecretProps); g_free(data->encryptsecretAlias); g_free(data->httpcookiesecretAlias); g_free(data->driveCmd); @@ -XXX,XX +XXX,XX @@ static int qemuBlockStorageSourceAttachApplyFormatDeps(qemuMonitor *mon, qemuBlockStorageSourceAttachData *data) { - if (data->encryptsecretProps && - qemuMonitorAddObject(mon, &data->encryptsecretProps, - &data->encryptsecretAlias) < 0) - return -1; + size_t i; + for (i = 0; i < data->encryptsecretCount; ++i) { + if (qemuMonitorAddObject(mon, &data->encryptsecretProps[i], + &data->encryptsecretAlias[i]) < 0) + return -1; + } return 0; } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachRollback(qemuMonitor *mon, qemuBlockStorageSourceAttachData *data) { virErrorPtr orig_err; + size_t i; virErrorPreserveLast(&orig_err); @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachRollback(qemuMonitor *mon, if (data->authsecretAlias) ignore_value(qemuMonitorDelObject(mon, data->authsecretAlias, false)); - if (data->encryptsecretAlias) - ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias, false)); + for (i = 0; i < data->encryptsecretCount; ++i) { + if (data->encryptsecretAlias[i]) + ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias[i], false)); + } if (data->httpcookiesecretAlias) ignore_value(qemuMonitorDelObject(mon, data->httpcookiesecretAlias, false)); @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceDetachPrepare(virStorageSource *src) if (srcpriv->secinfo) data->authsecretAlias = g_strdup(srcpriv->secinfo->alias); - if (srcpriv->encinfo) - data->encryptsecretAlias = g_strdup(srcpriv->encinfo->alias); + if (srcpriv->encinfo) { + if (!data->encryptsecretAlias) { + data->encryptsecretCount = 1; + data->encryptsecretProps = g_new0(virJSONValue *, 1); + data->encryptsecretAlias = g_new0(char *, 1); + } + + data->encryptsecretAlias[0] = g_strdup(srcpriv->encinfo->alias); + } if (srcpriv->httpcookie) data->httpcookiesecretAlias = g_strdup(srcpriv->httpcookie->alias); diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -XXX,XX +XXX,XX @@ struct qemuBlockStorageSourceAttachData { virJSONValue *authsecretProps; char *authsecretAlias; - virJSONValue *encryptsecretProps; - char *encryptsecretAlias; + size_t encryptsecretCount; + virJSONValue **encryptsecretProps; + char **encryptsecretAlias; virJSONValue *httpcookiesecretProps; char *httpcookiesecretAlias; diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -XXX,XX +XXX,XX @@ qemuBlockJobProcessEventConcludedCreate(virQEMUDriver *driver, /* the format node part was not attached yet, so we don't need to detach it */ backend->formatAttached = false; if (job->data.create.storage) { + size_t i; + backend->storageAttached = false; backend->storageSliceAttached = false; + for (i = 0; i < backend->encryptsecretCount; ++i) { + VIR_FREE(backend->encryptsecretAlias[i]); + } VIR_FREE(backend->encryptsecretAlias); + VIR_FREE(backend->encryptsecretProps); } if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -XXX,XX +XXX,XX @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, virQEMUCaps *qemuCaps) { char *tmp; + size_t i; if (qemuBuildObjectCommandline(cmd, data->prmgrProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->authsecretProps, qemuCaps) < 0 || - qemuBuildObjectCommandline(cmd, data->encryptsecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->httpcookiesecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->tlsKeySecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->tlsProps, qemuCaps) < 0) return -1; + for (i = 0; i < data->encryptsecretCount; ++i) { + if (qemuBuildObjectCommandline(cmd, data->encryptsecretProps[i], qemuCaps) < 0) { + return -1; + } + } + if (data->driveCmd) virCommandAddArgList(cmd, "-drive", data->driveCmd, NULL); @@ -XXX,XX +XXX,XX @@ qemuBuildStorageSourceAttachPrepareCommon(virStorageSource *src, qemuBuildSecretInfoProps(srcpriv->secinfo, &data->authsecretProps) < 0) return -1; - if (srcpriv->encinfo && - qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps) < 0) - return -1; + if (srcpriv->encinfo) { + if (!data->encryptsecretProps) { + data->encryptsecretCount = 1; + data->encryptsecretProps = g_new0(virJSONValue *, 1); + data->encryptsecretAlias = g_new0(char *, 1); + } + + if (qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps[0]) < 0) + return -1; + } if (srcpriv->httpcookie && qemuBuildSecretInfoProps(srcpriv->httpcookie, &data->httpcookiesecretProps) < 0) -- 2.25.1
This commit changes the _qemuDomainStorageSourcePrivate struct to support multiple secrets (instead of a single one before this commit). This will useful for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_block.c | 22 +++++++----- src/qemu/qemu_command.c | 20 ++++++----- src/qemu/qemu_domain.c | 75 ++++++++++++++++++++++++++++++++--------- src/qemu/qemu_domain.h | 3 +- tests/qemublocktest.c | 7 ++-- 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, if (virJSONValueObjectAdd(&encrypt, "s:format", encformat, - "s:key-secret", srcPriv->encinfo->alias, + "s:key-secret", srcPriv->encinfo[0]->alias, NULL) < 0) return NULL; } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetFormatLUKSProps(virStorageSource *src, { qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); - if (!srcPriv || !srcPriv->encinfo || !srcPriv->encinfo->alias) { + if (!srcPriv || !srcPriv->encinfo || !srcPriv->encinfo[0]->alias) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing secret info for 'luks' driver")); return -1; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetFormatLUKSProps(virStorageSource *src, if (virJSONValueObjectAdd(&props, "s:driver", "luks", - "s:key-secret", srcPriv->encinfo->alias, + "s:key-secret", srcPriv->encinfo[0]->alias, NULL) < 0) return -1; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetCryptoProps(virStorageSource *src, return virJSONValueObjectAdd(encprops, "s:format", encformat, - "s:key-secret", srcpriv->encinfo->alias, + "s:key-secret", srcpriv->encinfo[0]->alias, NULL); } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceDetachPrepare(virStorageSource *src) data->authsecretAlias = g_strdup(srcpriv->secinfo->alias); if (srcpriv->encinfo) { + size_t i; + if (!data->encryptsecretAlias) { - data->encryptsecretCount = 1; - data->encryptsecretProps = g_new0(virJSONValue *, 1); - data->encryptsecretAlias = g_new0(char *, 1); + data->encryptsecretCount = srcpriv->enccount; + data->encryptsecretProps = g_new0(virJSONValue *, srcpriv->enccount); + data->encryptsecretAlias = g_new0(char *, srcpriv->enccount); } - data->encryptsecretAlias[0] = g_strdup(srcpriv->encinfo->alias); + for (i = 0; i < srcpriv->enccount; ++i) { + data->encryptsecretAlias[i] = g_strdup(srcpriv->encinfo[i]->alias); + } } if (srcpriv->httpcookie) @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceCreateGetEncryptionLUKS(virStorageSource *src, if (srcpriv && srcpriv->encinfo) - keysecret = srcpriv->encinfo->alias; + keysecret = srcpriv->encinfo[0]->alias; if (virJSONValueObjectAdd(&props, "s:key-secret", keysecret, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -XXX,XX +XXX,XX @@ qemuBuildDriveSourceStr(virDomainDiskDef *disk, { virStorageType actualType = virStorageSourceGetActualType(disk->src); qemuDomainStorageSourcePrivate *srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); - qemuDomainSecretInfo *encinfo = NULL; + qemuDomainSecretInfo **encinfo = NULL; g_autoptr(virJSONValue) srcprops = NULL; bool rawluks = false; @@ -XXX,XX +XXX,XX @@ qemuBuildDriveSourceStr(virDomainDiskDef *disk, if (encinfo) { if (disk->src->format == VIR_STORAGE_FILE_RAW) { - virBufferAsprintf(buf, "key-secret=%s,", encinfo->alias); + virBufferAsprintf(buf, "key-secret=%s,", encinfo[0]->alias); rawluks = true; } else if (disk->src->format == VIR_STORAGE_FILE_QCOW2 && disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { virBufferAddLit(buf, "encrypt.format=luks,"); - virBufferAsprintf(buf, "encrypt.key-secret=%s,", encinfo->alias); + virBufferAsprintf(buf, "encrypt.key-secret=%s,", encinfo[0]->alias); } } @@ -XXX,XX +XXX,XX @@ qemuBuildStorageSourceAttachPrepareCommon(virStorageSource *src, return -1; if (srcpriv->encinfo) { + size_t i; + if (!data->encryptsecretProps) { - data->encryptsecretCount = 1; - data->encryptsecretProps = g_new0(virJSONValue *, 1); - data->encryptsecretAlias = g_new0(char *, 1); + data->encryptsecretCount = srcpriv->enccount; + data->encryptsecretProps = g_new0(virJSONValue *, srcpriv->enccount); + data->encryptsecretAlias = g_new0(char *, srcpriv->enccount); } - if (qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps[0]) < 0) - return -1; + for (i = 0; i < srcpriv->enccount; ++i) { + if (qemuBuildSecretInfoProps(srcpriv->encinfo[i], &data->encryptsecretProps[i]) < 0) + return -1; + } } if (srcpriv->httpcookie && diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainStorageSourcePrivateDispose(void *obj) qemuDomainStorageSourcePrivate *priv = obj; g_clear_pointer(&priv->secinfo, qemuDomainSecretInfoFree); - g_clear_pointer(&priv->encinfo, qemuDomainSecretInfoFree); + if (priv->encinfo) { + size_t i; + for (i = 0; i < priv->enccount; ++i) { + g_clear_pointer(&priv->encinfo[i], qemuDomainSecretInfoFree); + } + priv->encinfo = NULL; + } g_clear_pointer(&priv->httpcookie, qemuDomainSecretInfoFree); g_clear_pointer(&priv->tlsKeySecret, qemuDomainSecretInfoFree); g_clear_pointer(&priv->fdpass, qemuFDPassFree); @@ -XXX,XX +XXX,XX @@ qemuDomainSecretDiskDestroy(virDomainDiskDef *disk) for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) { if ((srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(n))) { qemuDomainSecretInfoDestroy(srcPriv->secinfo); - qemuDomainSecretInfoDestroy(srcPriv->encinfo); + if (srcPriv->encinfo) { + size_t i; + + for (i = 0; i < srcPriv->enccount; ++i) { + qemuDomainSecretInfoDestroy(srcPriv->encinfo[i]); + } + } qemuDomainSecretInfoDestroy(srcPriv->tlsKeySecret); } } @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, } if (hasEnc) { - if (!(srcPriv->encinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, - "encryption", 0, - VIR_SECRET_USAGE_TYPE_VOLUME, - NULL, - &src->encryption->secrets[0]->seclookupdef))) - return -1; + srcPriv->enccount = 1; + srcPriv->encinfo = g_new0(qemuDomainSecretInfo *, 1); + if (!(srcPriv->encinfo[0] = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, + "encryption", 0, + VIR_SECRET_USAGE_TYPE_VOLUME, + NULL, + &src->encryption->secrets[0]->seclookupdef))) + return -1; } if (src->ncookies && @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, virStorageSource *src) { qemuDomainStorageSourcePrivate *priv; + g_autofree xmlNodePtr *encnodes = NULL; g_autofree char *authalias = NULL; - g_autofree char *encalias = NULL; g_autofree char *httpcookiealias = NULL; g_autofree char *tlskeyalias = NULL; g_autofree char *thresholdEventWithIndex = NULL; bool fdsetPresent = false; unsigned int fdSetID; + int enccount; src->nodestorage = virXPathString("string(./nodenames/nodename[@type='storage']/@name)", ctxt); src->nodeformat = virXPathString("string(./nodenames/nodename[@type='format']/@name)", ctxt); @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, src->pr->mgralias = virXPathString("string(./reservations/@mgralias)", ctxt); authalias = virXPathString("string(./objects/secret[@type='auth']/@alias)", ctxt); - encalias = virXPathString("string(./objects/secret[@type='encryption']/@alias)", ctxt); + if ((enccount = virXPathNodeSet("./objects/secret[@type='encryption']", ctxt, &encnodes)) < 0) + return -1; httpcookiealias = virXPathString("string(./objects/secret[@type='httpcookie']/@alias)", ctxt); tlskeyalias = virXPathString("string(./objects/secret[@type='tlskey']/@alias)", ctxt); fdsetPresent = virXPathUInt("string(./fdsets/fdset[@type='storage']/@id)", ctxt, &fdSetID) == 0; - if (authalias || encalias || httpcookiealias || tlskeyalias || fdsetPresent) { + if (authalias || (enccount > 0) || httpcookiealias || tlskeyalias || fdsetPresent) { + size_t i; + if (!src->privateData && !(src->privateData = qemuDomainStorageSourcePrivateNew())) return -1; @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0) return -1; - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo, &encalias) < 0) - return -1; + if (enccount > 0) { + xmlNodePtr tmp = ctxt->node; + + priv->enccount = enccount; + priv->encinfo = g_new0(qemuDomainSecretInfo *, enccount); + for (i = 0; i < enccount; ++i) { + g_autofree char *encalias = NULL; + + ctxt->node = encnodes[i]; + if (!(encalias = virXMLPropString(encnodes[i], "alias"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing alias on encryption secret #%lu"), i); + return -1; + } + + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[i], &encalias) < 0) + return -1; + } + + ctxt->node = tmp; + } if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->httpcookie, &httpcookiealias) < 0) return -1; @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataFormat(virStorageSource *src, return -1; if (srcPriv) { + size_t i; unsigned int fdSetID; qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->secinfo, "auth"); - qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->encinfo, "encryption"); + for (i = 0; i < srcPriv->enccount; ++i) { + qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->encinfo[i], "encryption"); + } qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->httpcookie, "httpcookie"); qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->tlsKeySecret, "tlskey"); @@ -XXX,XX +XXX,XX @@ qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk, } if (restoreEncSecret) { + if (!priv->encinfo) { + priv->enccount = 1; + priv->encinfo = g_new0(qemuDomainSecretInfo *, 1); + } + encalias = g_strdup_printf("%s-luks-secret0", disk->info.alias); - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo, &encalias) < 0) + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &encalias) < 0) return -1; } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -XXX,XX +XXX,XX @@ struct _qemuDomainStorageSourcePrivate { qemuDomainSecretInfo *secinfo; /* data required for decryption of encrypted storage source */ - qemuDomainSecretInfo *encinfo; + size_t enccount; + qemuDomainSecretInfo **encinfo; /* secure passthrough of the http cookie */ qemuDomainSecretInfo *httpcookie; diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -XXX,XX +XXX,XX @@ testQemuDiskXMLToJSONFakeSecrets(virStorageSource *src) } if (src->encryption) { - srcpriv->encinfo = g_new0(qemuDomainSecretInfo, 1); + srcpriv->encinfo = g_new0(qemuDomainSecretInfo *, 1); + srcpriv->encinfo[0] = g_new0(qemuDomainSecretInfo, 1); - srcpriv->encinfo->alias = g_strdup_printf("%s-encalias", - NULLSTR(src->nodeformat)); + srcpriv->encinfo[0]->alias = g_strdup_printf("%s-encalias", + NULLSTR(src->nodeformat)); } return 0; -- 2.25.1
This commit extends qemuDomainSecretStorageSourcePrepare to setup multiple qemu secrets as defined by virStorageSource->encryption. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_domain.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, } if (hasEnc) { - srcPriv->enccount = 1; - srcPriv->encinfo = g_new0(qemuDomainSecretInfo *, 1); - if (!(srcPriv->encinfo[0] = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, - "encryption", 0, - VIR_SECRET_USAGE_TYPE_VOLUME, - NULL, - &src->encryption->secrets[0]->seclookupdef))) - return -1; + size_t nsecrets = src->encryption->nsecrets; + size_t i; + + srcPriv->enccount = nsecrets; + srcPriv->encinfo = g_new0(qemuDomainSecretInfo *, nsecrets); + for (i = 0; i < nsecrets; ++i) { + if (!(srcPriv->encinfo[i] = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, + "encryption", i, + VIR_SECRET_USAGE_TYPE_VOLUME, + NULL, + &src->encryption->secrets[i]->seclookupdef))) + return -1; + } } if (src->ncookies && -- 2.25.1
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> --- 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 XXXXXXX..XXXXXXX 100644 --- a/docs/formatstorageencryption.rst +++ b/docs/formatstorageencryption.rst @@ -XXX,XX +XXX,XX @@ 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`` ( @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/storagecommon.rng +++ b/src/conf/schemas/storagecommon.rng @@ -XXX,XX +XXX,XX @@ </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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ 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; @@ -XXX,XX +XXX,XX @@ 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"; @@ -XXX,XX +XXX,XX @@ 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, @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ 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: @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX --- /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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args @@ -XXX,XX +XXX,XX @@ +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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml @@ -XXX,XX +XXX,XX @@ +<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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml @@ -XXX,XX +XXX,XX @@ +<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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -XXX,XX +XXX,XX @@ 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
v2: - add luks-any commits (including nit fixes) - removed qemu 8.0.0 replies commit - remove tautological if condition in qemuBlockStorageSourceAttachData initialization - add comments on validation of a single secret in qemu encryption engine - fix leak of qemuDomainStorageSourcePrivate->encinfo - remove ctxt->node modification in privatedata xml parsing - add test to modern-in.xml - squash commit #6 - add validation for a single secret in sd card disk Or Ozeri (7): qemu: capabilities: Introduce QEMU_CAPS_RBD_ENCRYPTION_LAYERING capability qemu: add support for multiple secret aliases qemu: add multi-secret support in qemuBlockStorageSourceAttachData qemu: add multi-secret support in _qemuDomainStorageSourcePrivate qemu: add support for librbd layered encryption qemu: capabilities: Introduce QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY capability qemu: add luks-any encryption support for RBD images docs/formatstorageencryption.rst | 20 ++- src/conf/schemas/storagecommon.rng | 5 +- src/conf/storage_encryption_conf.c | 2 +- src/conf/storage_encryption_conf.h | 1 + src/libvirt_private.syms | 1 + src/qemu/qemu_alias.c | 8 +- src/qemu/qemu_alias.h | 3 +- src/qemu/qemu_block.c | 77 ++++++++--- src/qemu/qemu_block.h | 5 +- src/qemu/qemu_blockjob.c | 6 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_command.c | 29 +++- src/qemu/qemu_domain.c | 130 +++++++++++++++--- src/qemu/qemu_domain.h | 3 +- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration_params.c | 2 +- src/qemu/qemu_validate.c | 8 ++ tests/qemublocktest.c | 7 +- .../caps_8.0.0.x86_64.xml | 2 + tests/qemustatusxml2xmldata/modern-in.xml | 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 | 41 ++++++ ...k-rbd-encryption-luks-any.x86_64-7.2.0.err | 1 + ...rbd-encryption-luks-any.x86_64-latest.args | 37 +++++ .../disk-network-rbd-encryption-luks-any.xml | 39 ++++++ tests/qemuxml2argvtest.c | 4 + ...-rbd-encryption-layering.x86_64-latest.xml | 46 +++++++ ...-rbd-encryption-luks-any.x86_64-latest.xml | 44 ++++++ tests/qemuxml2xmltest.c | 2 + 31 files changed, 517 insertions(+), 68 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/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-7.2.0.err create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.xml create mode 100644 tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/disk-network-rbd-encryption-luks-any.x86_64-latest.xml -- 2.25.1
This capability represents that qemu supports the layered encryption of RBD images, where a cloned image is encrypted with a possible different encryption than its parent image. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml | 1 + 3 files changed, 4 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(virQEMUCaps, /* 445 */ "netdev.stream.reconnect", /* QEMU_CAPS_NETDEV_STREAM_RECONNECT */ "virtio-gpu.blob", /* QEMU_CAPS_VIRTIO_GPU_BLOB */ + "rbd-encryption-layering", /* QEMU_CAPS_RBD_ENCRYPTION_LAYERING */ ); @@ -XXX,XX +XXX,XX @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "blockdev-add/arg-type/+nvme", QEMU_CAPS_DRIVE_NVME }, { "blockdev-add/arg-type/+file/aio/^io_uring", QEMU_CAPS_AIO_IO_URING }, { "blockdev-add/arg-type/+rbd/encrypt", QEMU_CAPS_RBD_ENCRYPTION }, + { "blockdev-add/arg-type/+rbd/encrypt/parent", QEMU_CAPS_RBD_ENCRYPTION_LAYERING }, { "blockdev-add/arg-type/+nbd/tls-hostname", QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME }, { "blockdev-snapshot/$allow-write-only-overlay", QEMU_CAPS_BLOCKDEV_SNAPSHOT_ALLOW_WRITE_ONLY }, { "chardev-add/arg-type/backend/+socket/data/reconnect", QEMU_CAPS_CHARDEV_RECONNECT }, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -XXX,XX +XXX,XX @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 445 */ QEMU_CAPS_NETDEV_STREAM_RECONNECT, /* -netdev stream supports reconnect */ QEMU_CAPS_VIRTIO_GPU_BLOB, /* -device virtio-gpu-*.blob= */ + QEMU_CAPS_RBD_ENCRYPTION_LAYERING, /* layered encryption support for Ceph RBD */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='pvpanic-pci'/> <flag name='netdev.stream.reconnect'/> <flag name='virtio-gpu.blob'/> + <flag name='rbd-encryption-layering'/> <version>7002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100244</microcodeVersion> -- 2.25.1
Change secret aliases from %s-%s-secret0 to %s-%s-secret%lu, which will later be used for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_alias.c | 8 +++++--- src/qemu/qemu_alias.h | 3 ++- src/qemu/qemu_domain.c | 14 ++++++++------ src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration_params.c | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -XXX,XX +XXX,XX @@ qemuDomainGetMasterKeyAlias(void) /* qemuAliasForSecret: * @parentalias: alias of the parent object * @obj: optional sub-object of the parent device the secret is for + * @secret_idx: secret index number (0 in the case of a single secret) * * Generate alias for a secret object used by @parentalias device or one of * the dependencies of the device described by @obj. */ char * qemuAliasForSecret(const char *parentalias, - const char *obj) + const char *obj, + size_t secret_idx) { if (obj) - return g_strdup_printf("%s-%s-secret0", parentalias, obj); - return g_strdup_printf("%s-secret0", parentalias); + return g_strdup_printf("%s-%s-secret%lu", parentalias, obj, secret_idx); + return g_strdup_printf("%s-secret%lu", parentalias, secret_idx); } /* qemuAliasTLSObjFromSrcAlias diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -XXX,XX +XXX,XX @@ char *qemuAliasFromHostdev(const virDomainHostdevDef *hostdev); char *qemuDomainGetMasterKeyAlias(void); char *qemuAliasForSecret(const char *parentalias, - const char *obj); + const char *obj, + size_t secret_idx); char *qemuAliasTLSObjFromSrcAlias(const char *srcAlias) ATTRIBUTE_NONNULL(1); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainSecretInfoSetup(qemuDomainObjPrivate *priv, * @priv: pointer to domain private object * @srcalias: Alias of the disk/hostdev used to generate the secret alias * @secretuse: specific usage for the secret (may be NULL if main object is using it) + * @secret_idx: secret index number (0 in the case of a single secret) * @usageType: The virSecretUsageType * @username: username to use for authentication (may be NULL) * @seclookupdef: Pointer to seclookupdef data @@ -XXX,XX +XXX,XX @@ static qemuDomainSecretInfo * qemuDomainSecretInfoSetupFromSecret(qemuDomainObjPrivate *priv, const char *srcalias, const char *secretuse, + size_t secret_idx, virSecretUsageType usageType, const char *username, virSecretLookupTypeDef *seclookupdef) { qemuDomainSecretInfo *secinfo; - g_autofree char *alias = qemuAliasForSecret(srcalias, secretuse); + g_autofree char *alias = qemuAliasForSecret(srcalias, secretuse, secret_idx); g_autofree uint8_t *secret = NULL; size_t secretlen = 0; VIR_IDENTITY_AUTORESTORE virIdentity *oldident = virIdentityElevateCurrent(); @@ -XXX,XX +XXX,XX @@ qemuDomainSecretInfoTLSNew(qemuDomainObjPrivate *priv, } seclookupdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; - return qemuDomainSecretInfoSetupFromSecret(priv, srcAlias, NULL, + return qemuDomainSecretInfoSetupFromSecret(priv, srcAlias, NULL, 0, VIR_SECRET_USAGE_TYPE_TLS, NULL, &seclookupdef); } @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepareCookies(qemuDomainObjPrivate *priv, virStorageSource *src, const char *aliasprotocol) { - g_autofree char *secretalias = qemuAliasForSecret(aliasprotocol, "httpcookie"); + g_autofree char *secretalias = qemuAliasForSecret(aliasprotocol, "httpcookie", 0); g_autofree char *cookies = qemuBlockStorageSourceGetCookieString(src); return qemuDomainSecretInfoSetup(priv, secretalias, NULL, @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, usageType = VIR_SECRET_USAGE_TYPE_CEPH; if (!(srcPriv->secinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasprotocol, - "auth", + "auth", 0, usageType, src->auth->username, &src->auth->seclookupdef))) @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, if (hasEnc) { if (!(srcPriv->encinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, - "encryption", + "encryption", 0, VIR_SECRET_USAGE_TYPE_VOLUME, NULL, &src->encryption->secrets[0]->seclookupdef))) @@ -XXX,XX +XXX,XX @@ qemuDomainPrepareHostdev(virDomainHostdevDef *hostdev, if (!(srcPriv->secinfo = qemuDomainSecretInfoSetupFromSecret(priv, backendalias, - NULL, + NULL, 0, usageType, src->auth->username, &src->auth->seclookupdef))) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -XXX,XX +XXX,XX @@ qemuDomainDelChardevTLSObjects(virQEMUDriver *driver, * secret UUID and we have a serial TCP chardev, then formulate a * secAlias which we'll attempt to destroy. */ if (cfg->chardevTLSx509secretUUID && - !(secAlias = qemuAliasForSecret(inAlias, NULL))) + !(secAlias = qemuAliasForSecret(inAlias, NULL, 0))) return -1; qemuDomainObjEnterMonitor(vm); diff --git a/src/qemu/qemu_migration_params.c b/src/qemu/qemu_migration_params.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_migration_params.c +++ b/src/qemu/qemu_migration_params.c @@ -XXX,XX +XXX,XX @@ qemuMigrationParamsResetTLS(virDomainObj *vm, return; tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE); - secAlias = qemuAliasForSecret(QEMU_MIGRATION_TLS_ALIAS_BASE, NULL); + secAlias = qemuAliasForSecret(QEMU_MIGRATION_TLS_ALIAS_BASE, NULL, 0); qemuDomainDelTLSObjects(vm, asyncJob, secAlias, tlsAlias); g_clear_pointer(&QEMU_DOMAIN_PRIVATE(vm)->migSecinfo, qemuDomainSecretInfoFree); -- 2.25.1
This commit changes the qemuBlockStorageSourceAttachData struct to support multiple secrets (instead of a single one before this commit). This will useful for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_block.c | 32 +++++++++++++++++++++++--------- src/qemu/qemu_block.h | 5 +++-- src/qemu/qemu_blockjob.c | 6 ++++++ src/qemu/qemu_command.c | 19 +++++++++++++++---- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetBlockdevStorageSliceProps(virStorageSource *src) void qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachData *data) { + size_t i; if (!data) return; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachDataFree(qemuBlockStorageSourceAttachData *data) virJSONValueFree(data->prmgrProps); virJSONValueFree(data->authsecretProps); virJSONValueFree(data->httpcookiesecretProps); - virJSONValueFree(data->encryptsecretProps); + for (i = 0; i < data->encryptsecretCount; ++i) { + virJSONValueFree(data->encryptsecretProps[i]); + g_free(data->encryptsecretAlias[i]); + } virJSONValueFree(data->tlsProps); virJSONValueFree(data->tlsKeySecretProps); g_free(data->tlsAlias); g_free(data->tlsKeySecretAlias); g_free(data->authsecretAlias); + g_free(data->encryptsecretProps); g_free(data->encryptsecretAlias); g_free(data->httpcookiesecretAlias); g_free(data->driveCmd); @@ -XXX,XX +XXX,XX @@ static int qemuBlockStorageSourceAttachApplyFormatDeps(qemuMonitor *mon, qemuBlockStorageSourceAttachData *data) { - if (data->encryptsecretProps && - qemuMonitorAddObject(mon, &data->encryptsecretProps, - &data->encryptsecretAlias) < 0) - return -1; + size_t i; + for (i = 0; i < data->encryptsecretCount; ++i) { + if (qemuMonitorAddObject(mon, &data->encryptsecretProps[i], + &data->encryptsecretAlias[i]) < 0) + return -1; + } return 0; } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachRollback(qemuMonitor *mon, qemuBlockStorageSourceAttachData *data) { virErrorPtr orig_err; + size_t i; virErrorPreserveLast(&orig_err); @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceAttachRollback(qemuMonitor *mon, if (data->authsecretAlias) ignore_value(qemuMonitorDelObject(mon, data->authsecretAlias, false)); - if (data->encryptsecretAlias) - ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias, false)); + for (i = 0; i < data->encryptsecretCount; ++i) { + if (data->encryptsecretAlias[i]) + ignore_value(qemuMonitorDelObject(mon, data->encryptsecretAlias[i], false)); + } if (data->httpcookiesecretAlias) ignore_value(qemuMonitorDelObject(mon, data->httpcookiesecretAlias, false)); @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceDetachPrepare(virStorageSource *src) if (srcpriv->secinfo) data->authsecretAlias = g_strdup(srcpriv->secinfo->alias); - if (srcpriv->encinfo) - data->encryptsecretAlias = g_strdup(srcpriv->encinfo->alias); + if (srcpriv->encinfo) { + data->encryptsecretCount = 1; + data->encryptsecretProps = g_new0(virJSONValue *, 1); + data->encryptsecretAlias = g_new0(char *, 1); + data->encryptsecretAlias[0] = g_strdup(srcpriv->encinfo->alias); + } if (srcpriv->httpcookie) data->httpcookiesecretAlias = g_strdup(srcpriv->httpcookie->alias); diff --git a/src/qemu/qemu_block.h b/src/qemu/qemu_block.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.h +++ b/src/qemu/qemu_block.h @@ -XXX,XX +XXX,XX @@ struct qemuBlockStorageSourceAttachData { virJSONValue *authsecretProps; char *authsecretAlias; - virJSONValue *encryptsecretProps; - char *encryptsecretAlias; + size_t encryptsecretCount; + virJSONValue **encryptsecretProps; + char **encryptsecretAlias; virJSONValue *httpcookiesecretProps; char *httpcookiesecretAlias; diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -XXX,XX +XXX,XX @@ qemuBlockJobProcessEventConcludedCreate(virQEMUDriver *driver, /* the format node part was not attached yet, so we don't need to detach it */ backend->formatAttached = false; if (job->data.create.storage) { + size_t i; + backend->storageAttached = false; backend->storageSliceAttached = false; + for (i = 0; i < backend->encryptsecretCount; ++i) { + VIR_FREE(backend->encryptsecretAlias[i]); + } VIR_FREE(backend->encryptsecretAlias); + VIR_FREE(backend->encryptsecretProps); } if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -XXX,XX +XXX,XX @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, virQEMUCaps *qemuCaps) { char *tmp; + size_t i; if (qemuBuildObjectCommandline(cmd, data->prmgrProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->authsecretProps, qemuCaps) < 0 || - qemuBuildObjectCommandline(cmd, data->encryptsecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->httpcookiesecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->tlsKeySecretProps, qemuCaps) < 0 || qemuBuildObjectCommandline(cmd, data->tlsProps, qemuCaps) < 0) return -1; + for (i = 0; i < data->encryptsecretCount; ++i) { + if (qemuBuildObjectCommandline(cmd, data->encryptsecretProps[i], qemuCaps) < 0) { + return -1; + } + } + if (data->driveCmd) virCommandAddArgList(cmd, "-drive", data->driveCmd, NULL); @@ -XXX,XX +XXX,XX @@ qemuBuildStorageSourceAttachPrepareCommon(virStorageSource *src, qemuBuildSecretInfoProps(srcpriv->secinfo, &data->authsecretProps) < 0) return -1; - if (srcpriv->encinfo && - qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps) < 0) - return -1; + if (srcpriv->encinfo) { + data->encryptsecretCount = 1; + data->encryptsecretProps = g_new0(virJSONValue *, 1); + data->encryptsecretAlias = g_new0(char *, 1); + + if (qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps[0]) < 0) + return -1; + } if (srcpriv->httpcookie && qemuBuildSecretInfoProps(srcpriv->httpcookie, &data->httpcookiesecretProps) < 0) -- 2.25.1
This commit changes the _qemuDomainStorageSourcePrivate struct to support multiple secrets (instead of a single one before this commit). This will useful for storage encryption requiring more than a single secret. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_block.c | 25 +++++--- src/qemu/qemu_command.c | 20 +++--- src/qemu/qemu_domain.c | 75 ++++++++++++++++++----- src/qemu/qemu_domain.h | 3 +- tests/qemublocktest.c | 7 ++- tests/qemustatusxml2xmldata/modern-in.xml | 14 +++++ 6 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, if (virJSONValueObjectAdd(&encrypt, "s:format", encformat, - "s:key-secret", srcPriv->encinfo->alias, + "s:key-secret", srcPriv->encinfo[0]->alias, NULL) < 0) return NULL; } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetFormatLUKSProps(virStorageSource *src, { qemuDomainStorageSourcePrivate *srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); - if (!srcPriv || !srcPriv->encinfo || !srcPriv->encinfo->alias) { + /* validation ensures that the qemu encryption engine accepts only a single secret */ + if (!srcPriv || !srcPriv->encinfo || !srcPriv->encinfo[0]->alias) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing secret info for 'luks' driver")); return -1; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetFormatLUKSProps(virStorageSource *src, if (virJSONValueObjectAdd(&props, "s:driver", "luks", - "s:key-secret", srcPriv->encinfo->alias, + "s:key-secret", srcPriv->encinfo[0]->alias, NULL) < 0) return -1; @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetCryptoProps(virStorageSource *src, return -1; } + /* validation ensures that the qemu encryption engine accepts only a single secret */ return virJSONValueObjectAdd(encprops, "s:format", encformat, - "s:key-secret", srcpriv->encinfo->alias, + "s:key-secret", srcpriv->encinfo[0]->alias, NULL); } @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceDetachPrepare(virStorageSource *src) data->authsecretAlias = g_strdup(srcpriv->secinfo->alias); if (srcpriv->encinfo) { - data->encryptsecretCount = 1; - data->encryptsecretProps = g_new0(virJSONValue *, 1); - data->encryptsecretAlias = g_new0(char *, 1); - data->encryptsecretAlias[0] = g_strdup(srcpriv->encinfo->alias); + size_t i; + + data->encryptsecretCount = srcpriv->enccount; + data->encryptsecretProps = g_new0(virJSONValue *, srcpriv->enccount); + data->encryptsecretAlias = g_new0(char *, srcpriv->enccount); + + for (i = 0; i < srcpriv->enccount; ++i) { + data->encryptsecretAlias[i] = g_strdup(srcpriv->encinfo[i]->alias); + } } if (srcpriv->httpcookie) @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceCreateGetEncryptionLUKS(virStorageSource *src, if (srcpriv && srcpriv->encinfo) - keysecret = srcpriv->encinfo->alias; + keysecret = srcpriv->encinfo[0]->alias; if (virJSONValueObjectAdd(&props, "s:key-secret", keysecret, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -XXX,XX +XXX,XX @@ qemuBuildDriveSourceStr(virDomainDiskDef *disk, { virStorageType actualType = virStorageSourceGetActualType(disk->src); qemuDomainStorageSourcePrivate *srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); - qemuDomainSecretInfo *encinfo = NULL; + qemuDomainSecretInfo **encinfo = NULL; g_autoptr(virJSONValue) srcprops = NULL; bool rawluks = false; @@ -XXX,XX +XXX,XX @@ qemuBuildDriveSourceStr(virDomainDiskDef *disk, if (encinfo) { if (disk->src->format == VIR_STORAGE_FILE_RAW) { - virBufferAsprintf(buf, "key-secret=%s,", encinfo->alias); + virBufferAsprintf(buf, "key-secret=%s,", encinfo[0]->alias); rawluks = true; } else if (disk->src->format == VIR_STORAGE_FILE_QCOW2 && disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { virBufferAddLit(buf, "encrypt.format=luks,"); - virBufferAsprintf(buf, "encrypt.key-secret=%s,", encinfo->alias); + virBufferAsprintf(buf, "encrypt.key-secret=%s,", encinfo[0]->alias); } } @@ -XXX,XX +XXX,XX @@ qemuBuildStorageSourceAttachPrepareCommon(virStorageSource *src, return -1; if (srcpriv->encinfo) { - data->encryptsecretCount = 1; - data->encryptsecretProps = g_new0(virJSONValue *, 1); - data->encryptsecretAlias = g_new0(char *, 1); + size_t i; - if (qemuBuildSecretInfoProps(srcpriv->encinfo, &data->encryptsecretProps[0]) < 0) - return -1; + data->encryptsecretCount = srcpriv->enccount; + data->encryptsecretProps = g_new0(virJSONValue *, srcpriv->enccount); + data->encryptsecretAlias = g_new0(char *, srcpriv->enccount); + + for (i = 0; i < srcpriv->enccount; ++i) { + if (qemuBuildSecretInfoProps(srcpriv->encinfo[i], &data->encryptsecretProps[i]) < 0) + return -1; + } } if (srcpriv->httpcookie && diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainStorageSourcePrivateDispose(void *obj) qemuDomainStorageSourcePrivate *priv = obj; g_clear_pointer(&priv->secinfo, qemuDomainSecretInfoFree); - g_clear_pointer(&priv->encinfo, qemuDomainSecretInfoFree); + if (priv->encinfo) { + size_t i; + for (i = 0; i < priv->enccount; ++i) { + g_clear_pointer(&priv->encinfo[i], qemuDomainSecretInfoFree); + } + VIR_FREE(priv->encinfo); + } g_clear_pointer(&priv->httpcookie, qemuDomainSecretInfoFree); g_clear_pointer(&priv->tlsKeySecret, qemuDomainSecretInfoFree); g_clear_pointer(&priv->fdpass, qemuFDPassFree); @@ -XXX,XX +XXX,XX @@ qemuDomainSecretDiskDestroy(virDomainDiskDef *disk) for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) { if ((srcPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(n))) { qemuDomainSecretInfoDestroy(srcPriv->secinfo); - qemuDomainSecretInfoDestroy(srcPriv->encinfo); + if (srcPriv->encinfo) { + size_t i; + + for (i = 0; i < srcPriv->enccount; ++i) { + qemuDomainSecretInfoDestroy(srcPriv->encinfo[i]); + } + } qemuDomainSecretInfoDestroy(srcPriv->tlsKeySecret); } } @@ -XXX,XX +XXX,XX @@ qemuDomainSecretStorageSourcePrepare(qemuDomainObjPrivate *priv, } if (hasEnc) { - if (!(srcPriv->encinfo = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, - "encryption", 0, - VIR_SECRET_USAGE_TYPE_VOLUME, - NULL, - &src->encryption->secrets[0]->seclookupdef))) - return -1; + size_t nsecrets = src->encryption->nsecrets; + size_t i; + + srcPriv->enccount = nsecrets; + srcPriv->encinfo = g_new0(qemuDomainSecretInfo *, nsecrets); + for (i = 0; i < nsecrets; ++i) { + if (!(srcPriv->encinfo[i] = qemuDomainSecretInfoSetupFromSecret(priv, aliasformat, + "encryption", i, + VIR_SECRET_USAGE_TYPE_VOLUME, + NULL, + &src->encryption->secrets[i]->seclookupdef))) + return -1; + } } if (src->ncookies && @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, virStorageSource *src) { qemuDomainStorageSourcePrivate *priv; + g_autofree xmlNodePtr *encnodes = NULL; g_autofree char *authalias = NULL; - g_autofree char *encalias = NULL; g_autofree char *httpcookiealias = NULL; g_autofree char *tlskeyalias = NULL; g_autofree char *thresholdEventWithIndex = NULL; bool fdsetPresent = false; unsigned int fdSetID; + int enccount; src->nodestorage = virXPathString("string(./nodenames/nodename[@type='storage']/@name)", ctxt); src->nodeformat = virXPathString("string(./nodenames/nodename[@type='format']/@name)", ctxt); @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, src->pr->mgralias = virXPathString("string(./reservations/@mgralias)", ctxt); authalias = virXPathString("string(./objects/secret[@type='auth']/@alias)", ctxt); - encalias = virXPathString("string(./objects/secret[@type='encryption']/@alias)", ctxt); + if ((enccount = virXPathNodeSet("./objects/secret[@type='encryption']", ctxt, &encnodes)) < 0) + return -1; httpcookiealias = virXPathString("string(./objects/secret[@type='httpcookie']/@alias)", ctxt); tlskeyalias = virXPathString("string(./objects/secret[@type='tlskey']/@alias)", ctxt); fdsetPresent = virXPathUInt("string(./fdsets/fdset[@type='storage']/@id)", ctxt, &fdSetID) == 0; - if (authalias || encalias || httpcookiealias || tlskeyalias || fdsetPresent) { + if (authalias || (enccount > 0) || httpcookiealias || tlskeyalias || fdsetPresent) { if (!src->privateData && !(src->privateData = qemuDomainStorageSourcePrivateNew())) return -1; @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataParse(xmlXPathContextPtr ctxt, if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0) return -1; - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo, &encalias) < 0) - return -1; + if (enccount > 0) { + size_t i; + + priv->enccount = enccount; + priv->encinfo = g_new0(qemuDomainSecretInfo *, enccount); + for (i = 0; i < enccount; ++i) { + g_autofree char *encalias = NULL; + + if (!(encalias = virXMLPropString(encnodes[i], "alias"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing alias on encryption secret #%lu"), i); + return -1; + } + + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[i], &encalias) < 0) + return -1; + } + } if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->httpcookie, &httpcookiealias) < 0) return -1; @@ -XXX,XX +XXX,XX @@ qemuStorageSourcePrivateDataFormat(virStorageSource *src, return -1; if (srcPriv) { + size_t i; unsigned int fdSetID; qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->secinfo, "auth"); - qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->encinfo, "encryption"); + for (i = 0; i < srcPriv->enccount; ++i) { + qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->encinfo[i], "encryption"); + } qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->httpcookie, "httpcookie"); qemuStorageSourcePrivateDataFormatSecinfo(&objectsChildBuf, srcPriv->tlsKeySecret, "tlskey"); @@ -XXX,XX +XXX,XX @@ qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk, } if (restoreEncSecret) { + if (!priv->encinfo) { + priv->enccount = 1; + priv->encinfo = g_new0(qemuDomainSecretInfo *, 1); + } + encalias = g_strdup_printf("%s-luks-secret0", disk->info.alias); - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo, &encalias) < 0) + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &encalias) < 0) return -1; } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -XXX,XX +XXX,XX @@ struct _qemuDomainStorageSourcePrivate { qemuDomainSecretInfo *secinfo; /* data required for decryption of encrypted storage source */ - qemuDomainSecretInfo *encinfo; + size_t enccount; + qemuDomainSecretInfo **encinfo; /* secure passthrough of the http cookie */ qemuDomainSecretInfo *httpcookie; diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -XXX,XX +XXX,XX @@ testQemuDiskXMLToJSONFakeSecrets(virStorageSource *src) } if (src->encryption) { - srcpriv->encinfo = g_new0(qemuDomainSecretInfo, 1); + srcpriv->encinfo = g_new0(qemuDomainSecretInfo *, 1); + srcpriv->encinfo[0] = g_new0(qemuDomainSecretInfo, 1); - srcpriv->encinfo->alias = g_strdup_printf("%s-encalias", - NULLSTR(src->nodeformat)); + srcpriv->encinfo[0]->alias = g_strdup_printf("%s-encalias", + NULLSTR(src->nodeformat)); } return 0; diff --git a/tests/qemustatusxml2xmldata/modern-in.xml b/tests/qemustatusxml2xmldata/modern-in.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemustatusxml2xmldata/modern-in.xml +++ b/tests/qemustatusxml2xmldata/modern-in.xml @@ -XXX,XX +XXX,XX @@ </privateData> <diskSecretsPlacement auth='true'/> </disk> + <disk type='network' device='disk'> + <driver name='qemu' type='raw'/> + <source protocol='rbd' name='pool/image' tlsFromConfig='0'> + <host name='example.org'/> + <privateData> + <objects> + <secret type='encryption' alias='test-encryption-alias'/> + <secret type='encryption' alias='test-encryption-alias2'/> + </objects> + </privateData> + </source> + <target dev='vdc' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0c' function='0x0'/> + </disk> <disk type='file' device='cdrom'> <driver name='qemu' type='raw'/> <source file='/var/lib/libvirt/images/systemrescuecd-x86-4.9.5.iso'/> -- 2.25.1
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> --- 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 XXXXXXX..XXXXXXX 100644 --- a/docs/formatstorageencryption.rst +++ b/docs/formatstorageencryption.rst @@ -XXX,XX +XXX,XX @@ 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`` ( @@ -XXX,XX +XXX,XX @@ 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 @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/storagecommon.rng +++ b/src/conf/schemas/storagecommon.rng @@ -XXX,XX +XXX,XX @@ </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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ 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"; @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ 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: @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX --- /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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.x86_64-latest.args @@ -XXX,XX +XXX,XX @@ +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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-layering.xml @@ -XXX,XX +XXX,XX @@ +<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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -XXX,XX +XXX,XX @@ 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 XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-layering.x86_64-latest.xml @@ -XXX,XX +XXX,XX @@ +<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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -XXX,XX +XXX,XX @@ 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
This capability represents that qemu supports the "luks-any" encryption format for RBD images. Both LUKS and LUKS2 formats can be parsed using this wildcard format. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml | 1 + 3 files changed, 4 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(virQEMUCaps, "netdev.stream.reconnect", /* QEMU_CAPS_NETDEV_STREAM_RECONNECT */ "virtio-gpu.blob", /* QEMU_CAPS_VIRTIO_GPU_BLOB */ "rbd-encryption-layering", /* QEMU_CAPS_RBD_ENCRYPTION_LAYERING */ + "rbd-encryption-luks-any", /* QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY */ ); @@ -XXX,XX +XXX,XX @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] = { { "blockdev-add/arg-type/+file/aio/^io_uring", QEMU_CAPS_AIO_IO_URING }, { "blockdev-add/arg-type/+rbd/encrypt", QEMU_CAPS_RBD_ENCRYPTION }, { "blockdev-add/arg-type/+rbd/encrypt/parent", QEMU_CAPS_RBD_ENCRYPTION_LAYERING }, + { "blockdev-add/arg-type/+rbd/encrypt/format/^luks-any", QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY }, { "blockdev-add/arg-type/+nbd/tls-hostname", QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME }, { "blockdev-snapshot/$allow-write-only-overlay", QEMU_CAPS_BLOCKDEV_SNAPSHOT_ALLOW_WRITE_ONLY }, { "chardev-add/arg-type/backend/+socket/data/reconnect", QEMU_CAPS_CHARDEV_RECONNECT }, diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -XXX,XX +XXX,XX @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_NETDEV_STREAM_RECONNECT, /* -netdev stream supports reconnect */ QEMU_CAPS_VIRTIO_GPU_BLOB, /* -device virtio-gpu-*.blob= */ QEMU_CAPS_RBD_ENCRYPTION_LAYERING, /* layered encryption support for Ceph RBD */ + QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY, /* luks-any (LUKS and LUKS2) encryption format for Ceph RBD */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0.x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='netdev.stream.reconnect'/> <flag name='virtio-gpu.blob'/> <flag name='rbd-encryption-layering'/> + <flag name='rbd-encryption-luks-any'/> <version>7002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100244</microcodeVersion> -- 2.25.1
The newly added luks-any rbd encryption format in qemu allows for opening both LUKS and LUKS2 encryption formats. This commit enables libvirt uses to use this wildcard format. Signed-off-by: Or Ozeri <oro@il.ibm.com> --- docs/formatstorageencryption.rst | 9 ++++ src/conf/schemas/storagecommon.rng | 1 + src/conf/storage_encryption_conf.c | 2 +- src/conf/storage_encryption_conf.h | 1 + src/libvirt_private.syms | 1 + src/qemu/qemu_block.c | 10 +++-- src/qemu/qemu_domain.c | 29 +++++++++++- ...k-rbd-encryption-luks-any.x86_64-7.2.0.err | 1 + ...rbd-encryption-luks-any.x86_64-latest.args | 37 ++++++++++++++++ .../disk-network-rbd-encryption-luks-any.xml | 39 ++++++++++++++++ tests/qemuxml2argvtest.c | 2 + ...-rbd-encryption-luks-any.x86_64-latest.xml | 44 +++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 13 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-7.2.0.err create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.xml create mode 100644 tests/qemuxml2xmloutdata/disk-network-rbd-encryption-luks-any.x86_64-latest.xml diff --git a/docs/formatstorageencryption.rst b/docs/formatstorageencryption.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/formatstorageencryption.rst +++ b/docs/formatstorageencryption.rst @@ -XXX,XX +XXX,XX @@ to a qemu VM using the qemu VM driver. A single ``<secret type='passphrase'...>`` element is expected (except for the case of RBD layered encryption mentioned above). +``luks-any`` format +~~~~~~~~~~~~~~~~~~~ + +The ``luks-any`` format is currently supported only by the ``librbd`` engine, +and can only be applied to RBD network disks (RBD images). This format will try +to parse the disk as either LUKS or LUKS2, depending on the actual on-disk +format. A single ``<secret type='passphrase'...>`` element is expected (except +for the case of RBD layered encryption mentioned above) :since:`Since 9.3.0` . + Examples -------- diff --git a/src/conf/schemas/storagecommon.rng b/src/conf/schemas/storagecommon.rng index XXXXXXX..XXXXXXX 100644 --- a/src/conf/schemas/storagecommon.rng +++ b/src/conf/schemas/storagecommon.rng @@ -XXX,XX +XXX,XX @@ <value>qcow</value> <value>luks</value> <value>luks2</value> + <value>luks-any</value> </choice> </attribute> <optional> diff --git a/src/conf/storage_encryption_conf.c b/src/conf/storage_encryption_conf.c index XXXXXXX..XXXXXXX 100644 --- a/src/conf/storage_encryption_conf.c +++ b/src/conf/storage_encryption_conf.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(virStorageEncryptionSecret, VIR_ENUM_IMPL(virStorageEncryptionFormat, VIR_STORAGE_ENCRYPTION_FORMAT_LAST, - "default", "qcow", "luks", "luks2", + "default", "qcow", "luks", "luks2", "luks-any", ); VIR_ENUM_IMPL(virStorageEncryptionEngine, diff --git a/src/conf/storage_encryption_conf.h b/src/conf/storage_encryption_conf.h index XXXXXXX..XXXXXXX 100644 --- a/src/conf/storage_encryption_conf.h +++ b/src/conf/storage_encryption_conf.h @@ -XXX,XX +XXX,XX @@ typedef enum { VIR_STORAGE_ENCRYPTION_FORMAT_QCOW, /* Both qcow and qcow2 */ VIR_STORAGE_ENCRYPTION_FORMAT_LUKS, VIR_STORAGE_ENCRYPTION_FORMAT_LUKS2, + VIR_STORAGE_ENCRYPTION_FORMAT_LUKS_ANY, VIR_STORAGE_ENCRYPTION_FORMAT_LAST, } virStorageEncryptionFormatType; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index XXXXXXX..XXXXXXX 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -XXX,XX +XXX,XX @@ virStorageVolTypeToString; # conf/storage_encryption_conf.h virStorageEncryptionFormat; +virStorageEncryptionFormatTypeToString; virStorageEncryptionFree; virStorageEncryptionParseNode; diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src, encformat = "luks2"; break; + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS_ANY: + encformat = "luks-any"; + break; + case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW: case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT: case VIR_STORAGE_ENCRYPTION_FORMAT_LAST: @@ -XXX,XX +XXX,XX @@ qemuBlockStorageSourceGetCryptoProps(virStorageSource *src, break; case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS2: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("luks2 is currently not supported by the qemu encryption engine")); - return -1; - + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS_ANY: + /* validation code asserts the above cases are impossible */ case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT: case VIR_STORAGE_ENCRYPTION_FORMAT_LAST: default: diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainValidateStorageSource(virStorageSource *src, break; case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS2: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("luks2 is currently not supported by the qemu encryption engine")); + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS_ANY: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("'%s' is currently not supported by the qemu encryption engine"), + virStorageEncryptionFormatTypeToString(src->encryption->format)); return -1; case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT: @@ -XXX,XX +XXX,XX @@ qemuDomainValidateStorageSource(virStorageSource *src, return -1; } } + + switch ((virStorageEncryptionFormatType) src->encryption->format) { + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS: + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS2: + break; + + case VIR_STORAGE_ENCRYPTION_FORMAT_LUKS_ANY: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_RBD_ENCRYPTION_LUKS_ANY)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("luks-any encryption is not supported by this QEMU binary")); + return -1; + } + break; + + case VIR_STORAGE_ENCRYPTION_FORMAT_QCOW: + /* validation code above already asserts this case is impossible */ + case VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT: + case VIR_STORAGE_ENCRYPTION_FORMAT_LAST: + default: + virReportEnumRangeError(virStorageEncryptionFormatType, + src->encryption->format); + return -1; + } break; case VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT: diff --git a/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-7.2.0.err b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-7.2.0.err new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-7.2.0.err @@ -0,0 +1 @@ +unsupported configuration: luks-any encryption is not supported by this QEMU binary diff --git a/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-latest.args b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-latest.args new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.x86_64-latest.args @@ -XXX,XX +XXX,XX @@ +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"}' \ +-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-any","key-secret":"libvirt-1-format-encryption-secret0"},"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-luks-any.xml b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.xml new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2argvdata/disk-network-rbd-encryption-luks-any.xml @@ -XXX,XX +XXX,XX @@ +<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-any' engine='librbd'> + <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80fb0'/> + </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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -XXX,XX +XXX,XX @@ mymain(void) 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_VER_PARSE_ERROR("disk-network-rbd-encryption-luks-any", "7.2.0"); + DO_TEST_CAPS_LATEST("disk-network-rbd-encryption-luks-any"); 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-luks-any.x86_64-latest.xml b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-luks-any.x86_64-latest.xml new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-network-rbd-encryption-luks-any.x86_64-latest.xml @@ -XXX,XX +XXX,XX @@ +<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-any' engine='librbd'> + <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80fb0'/> + </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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -XXX,XX +XXX,XX @@ mymain(void) 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_CAPS_LATEST("disk-network-rbd-encryption-luks-any"); DO_TEST_NOCAPS("disk-network-source-auth"); DO_TEST_NOCAPS("disk-network-sheepdog"); DO_TEST_NOCAPS("disk-network-vxhs"); -- 2.25.1