storage: add support for qcow2 LUKS encryption

The storage driver was wired up to support creating raw volumes in LUKS
format, but was never adapted to support LUKS-in-qcow2. This is trivial
as it merely requires the encryption properties to be prefixed with
the "encrypt." prefix, and "encrypt.format=luks" when creating the
volume.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2020-09-15 16:30:37 +01:00
parent 093ee8b3e2
commit ecfc4094d8
12 changed files with 234 additions and 22 deletions

View File

@ -707,7 +707,7 @@ storageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr encinfo,
virStorageFileFormatTypeToString(info->backingFormat)); virStorageFileFormatTypeToString(info->backingFormat));
if (encinfo) if (encinfo)
virQEMUBuildQemuImgKeySecretOpts(&buf, encinfo, info->secretAlias); virQEMUBuildQemuImgKeySecretOpts(&buf, info->format, encinfo, info->secretAlias);
if (info->preallocate) { if (info->preallocate) {
if (info->size_arg > info->allocation) if (info->size_arg > info->allocation)
@ -761,7 +761,8 @@ storageBackendCreateQemuImgCheckEncryption(int format,
{ {
virStorageEncryptionPtr enc = vol->target.encryption; virStorageEncryptionPtr enc = vol->target.encryption;
if (format == VIR_STORAGE_FILE_RAW) { if (format == VIR_STORAGE_FILE_RAW ||
format == VIR_STORAGE_FILE_QCOW2) {
if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported volume encryption format %d"), _("unsupported volume encryption format %d"),
@ -927,21 +928,34 @@ storageBackendCreateQemuImgSecretObject(virCommandPtr cmd,
} }
/* Add a --image-opts to the qemu-img resize command line: /* Add a --image-opts to the qemu-img resize command line for use
* with encryption:
* --image-opts driver=luks,file.filename=$volpath,key-secret=$secretAlias * --image-opts driver=luks,file.filename=$volpath,key-secret=$secretAlias
* or
* --image-opts driver=qcow2,file.filename=$volpath,encrypt.key-secret=$secretAlias
* *
* NB: format=raw is assumed
*/ */
static int static int
storageBackendResizeQemuImgImageOpts(virCommandPtr cmd, storageBackendResizeQemuImgImageOpts(virCommandPtr cmd,
int format,
const char *path, const char *path,
const char *secretAlias) const char *secretAlias)
{ {
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
g_autofree char *commandStr = NULL; g_autofree char *commandStr = NULL;
const char *encprefix;
const char *driver;
virBufferAsprintf(&buf, "driver=luks,key-secret=%s,file.filename=", if (format == VIR_STORAGE_FILE_QCOW2) {
secretAlias); driver = "qcow2";
encprefix = "encrypt.";
} else {
driver = "luks";
encprefix = "";
}
virBufferAsprintf(&buf, "driver=%s,%skey-secret=%s,file.filename=",
driver, encprefix, secretAlias);
virQEMUBuildBufferEscapeComma(&buf, path); virQEMUBuildBufferEscapeComma(&buf, path);
commandStr = virBufferContentAndReset(&buf); commandStr = virBufferContentAndReset(&buf);
@ -1006,6 +1020,16 @@ virStorageBackendCreateQemuImgSetInfo(virStoragePoolObjPtr pool,
return -1; return -1;
} }
} }
if (inputvol && inputvol->target.format == VIR_STORAGE_FILE_RAW &&
inputvol->target.encryption) {
if (inputvol->target.encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
info->inputType = "luks";
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Only luks encryption is supported for raw files"));
return -1;
}
}
if (inputvol && if (inputvol &&
storageBackendCreateQemuImgSetInput(inputvol, convertStep, info) < 0) storageBackendCreateQemuImgSetInput(inputvol, convertStep, info) < 0)
@ -1056,6 +1080,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virStoragePoolObjPtr pool,
virStorageEncryptionPtr inputenc = inputvol ? inputvol->target.encryption : NULL; virStorageEncryptionPtr inputenc = inputvol ? inputvol->target.encryption : NULL;
virStorageEncryptionInfoDefPtr encinfo = NULL; virStorageEncryptionInfoDefPtr encinfo = NULL;
g_autofree char *inputSecretAlias = NULL; g_autofree char *inputSecretAlias = NULL;
const char *encprefix;
const char *inputencprefix;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL);
@ -1134,24 +1160,34 @@ virStorageBackendCreateQemuImgCmdFromVol(virStoragePoolObjPtr pool,
virCommandAddArgFormat(cmd, "%lluK", info.size_arg); virCommandAddArgFormat(cmd, "%lluK", info.size_arg);
} else { } else {
/* source */ /* source */
if (inputenc) if (inputenc) {
if (inputvol->target.format == VIR_STORAGE_FILE_QCOW2)
inputencprefix = "encrypt.";
else
inputencprefix = "";
virCommandAddArgFormat(cmd, virCommandAddArgFormat(cmd,
"driver=luks,file.filename=%s,key-secret=%s", "driver=%s,file.filename=%s,%skey-secret=%s",
info.inputPath, inputSecretAlias); info.inputType, info.inputPath, inputencprefix, inputSecretAlias);
else } else {
virCommandAddArgFormat(cmd, "driver=%s,file.filename=%s", virCommandAddArgFormat(cmd, "driver=%s,file.filename=%s",
info.inputType ? info.inputType : "raw", info.inputType ? info.inputType : "raw",
info.inputPath); info.inputPath);
}
/* dest */ /* dest */
if (enc) if (enc) {
if (vol->target.format == VIR_STORAGE_FILE_QCOW2)
encprefix = "encrypt.";
else
encprefix = "";
virCommandAddArgFormat(cmd, virCommandAddArgFormat(cmd,
"driver=%s,file.filename=%s,key-secret=%s", "driver=%s,file.filename=%s,%skey-secret=%s",
info.type, info.path, info.secretAlias); info.type, info.path, encprefix, info.secretAlias);
else } else {
virCommandAddArgFormat(cmd, "driver=%s,file.filename=%s", virCommandAddArgFormat(cmd, "driver=%s,file.filename=%s",
info.type, info.path); info.type, info.path);
}
} }
VIR_FREE(info.secretAlias); VIR_FREE(info.secretAlias);
@ -2276,7 +2312,9 @@ storageBackendResizeQemuImg(virStoragePoolObjPtr pool,
secretAlias) < 0) secretAlias) < 0)
goto cleanup; goto cleanup;
if (storageBackendResizeQemuImgImageOpts(cmd, vol->target.path, if (storageBackendResizeQemuImgImageOpts(cmd,
vol->target.format,
vol->target.path,
secretAlias) < 0) secretAlias) < 0)
goto cleanup; goto cleanup;
} }

