From: "Daniel P. Berrangé" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Hanna Reitz" <hreitz@redhat.com>,
"Gerd Hoffmann" <kraxel@redhat.com>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Markus Armbruster" <armbru@redhat.com>,
"Cleber Rosa" <crosa@redhat.com>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Eric Blake" <eblake@redhat.com>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Eduardo Habkost" <eduardo@habkost.net>,
"Stefan Weil" <sw@weilnetz.de>, "Kevin Wolf" <kwolf@redhat.com>,
"Thomas Huth" <thuth@redhat.com>,
"Hyman Huang" <yong.huang@smartx.com>,
"Richard Henderson" <richard.henderson@linaro.org>,
"John Snow" <jsnow@redhat.com>,
qemu-block@nongnu.org, "Daniel P. Berrangé" <berrange@redhat.com>
Subject: [PULL 11/17] crypto: Support LUKS volume with detached header
Date: Fri, 9 Feb 2024 14:04:59 +0000 [thread overview]
Message-ID: <20240209140505.2536635-12-berrange@redhat.com> (raw)
In-Reply-To: <20240209140505.2536635-1-berrange@redhat.com>
From: Hyman Huang <yong.huang@smartx.com>
By enhancing the LUKS driver, it is possible to implement
the LUKS volume with a detached header.
Normally a LUKS volume has a layout:
disk: | header | key material | disk payload data |
With a detached LUKS header, you need 2 disks so getting:
disk1: | header | key material |
disk2: | disk payload data |
There are a variety of benefits to doing this:
* Secrecy - the disk2 cannot be identified as containing LUKS
volume since there's no header
* Control - if access to the disk1 is restricted, then even
if someone has access to disk2 they can't unlock
it. Might be useful if you have disks on NFS but
want to restrict which host can launch a VM
instance from it, by dynamically providing access
to the header to a designated host
* Flexibility - your application data volume may be a given
size and it is inconvenient to resize it to
add encryption.You can store the LUKS header
separately and use the existing storage
volume for payload
* Recovery - corruption of a bit in the header may make the
entire payload inaccessible. It might be
convenient to take backups of the header. If
your primary disk header becomes corrupt, you
can unlock the data still by pointing to the
backup detached header
Take the raw-format image as an example to introduce the usage
of the LUKS volume with a detached header:
1. prepare detached LUKS header images
$ dd if=/dev/zero of=test-header.img bs=1M count=32
$ dd if=/dev/zero of=test-payload.img bs=1M count=1000
$ cryptsetup luksFormat --header test-header.img test-payload.img
> --force-password --type luks1
2. block-add a protocol blockdev node of payload image
$ virsh qemu-monitor-command vm '{"execute":"blockdev-add",
> "arguments":{"node-name":"libvirt-1-storage", "driver":"file",
> "filename":"test-payload.img"}}'
3. block-add a protocol blockdev node of LUKS header as above.
$ virsh qemu-monitor-command vm '{"execute":"blockdev-add",
> "arguments":{"node-name":"libvirt-2-storage", "driver":"file",
> "filename": "test-header.img" }}'
4. object-add the secret for decrypting the cipher stored in
LUKS header above
$ virsh qemu-monitor-command vm '{"execute":"object-add",
> "arguments":{"qom-type":"secret", "id":
> "libvirt-2-storage-secret0", "data":"abc123"}}'
5. block-add the raw-drived blockdev format node
$ virsh qemu-monitor-command vm '{"execute":"blockdev-add",
> "arguments":{"node-name":"libvirt-1-format", "driver":"raw",
> "file":"libvirt-1-storage"}}'
6. block-add the luks-drived blockdev to link the raw disk
with the LUKS header by specifying the field "header"
$ virsh qemu-monitor-command vm '{"execute":"blockdev-add",
> "arguments":{"node-name":"libvirt-2-format", "driver":"luks",
> "file":"libvirt-1-format", "header":"libvirt-2-storage",
> "key-secret":"libvirt-2-format-secret0"}}'
7. hot-plug the virtio-blk device finally
$ virsh qemu-monitor-command vm '{"execute":"device_add",
> "arguments": {"num-queues":"1", "driver":"virtio-blk-pci",
> "drive": "libvirt-2-format", "id":"virtio-disk2"}}'
Starting a VM with a LUKS volume with detached header is
somewhat similar to hot-plug in that both maintaining the
same json command while the starting VM changes the
"blockdev-add/device_add" parameters to "blockdev/device".
Signed-off-by: Hyman Huang <yong.huang@smartx.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
block/crypto.c | 21 +++++++++++++++++++--
crypto/block-luks.c | 11 +++++++----
include/crypto/block.h | 5 +++++
qapi/block-core.json | 5 ++++-
4 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/block/crypto.c b/block/crypto.c
index 921933a5e5..68656158e9 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -39,6 +39,7 @@ typedef struct BlockCrypto BlockCrypto;
struct BlockCrypto {
QCryptoBlock *block;
bool updating_keys;
+ BdrvChild *header; /* Reference to the detached LUKS header */
};
@@ -63,12 +64,14 @@ static int block_crypto_read_func(QCryptoBlock *block,
Error **errp)
{
BlockDriverState *bs = opaque;
+ BlockCrypto *crypto = bs->opaque;
ssize_t ret;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
- ret = bdrv_pread(bs->file, offset, buflen, buf, 0);
+ ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
+ offset, buflen, buf, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read encryption header");
return ret;
@@ -84,12 +87,14 @@ static int block_crypto_write_func(QCryptoBlock *block,
Error **errp)
{
BlockDriverState *bs = opaque;
+ BlockCrypto *crypto = bs->opaque;
ssize_t ret;
GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
- ret = bdrv_pwrite(bs->file, offset, buflen, buf, 0);
+ ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
+ offset, buflen, buf, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write encryption header");
return ret;
@@ -262,6 +267,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
int flags,
Error **errp)
{
+ ERRP_GUARD();
+
BlockCrypto *crypto = bs->opaque;
QemuOpts *opts = NULL;
int ret;
@@ -276,6 +283,13 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
return ret;
}
+ crypto->header = bdrv_open_child(NULL, options, "header", bs,
+ &child_of_bds, BDRV_CHILD_METADATA,
+ true, errp);
+ if (*errp != NULL) {
+ return -EINVAL;
+ }
+
GRAPH_RDLOCK_GUARD_MAINLOOP();
bs->supported_write_flags = BDRV_REQ_FUA &
@@ -299,6 +313,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
if (flags & BDRV_O_NO_IO) {
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
}
+ if (crypto->header != NULL) {
+ cflags |= QCRYPTO_BLOCK_OPEN_DETACHED;
+ }
crypto->block = qcrypto_block_open(open_opts, NULL,
block_crypto_read_func,
bs,
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index f0813d69b4..7e1235c213 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -468,12 +468,15 @@ qcrypto_block_luks_load_header(QCryptoBlock *block,
* Does basic sanity checks on the LUKS header
*/
static int
-qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
+qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks,
+ unsigned int flags,
+ Error **errp)
{
size_t i, j;
unsigned int header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+ bool detached = flags & QCRYPTO_BLOCK_OPEN_DETACHED;
if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
@@ -505,7 +508,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
return -1;
}
- if (luks->header.payload_offset_sector <
+ if (!detached && luks->header.payload_offset_sector <
DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET,
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) {
error_setg(errp, "LUKS payload is overlapping with the header");
@@ -554,7 +557,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
return -1;
}
- if (start1 + len1 > luks->header.payload_offset_sector) {
+ if (!detached && start1 + len1 > luks->header.payload_offset_sector) {
error_setg(errp,
"Keyslot %zu is overlapping with the encrypted payload",
i);
@@ -1214,7 +1217,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
goto fail;
}
- if (qcrypto_block_luks_check_header(luks, errp) < 0) {
+ if (qcrypto_block_luks_check_header(luks, flags, errp) < 0) {
goto fail;
}
diff --git a/include/crypto/block.h b/include/crypto/block.h
index 4f63a37872..d0d97f5d12 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -66,6 +66,7 @@ bool qcrypto_block_has_format(QCryptoBlockFormat format,
typedef enum {
QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0),
+ QCRYPTO_BLOCK_OPEN_DETACHED = (1 << 1),
} QCryptoBlockOpenFlags;
/**
@@ -95,6 +96,10 @@ typedef enum {
* metadata such as the payload offset. There will be
* no cipher or ivgen objects available.
*
+ * If @flags contains QCRYPTO_BLOCK_OPEN_DETACHED then
+ * the open process will be optimized to skip the LUKS
+ * payload overlap check.
+ *
* If any part of initializing the encryption context
* fails an error will be returned. This could be due
* to the volume being in the wrong format, a cipher
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 781c9bd03e..e7d4fb539b 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3365,11 +3365,14 @@
# decryption key (since 2.6). Mandatory except when doing a
# metadata-only probe of the image.
#
+# @header: block device holding a detached LUKS header. (since 9.0)
+#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsLUKS',
'base': 'BlockdevOptionsGenericFormat',
- 'data': { '*key-secret': 'str' } }
+ 'data': { '*key-secret': 'str',
+ '*header': 'BlockdevRef'} }
##
# @BlockdevOptionsGenericCOWFormat:
--
2.43.0
next prev parent reply other threads:[~2024-02-09 14:07 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-09 14:04 [PULL 00/17] Misc fixes patches Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 01/17] meson: sort C warning flags alphabetically Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 02/17] crypto: Introduce SM4 symmetric cipher algorithm Daniel P. Berrangé
2024-06-07 14:27 ` Peter Maydell
2024-02-09 14:04 ` [PULL 03/17] qemu_init: increase NOFILE soft limit on POSIX Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 04/17] ui: drop VNC feature _MASK constants Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 05/17] softmmu: remove obsolete comment about libvirt timeouts Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 06/17] scripts: drop comment about autogenerated CPU API file Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 07/17] docs: fix highlighting of CPU ABI header rows Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 08/17] docs: re-generate x86_64 ABI compatibility CSV Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 09/17] chardev: close QIOChannel before unref'ing Daniel P. Berrangé
2024-02-09 14:04 ` [PULL 10/17] io: add trace event when cancelling TLS handshake Daniel P. Berrangé
2024-02-09 14:04 ` Daniel P. Berrangé [this message]
2024-02-09 14:05 ` [PULL 12/17] qapi: Make parameter 'file' optional for BlockdevCreateOptionsLUKS Daniel P. Berrangé
2024-02-09 14:05 ` [PULL 13/17] crypto: Modify the qcrypto_block_create to support creation flags Daniel P. Berrangé
2024-02-09 14:05 ` [PULL 14/17] block: Support detached LUKS header creation using blockdev-create Daniel P. Berrangé
2024-02-09 14:05 ` [PULL 15/17] block: Support detached LUKS header creation using qemu-img Daniel P. Berrangé
2024-02-09 14:05 ` [PULL 16/17] crypto: Introduce 'detached-header' field in QCryptoBlockInfoLUKS Daniel P. Berrangé
2024-02-09 14:05 ` [PULL 17/17] tests: Add case for LUKS volume with detached header Daniel P. Berrangé
2024-02-12 18:31 ` [PULL 00/17] Misc fixes patches Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240209140505.2536635-12-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=armbru@redhat.com \
--cc=crosa@redhat.com \
--cc=eblake@redhat.com \
--cc=eduardo@habkost.net \
--cc=hreitz@redhat.com \
--cc=jsnow@redhat.com \
--cc=kraxel@redhat.com \
--cc=kwolf@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=sw@weilnetz.de \
--cc=thuth@redhat.com \
--cc=yong.huang@smartx.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.