mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
encryption: Add <cipher> and <ivgen> to encryption
For a luks device, allow the configuration of a specific cipher to be used for encrypting the volume. Signed-off-by: John Ferlan <jferlan@redhat.com>
This commit is contained in:
parent
9bbf0d7e64
commit
2552fec248
@ -68,6 +68,60 @@
|
||||
be used as the passphrase to decrypt the volume.
|
||||
<span class="since">Since 2.1.0</span>.
|
||||
</p>
|
||||
<p>
|
||||
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 that purpose. It is hypervisor
|
||||
dependent as to which algorithms are supported. The default algorithm
|
||||
used by the storage driver backend when using qemu-img to create
|
||||
the volume is 'aes-256-cbc' using 'essiv' for initialization vector
|
||||
generation and 'sha256' hash algorithm for both the cipher and the
|
||||
initialization vector generation.
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>cipher</code></dt>
|
||||
<dd>This element describes the cipher algorithm to be used to either
|
||||
encrypt or decrypt the luks volume. This element has the following
|
||||
attributes:
|
||||
<dl>
|
||||
<dt><code>name</code></dt>
|
||||
<dd>The name of the cipher algorithm used for data encryption,
|
||||
such as 'aes', 'des', 'cast5', 'serpent', 'twofish', etc.
|
||||
Support of the specific algorithm is storage driver
|
||||
implementation dependent.</dd>
|
||||
<dt><code>size</code></dt>
|
||||
<dd>The size of the cipher in bits, such as '256', '192', '128',
|
||||
etc. Support of the specific size for a specific cipher is
|
||||
hypervisor dependent.</dd>
|
||||
<dt><code>mode</code></dt>
|
||||
<dd>An optional cipher algorithm mode such as 'cbc', 'xts',
|
||||
'ecb', etc. Support of the specific cipher mode is
|
||||
hypervisor dependent.</dd>
|
||||
<dt><code>hash</code></dt>
|
||||
<dd>An optional master key hash algorithm such as 'md5', 'sha1',
|
||||
'sha256', etc. Support of the specific hash algorithm is
|
||||
hypervisor dependent.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code>ivgen</code></dt>
|
||||
<dd>This optional element describes the initialization vector
|
||||
generation algorithm used in conjunction with the
|
||||
<code>cipher</code>. If the <code>cipher</code> is not provided,
|
||||
then an error will be generated by the parser.
|
||||
<dl>
|
||||
<dt><code>name</code></dt>
|
||||
<dd>The name of the algorithm, such as 'plain', 'plain64',
|
||||
'essiv', etc. Support of the specific algorithm is hypervisor
|
||||
dependent.</dd>
|
||||
<dt><code>hash</code></dt>
|
||||
<dd>An optional hash algorithm such as 'md5', 'sha1', 'sha256',
|
||||
etc. Support of the specific ivgen hash algorithm is hypervisor
|
||||
dependent.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<h2><a name="example">Examples</a></h2>
|
||||
|
||||
@ -81,9 +135,12 @@
|
||||
</encryption></pre>
|
||||
|
||||
<p>
|
||||
Here is a simple example, specifying use of the <code>luks</code> format
|
||||
where it's assumed that a <code>secret</code> has been defined using a
|
||||
<code>usage</code> element with a <code>id</code> of "luks_example":
|
||||
Assuming a <a href="formatsecret.html#luksUsageType">
|
||||
<code>luks secret</code></a> is already defined using a
|
||||
<code>usage</code> element with an <code>name</code> of "luks_example",
|
||||
a simple example specifying use of the <code>luks</code> format
|
||||
for either volume creation without a specific cipher being defined or
|
||||
as part of a domain volume definition:
|
||||
</p>
|
||||
<pre>
|
||||
<encryption format='luks'>
|
||||
@ -91,5 +148,25 @@
|
||||
</encryption>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Here is an example, specifying use of the <code>luks</code> format for
|
||||
a specific cipher algorihm for volume creation:
|
||||
</p>
|
||||
<pre>
|
||||
<volume>
|
||||
<name>twofish.luks</name>
|
||||
<capacity unit='G'>5</capacity>
|
||||
<target>
|
||||
<path>/var/lib/libvirt/images/demo.luks</path>
|
||||
<format type='luks'/>
|
||||
<encryption format='luks'>
|
||||
<secret type='passphrase' usage='luks_example'/>
|
||||
<cipher name='twofish' size='256' mode='cbc' hash='sha256'/>
|
||||
<ivgen name='plain64' hash='sha256'/>
|
||||
</encryption>
|
||||
</target>
|
||||
</volume>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -15,9 +15,19 @@
|
||||
<value>luks</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<zeroOrMore>
|
||||
<ref name='secret'/>
|
||||
</zeroOrMore>
|
||||
<interleave>
|
||||
<zeroOrMore>
|
||||
<ref name='secret'/>
|
||||
</zeroOrMore>
|
||||
<optional>
|
||||
<element name='cipher'>
|
||||
<ref name='keycipher'/>
|
||||
</element>
|
||||
<element name='ivgen'>
|
||||
<ref name='keyivgen'/>
|
||||
</element>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
@ -136,4 +146,32 @@
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
<define name='keycipher'>
|
||||
<attribute name='name'>
|
||||
<text/>
|
||||
</attribute>
|
||||
<attribute name='size'>
|
||||
<ref name="unsignedInt"/>
|
||||
</attribute>
|
||||
<optional>
|
||||
<attribute name='mode'>
|
||||
<text/>
|
||||
</attribute>
|
||||
<attribute name='hash'>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
<define name='keyivgen'>
|
||||
<attribute name='name'>
|
||||
<text/>
|
||||
</attribute>
|
||||
<optional>
|
||||
<attribute name='hash'>
|
||||
<text/>
|
||||
</attribute>
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
</grammar>
|
||||
|
@ -7802,6 +7802,17 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
|
||||
def->startupPolicy = val;
|
||||
}
|
||||
|
||||
if (encryption) {
|
||||
if (encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
|
||||
encryption->encinfo.cipher_name) {
|
||||
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("supplying the <cipher> for a domain is "
|
||||
"unnecessary"));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
def->dst = target;
|
||||
target = NULL;
|
||||
def->src->auth = authdef;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "viruuid.h"
|
||||
#include "virfile.h"
|
||||
#include "virsecret.h"
|
||||
#include "virstring.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
||||
|
||||
@ -45,6 +46,17 @@ VIR_ENUM_IMPL(virStorageEncryptionFormat,
|
||||
VIR_STORAGE_ENCRYPTION_FORMAT_LAST,
|
||||
"default", "qcow", "luks")
|
||||
|
||||
static void
|
||||
virStorageEncryptionInfoDefFree(virStorageEncryptionInfoDefPtr def)
|
||||
{
|
||||
VIR_FREE(def->cipher_name);
|
||||
VIR_FREE(def->cipher_mode);
|
||||
VIR_FREE(def->cipher_hash);
|
||||
VIR_FREE(def->ivgen_name);
|
||||
VIR_FREE(def->ivgen_hash);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virStorageEncryptionSecretFree(virStorageEncryptionSecretPtr secret)
|
||||
{
|
||||
@ -63,6 +75,7 @@ virStorageEncryptionFree(virStorageEncryptionPtr enc)
|
||||
|
||||
for (i = 0; i < enc->nsecrets; i++)
|
||||
virStorageEncryptionSecretFree(enc->secrets[i]);
|
||||
virStorageEncryptionInfoDefFree(&enc->encinfo);
|
||||
VIR_FREE(enc->secrets);
|
||||
VIR_FREE(enc);
|
||||
}
|
||||
@ -80,6 +93,23 @@ virStorageEncryptionSecretCopy(const virStorageEncryptionSecret *src)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageEncryptionInfoDefCopy(const virStorageEncryptionInfoDef *src,
|
||||
virStorageEncryptionInfoDefPtr dst)
|
||||
{
|
||||
dst->cipher_size = src->cipher_size;
|
||||
if (VIR_STRDUP(dst->cipher_name, src->cipher_name) < 0 ||
|
||||
VIR_STRDUP(dst->cipher_mode, src->cipher_mode) < 0 ||
|
||||
VIR_STRDUP(dst->cipher_hash, src->cipher_hash) < 0 ||
|
||||
VIR_STRDUP(dst->ivgen_name, src->ivgen_name) < 0 ||
|
||||
VIR_STRDUP(dst->ivgen_hash, src->ivgen_hash) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
virStorageEncryptionPtr
|
||||
virStorageEncryptionCopy(const virStorageEncryption *src)
|
||||
{
|
||||
@ -100,6 +130,9 @@ virStorageEncryptionCopy(const virStorageEncryption *src)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (virStorageEncryptionInfoDefCopy(&src->encinfo, &ret->encinfo) < 0)
|
||||
goto error;
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
@ -153,6 +186,61 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageEncryptionInfoParseCipher(xmlNodePtr info_node,
|
||||
virStorageEncryptionInfoDefPtr info)
|
||||
{
|
||||
int ret = -1;
|
||||
char *size_str = NULL;
|
||||
|
||||
if (!(info->cipher_name = virXMLPropString(info_node, "name"))) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("cipher info missing 'name' attribute"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((size_str = virXMLPropString(info_node, "size")) &&
|
||||
virStrToLong_uip(size_str, NULL, 10, &info->cipher_size) < 0) {
|
||||
virReportError(VIR_ERR_XML_ERROR,
|
||||
_("cannot parse cipher size: '%s'"),
|
||||
size_str);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!size_str) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("cipher info missing 'size' attribute"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
info->cipher_mode = virXMLPropString(info_node, "mode");
|
||||
info->cipher_hash = virXMLPropString(info_node, "hash");
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(size_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virStorageEncryptionInfoParseIvgen(xmlNodePtr info_node,
|
||||
virStorageEncryptionInfoDefPtr info)
|
||||
{
|
||||
if (!(info->ivgen_name = virXMLPropString(info_node, "name"))) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("missing ivgen info name string"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
info->ivgen_hash = virXMLPropString(info_node, "hash");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static virStorageEncryptionPtr
|
||||
virStorageEncryptionParseXML(xmlXPathContextPtr ctxt)
|
||||
{
|
||||
@ -196,6 +284,28 @@ virStorageEncryptionParseXML(xmlXPathContextPtr ctxt)
|
||||
VIR_FREE(nodes);
|
||||
}
|
||||
|
||||
if (ret->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
|
||||
xmlNodePtr tmpnode;
|
||||
|
||||
if ((tmpnode = virXPathNode("./cipher[1]", ctxt))) {
|
||||
if (virStorageEncryptionInfoParseCipher(tmpnode, &ret->encinfo) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((tmpnode = virXPathNode("./ivgen[1]", ctxt))) {
|
||||
/* If no cipher node, then fail */
|
||||
if (!ret->encinfo.cipher_name) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("ivgen element found, but cipher is missing"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virStorageEncryptionInfoParseIvgen(tmpnode, &ret->encinfo) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
|
||||
cleanup:
|
||||
@ -250,6 +360,28 @@ virStorageEncryptionSecretFormat(virBufferPtr buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
virStorageEncryptionInfoDefFormat(virBufferPtr buf,
|
||||
const virStorageEncryptionInfoDef *enc)
|
||||
{
|
||||
virBufferEscapeString(buf, "<cipher name='%s'", enc->cipher_name);
|
||||
virBufferAsprintf(buf, " size='%u'", enc->cipher_size);
|
||||
if (enc->cipher_mode)
|
||||
virBufferEscapeString(buf, " mode='%s'", enc->cipher_mode);
|
||||
if (enc->cipher_hash)
|
||||
virBufferEscapeString(buf, " hash='%s'", enc->cipher_hash);
|
||||
virBufferAddLit(buf, "/>\n");
|
||||
|
||||
if (enc->ivgen_name) {
|
||||
virBufferEscapeString(buf, "<ivgen name='%s'", enc->ivgen_name);
|
||||
if (enc->ivgen_hash)
|
||||
virBufferEscapeString(buf, " hash='%s'", enc->ivgen_hash);
|
||||
virBufferAddLit(buf, "/>\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
virStorageEncryptionFormat(virBufferPtr buf,
|
||||
virStorageEncryptionPtr enc)
|
||||
@ -270,6 +402,10 @@ virStorageEncryptionFormat(virBufferPtr buf,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
|
||||
enc->encinfo.cipher_name)
|
||||
virStorageEncryptionInfoDefFormat(buf, &enc->encinfo);
|
||||
|
||||
virBufferAdjustIndent(buf, -2);
|
||||
virBufferAddLit(buf, "</encryption>\n");
|
||||
|
||||
|
@ -44,6 +44,18 @@ struct _virStorageEncryptionSecret {
|
||||
virSecretLookupTypeDef seclookupdef;
|
||||
};
|
||||
|
||||
/* It's possible to dictate the cipher and if necessary iv */
|
||||
typedef struct _virStorageEncryptionInfoDef virStorageEncryptionInfoDef;
|
||||
typedef virStorageEncryptionInfoDef *virStorageEncryptionInfoDefPtr;
|
||||
struct _virStorageEncryptionInfoDef {
|
||||
unsigned int cipher_size;
|
||||
char *cipher_name;
|
||||
char *cipher_mode;
|
||||
char *cipher_hash;
|
||||
char *ivgen_name;
|
||||
char *ivgen_hash;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
/* "default" is only valid for volume creation */
|
||||
VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT = 0,
|
||||
@ -61,6 +73,8 @@ struct _virStorageEncryption {
|
||||
|
||||
size_t nsecrets;
|
||||
virStorageEncryptionSecretPtr *secrets;
|
||||
|
||||
virStorageEncryptionInfoDef encinfo;
|
||||
};
|
||||
|
||||
virStorageEncryptionPtr virStorageEncryptionCopy(const virStorageEncryption *src)
|
||||
|
45
tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml
Normal file
45
tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<domain type='qemu'>
|
||||
<name>encryptdisk</name>
|
||||
<uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid>
|
||||
<memory unit='KiB'>1048576</memory>
|
||||
<currentMemory unit='KiB'>524288</currentMemory>
|
||||
<vcpu placement='static'>1</vcpu>
|
||||
<os>
|
||||
<type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<clock offset='utc'/>
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>restart</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu</emulator>
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='luks'/>
|
||||
<source file='/storage/guest_disks/encryptdisk'/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
<encryption format='luks'>
|
||||
<secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/>
|
||||
</encryption>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
|
||||
</disk>
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='luks'/>
|
||||
<source file='/storage/guest_disks/encryptdisk2'/>
|
||||
<target dev='vdb' bus='virtio'/>
|
||||
<encryption format='luks'>
|
||||
<secret type='passphrase' usage='mycluster_myname'/>
|
||||
</encryption>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
|
||||
</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>
|
1
tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml
Symbolic link
1
tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml
Symbolic link
@ -0,0 +1 @@
|
||||
../qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml
|
@ -503,6 +503,7 @@ mymain(void)
|
||||
DO_TEST("encrypted-disk");
|
||||
DO_TEST("encrypted-disk-usage");
|
||||
DO_TEST("luks-disks");
|
||||
DO_TEST("luks-disk-cipher");
|
||||
DO_TEST("memtune");
|
||||
DO_TEST("memtune-unlimited");
|
||||
DO_TEST("blkiotune");
|
||||
|
23
tests/storagevolxml2xmlin/vol-luks-cipher.xml
Normal file
23
tests/storagevolxml2xmlin/vol-luks-cipher.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<volume>
|
||||
<name>LuksDemo.img</name>
|
||||
<key>/var/lib/libvirt/images/LuksDemo.img</key>
|
||||
<source>
|
||||
</source>
|
||||
<capacity unit="G">5</capacity>
|
||||
<allocation>294912</allocation>
|
||||
<target>
|
||||
<path>/var/lib/libvirt/images/LuksDemo.img</path>
|
||||
<format type='luks'/>
|
||||
<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' usage='mumblyfratz'/>
|
||||
<cipher name='serpent' size='256' mode='cbc' hash='sha256'/>
|
||||
<ivgen name='plain64' hash='sha256'/>
|
||||
</encryption>
|
||||
</target>
|
||||
</volume>
|
23
tests/storagevolxml2xmlout/vol-luks-cipher.xml
Normal file
23
tests/storagevolxml2xmlout/vol-luks-cipher.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<volume type='file'>
|
||||
<name>LuksDemo.img</name>
|
||||
<key>/var/lib/libvirt/images/LuksDemo.img</key>
|
||||
<source>
|
||||
</source>
|
||||
<capacity unit='bytes'>5368709120</capacity>
|
||||
<allocation unit='bytes'>294912</allocation>
|
||||
<target>
|
||||
<path>/var/lib/libvirt/images/LuksDemo.img</path>
|
||||
<format type='luks'/>
|
||||
<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' usage='mumblyfratz'/>
|
||||
<cipher name='serpent' size='256' mode='cbc' hash='sha256'/>
|
||||
<ivgen name='plain64' hash='sha256'/>
|
||||
</encryption>
|
||||
</target>
|
||||
</volume>
|
@ -106,6 +106,7 @@ mymain(void)
|
||||
DO_TEST("pool-dir", "vol-qcow2-0.10-lazy");
|
||||
DO_TEST("pool-dir", "vol-qcow2-nobacking");
|
||||
DO_TEST("pool-dir", "vol-luks");
|
||||
DO_TEST("pool-dir", "vol-luks-cipher");
|
||||
DO_TEST("pool-disk", "vol-partition");
|
||||
DO_TEST("pool-logical", "vol-logical");
|
||||
DO_TEST("pool-logical", "vol-logical-backing");
|
||||
|
Loading…
Reference in New Issue
Block a user