View File

@ -28,6 +28,7 @@
#include "virqemu.h" #include "virqemu.h"
#include "virstring.h" #include "virstring.h"
#include "viralloc.h" #include "viralloc.h"
#include "virstoragefile.h"
#define VIR_FROM_THIS VIR_FROM_NONE #define VIR_FROM_THIS VIR_FROM_NONE
@ -407,36 +408,46 @@ virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str)
*/ */
void void
virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf, virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf,
int format,
virStorageEncryptionInfoDefPtr encinfo, virStorageEncryptionInfoDefPtr encinfo,
const char *alias) const char *alias)
{ {
virBufferAsprintf(buf, "key-secret=%s,", alias); const char *encprefix;
if (format == VIR_STORAGE_FILE_QCOW2) {
virBufferAddLit(buf, "encrypt.format=luks,");
encprefix = "encrypt.";
} else {
encprefix = "";
}
virBufferAsprintf(buf, "%skey-secret=%s,", encprefix, alias);
if (!encinfo->cipher_name) if (!encinfo->cipher_name)
return; return;
virBufferAddLit(buf, "cipher-alg="); virBufferAsprintf(buf, "%scipher-alg=", encprefix);
virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_name); virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_name);
virBufferAsprintf(buf, "-%u,", encinfo->cipher_size); virBufferAsprintf(buf, "-%u,", encinfo->cipher_size);
if (encinfo->cipher_mode) { if (encinfo->cipher_mode) {
virBufferAddLit(buf, "cipher-mode="); virBufferAsprintf(buf, "%scipher-mode=", encprefix);
virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_mode); virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_mode);
virBufferAddLit(buf, ","); virBufferAddLit(buf, ",");
} }
if (encinfo->cipher_hash) { if (encinfo->cipher_hash) {
virBufferAddLit(buf, "hash-alg="); virBufferAsprintf(buf, "%shash-alg=", encprefix);
virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_hash); virQEMUBuildBufferEscapeComma(buf, encinfo->cipher_hash);
virBufferAddLit(buf, ","); virBufferAddLit(buf, ",");
} }
if (!encinfo->ivgen_name) if (!encinfo->ivgen_name)
return; return;
virBufferAddLit(buf, "ivgen-alg="); virBufferAsprintf(buf, "%sivgen-alg=", encprefix);
virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_name); virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_name);
virBufferAddLit(buf, ","); virBufferAddLit(buf, ",");
if (encinfo->ivgen_hash) { if (encinfo->ivgen_hash) {
virBufferAddLit(buf, "ivgen-hash-alg="); virBufferAsprintf(buf, "%sivgen-hash-alg=", encprefix);
virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_hash); virQEMUBuildBufferEscapeComma(buf, encinfo->ivgen_hash);
virBufferAddLit(buf, ","); virBufferAddLit(buf, ",");
} }

View File

@ -60,6 +60,7 @@ char *virQEMUBuildDriveCommandlineFromJSON(virJSONValuePtr src);
void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str); void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str);
void virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf, void virQEMUBuildQemuImgKeySecretOpts(virBufferPtr buf,
int format,
virStorageEncryptionInfoDefPtr enc, virStorageEncryptionInfoDefPtr enc,
const char *alias) const char *alias)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);

View File

@ -0,0 +1,18 @@
qemu-img \
create \
-f qcow2 \
--object secret,id=OtherDemoLuks.img_encrypt0,file=/path/to/secretFile \
-o encrypt.format=luks,encrypt.key-secret=OtherDemoLuks.img_encrypt0,\
compat=0.10 /var/lib/libvirt/images/OtherDemoLuks.img 5242880K
qemu-img \
convert \
--image-opts \
-n \
--target-image-opts \
--object secret,id=OtherDemoLuks.img_encrypt0,file=/path/to/secretFile \
--object secret,id=OtherDemoLuksConvert.img_encrypt0,\
file=/path/to/inputSecretFile driver=qcow2,\
file.filename=/var/lib/libvirt/images/OtherDemoLuksConvert.img,\
encrypt.key-secret=OtherDemoLuksConvert.img_encrypt0 driver=qcow2,\
file.filename=/var/lib/libvirt/images/OtherDemoLuks.img,\
encrypt.key-secret=OtherDemoLuks.img_encrypt0

View File

@ -0,0 +1,14 @@
qemu-img \
create \
-f qcow2 \
-o compat=0.10 /var/lib/libvirt/images/sparse-qcow2.img 1073741824K
qemu-img \
convert \
--image-opts \
-n \
--target-image-opts \
--object secret,id=OtherDemoLuksConvert.img_encrypt0,\
file=/path/to/inputSecretFile driver=qcow2,\
file.filename=/var/lib/libvirt/images/OtherDemoLuksConvert.img,\
encrypt.key-secret=OtherDemoLuksConvert.img_encrypt0 driver=qcow2,\
file.filename=/var/lib/libvirt/images/sparse-qcow2.img

View File

@ -0,0 +1,13 @@
qemu-img \
create \
-f raw /var/lib/libvirt/images/sparse.img 1073741824K
qemu-img \
convert \
--image-opts \
-n \
--target-image-opts \
--object secret,id=OtherDemoLuksConvert.img_encrypt0,\
file=/path/to/inputSecretFile driver=qcow2,\
file.filename=/var/lib/libvirt/images/OtherDemoLuksConvert.img,\
encrypt.key-secret=OtherDemoLuksConvert.img_encrypt0 driver=raw,\
file.filename=/var/lib/libvirt/images/sparse.img

View File

@ -0,0 +1,8 @@
qemu-img \
create \
-f qcow2 \
-b /dev/null \
--object secret,id=OtherDemoLuks.img_encrypt0,file=/path/to/secretFile \
-o backing_fmt=raw,encrypt.format=luks,\
encrypt.key-secret=OtherDemoLuks.img_encrypt0,\
compat=0.10 /var/lib/libvirt/images/OtherDemoLuks.img 5242880K

View File

@ -255,6 +255,9 @@ mymain(void)
DO_TEST("pool-dir", "vol-luks-cipher", DO_TEST("pool-dir", "vol-luks-cipher",
NULL, NULL, NULL, NULL,
"luks-cipher", 0); "luks-cipher", 0);
DO_TEST("pool-dir", "vol-qcow2-luks",
NULL, NULL,
"qcow2-luks", 0);
DO_TEST("pool-dir", "vol-luks-convert", DO_TEST("pool-dir", "vol-luks-convert",
"pool-dir", "vol-file", "pool-dir", "vol-file",
@ -276,6 +279,18 @@ mymain(void)
"pool-dir", "vol-luks-convert", "pool-dir", "vol-luks-convert",
"luks-convert-encrypt2fileqcow2", 0); "luks-convert-encrypt2fileqcow2", 0);
DO_TEST("pool-dir", "vol-qcow2-luks",
"pool-dir", "vol-qcow2-luks-convert",
"qcow2-luks-convert-encrypt", 0);
DO_TEST("pool-dir", "vol-file",
"pool-dir", "vol-qcow2-luks-convert",
"qcow2-luks-convert-encrypt2fileraw", 0);
DO_TEST("pool-dir", "vol-file-qcow2",
"pool-dir", "vol-qcow2-luks-convert",
"qcow2-luks-convert-encrypt2fileqcow2", 0);
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
} }

View File

@ -0,0 +1,31 @@
<volume>
<name>OtherDemoLuksConvert.img</name>
<key>/var/lib/libvirt/images/OtherDemoLuksConvert.img</key>
<source>
</source>
<capacity unit="G">5</capacity>
<allocation>294912</allocation>
<target>
<path>/var/lib/libvirt/images/OtherDemoLuksConvert.img</path>
<format type='qcow2'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
<encryption format='luks'>
<secret type='passphrase' uuid='e78d4b51-a2af-485f-b0f5-afca709a80f4'/>
</encryption>
</target>
<backingStore>
<path>/dev/null</path>
<format type='raw'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
</backingStore>
</volume>

View File

@ -0,0 +1,31 @@
<volume>
<name>OtherDemoLuks.img</name>
<key>/var/lib/libvirt/images/OtherDemoLuks.img</key>
<source>
</source>
<capacity unit="G">5</capacity>
<allocation>294912</allocation>
<target>
<path>/var/lib/libvirt/images/OtherDemoLuks.img</path>
<format type='qcow2'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
<encryption format='luks'>
<secret type='passphrase' uuid='e78d4b51-a2af-485f-b0f5-afca709a80f4'/>
</encryption>
</target>
<backingStore>
<path>/dev/null</path>
<format type='raw'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
</backingStore>
</volume>

View File

@ -0,0 +1,31 @@
<volume type='file'>
<name>OtherDemoLuks.img</name>
<key>/var/lib/libvirt/images/OtherDemoLuks.img</key>
<source>
</source>
<capacity unit='bytes'>5368709120</capacity>
<allocation unit='bytes'>294912</allocation>
<target>
<path>/var/lib/libvirt/images/OtherDemoLuks.img</path>
<format type='qcow2'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
<encryption format='luks'>
<secret type='passphrase' uuid='e78d4b51-a2af-485f-b0f5-afca709a80f4'/>
</encryption>
</target>
<backingStore>
<path>/dev/null</path>
<format type='raw'/>
<permissions>
<mode>0644</mode>
<owner>0</owner>
<group>0</group>
<label>unconfined_u:object_r:virt_image_t:s0</label>
</permissions>
</backingStore>
</volume>

View File

@ -87,6 +87,7 @@ mymain(void)
DO_TEST("pool-dir", "vol-qcow2-0.10-lazy"); DO_TEST("pool-dir", "vol-qcow2-0.10-lazy");
DO_TEST("pool-dir", "vol-qcow2-nobacking"); DO_TEST("pool-dir", "vol-qcow2-nobacking");
DO_TEST("pool-dir", "vol-qcow2-encryption"); DO_TEST("pool-dir", "vol-qcow2-encryption");
DO_TEST("pool-dir", "vol-qcow2-luks");
DO_TEST("pool-dir", "vol-luks"); DO_TEST("pool-dir", "vol-luks");
DO_TEST("pool-dir", "vol-luks-cipher"); DO_TEST("pool-dir", "vol-luks-cipher");
DO_TEST("pool-disk", "vol-partition"); DO_TEST("pool-disk", "vol-partition");