storage: Add support to create a luks volume

Partially resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1301021

If the volume xml was looking to create a luks volume take the necessary
steps in order to make that happen.

The processing will be:
 1. create a temporary file (virStorageBackendCreateQemuImgSecretPath)
   1a. use the storage driver state dir path that uses the pool and
       volume name as a base.

 2. create a secret object (virStorageBackendCreateQemuImgSecretObject)
   2a. use an alias combinding the volume name and "_luks0"
   2b. add the file to the object

 3. create/add luks options to the commandline (virQEMUBuildLuksOpts)
   3a. at the very least a "key-secret=%s" using the secret object alias
   3b. if found in the XML the various "cipher" and "ivgen" options

Signed-off-by: John Ferlan <jferlan@redhat.com>
This commit is contained in:
John Ferlan 2016-06-02 11:33:47 -04:00
parent bd93ba64fd
commit 5e46d7d6b6
6 changed files with 271 additions and 14 deletions

View File

@ -2197,6 +2197,7 @@ virProcessWait;
# util/virqemu.h # util/virqemu.h
virQEMUBuildBufferEscapeComma; virQEMUBuildBufferEscapeComma;
virQEMUBuildLuksOpts;
virQEMUBuildObjectCommandlineFromJSON; virQEMUBuildObjectCommandlineFromJSON;

View File

@ -55,11 +55,14 @@
#include "viralloc.h" #include "viralloc.h"
#include "internal.h" #include "internal.h"
#include "secret_conf.h" #include "secret_conf.h"
#include "secret_util.h"
#include "viruuid.h" #include "viruuid.h"
#include "virstoragefile.h" #include "virstoragefile.h"
#include "storage_backend.h" #include "storage_backend.h"
#include "virlog.h" #include "virlog.h"
#include "virfile.h" #include "virfile.h"
#include "virjson.h"
#include "virqemu.h"
#include "stat-time.h" #include "stat-time.h"
#include "virstring.h" #include "virstring.h"
#include "virxml.h" #include "virxml.h"
@ -907,6 +910,7 @@ virStorageBackendQemuImgSupportsCompat(const char *qemuimg)
return ret; return ret;
} }
static int static int
virStorageBackendQEMUImgBackingFormat(const char *qemuimg) virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
{ {
@ -925,6 +929,10 @@ virStorageBackendQEMUImgBackingFormat(const char *qemuimg)
return ret; return ret;
} }
/* The _virStorageBackendQemuImgInfo separates the command line building from
* the volume definition so that qemuDomainSnapshotCreateInactiveExternal can
* use it without needing to deal with a volume.
*/
struct _virStorageBackendQemuImgInfo { struct _virStorageBackendQemuImgInfo {
int format; int format;
const char *path; const char *path;
@ -941,14 +949,27 @@ struct _virStorageBackendQemuImgInfo {
const char *inputPath; const char *inputPath;
const char *inputFormatStr; const char *inputFormatStr;
int inputFormat; int inputFormat;
char *secretAlias;
const char *secretPath;
}; };
static int static int
virStorageBackendCreateQemuImgOpts(char **opts, virStorageBackendCreateQemuImgOpts(virStorageEncryptionInfoDefPtr enc,
char **opts,
struct _virStorageBackendQemuImgInfo info) struct _virStorageBackendQemuImgInfo info)
{ {
virBuffer buf = VIR_BUFFER_INITIALIZER; virBuffer buf = VIR_BUFFER_INITIALIZER;
if (info.format == VIR_STORAGE_FILE_LUKS) {
if (!enc) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("missing luks encryption information"));
goto error;
}
virQEMUBuildLuksOpts(&buf, enc, info.secretAlias);
} else {
if (info.backingPath) if (info.backingPath)
virBufferAsprintf(&buf, "backing_fmt=%s,", virBufferAsprintf(&buf, "backing_fmt=%s,",
virStorageFileFormatTypeToString(info.backingFormat)); virStorageFileFormatTypeToString(info.backingFormat));
@ -956,6 +977,8 @@ virStorageBackendCreateQemuImgOpts(char **opts,
virBufferAddLit(&buf, "encryption=on,"); virBufferAddLit(&buf, "encryption=on,");
if (info.preallocate) if (info.preallocate)
virBufferAddLit(&buf, "preallocation=metadata,"); virBufferAddLit(&buf, "preallocation=metadata,");
}
if (info.nocow) if (info.nocow)
virBufferAddLit(&buf, "nocow=on,"); virBufferAddLit(&buf, "nocow=on,");
@ -1025,6 +1048,22 @@ virStorageBackendCreateQemuImgCheckEncryption(int format,
if (virStorageGenerateQcowEncryption(conn, vol) < 0) if (virStorageGenerateQcowEncryption(conn, vol) < 0)
return -1; return -1;
} }
} else if (format == VIR_STORAGE_FILE_LUKS) {
if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported volume encryption format %d"),
vol->target.encryption->format);
return -1;
}
if (enc->nsecrets > 1) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("too many secrets for luks encryption"));
return -1;
}
if (enc->nsecrets == 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("no secret provided for luks encryption"));
}
} else { } else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("volume encryption unsupported with format %s"), type); _("volume encryption unsupported with format %s"), type);
@ -1069,6 +1108,12 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
int accessRetCode = -1; int accessRetCode = -1;
char *absolutePath = NULL; char *absolutePath = NULL;
if (info->format == VIR_STORAGE_FILE_LUKS) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot set backing store for luks volume"));
return -1;
}
info->backingFormat = vol->target.backingStore->format; info->backingFormat = vol->target.backingStore->format;
info->backingPath = vol->target.backingStore->path; info->backingPath = vol->target.backingStore->path;
@ -1122,6 +1167,7 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool,
static int static int
virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
int imgformat, int imgformat,
virStorageEncryptionInfoDefPtr enc,
struct _virStorageBackendQemuImgInfo info) struct _virStorageBackendQemuImgInfo info)
{ {
char *opts = NULL; char *opts = NULL;
@ -1130,7 +1176,7 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT)
info.compat = "0.10"; info.compat = "0.10";
if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) if (virStorageBackendCreateQemuImgOpts(enc, &opts, info) < 0)
return -1; return -1;
if (opts) if (opts)
virCommandAddArgList(cmd, "-o", opts, NULL); virCommandAddArgList(cmd, "-o", opts, NULL);
@ -1140,6 +1186,39 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd,
} }
/* Add a secret object to the command line:
* --object secret,id=$secretAlias,file=$secretPath
*
* NB: format=raw is assumed
*/
static int
virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd,
virStorageVolDefPtr vol,
struct _virStorageBackendQemuImgInfo *info)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *commandStr = NULL;
if (virAsprintf(&info->secretAlias, "%s_luks0", vol->name) < 0)
return -1;
virBufferAsprintf(&buf, "secret,id=%s,file=", info->secretAlias);
virQEMUBuildBufferEscapeComma(&buf, info->secretPath);
if (virBufferCheckError(&buf) < 0) {
virBufferFreeAndReset(&buf);
return -1;
}
commandStr = virBufferContentAndReset(&buf);
virCommandAddArgList(cmd, "--object", commandStr, NULL);
VIR_FREE(commandStr);
return 0;
}
/* Create a qemu-img virCommand from the supplied binary path, /* Create a qemu-img virCommand from the supplied binary path,
* volume definitions and imgformat * volume definitions and imgformat
*/ */
@ -1150,7 +1229,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
virStorageVolDefPtr inputvol, virStorageVolDefPtr inputvol,
unsigned int flags, unsigned int flags,
const char *create_tool, const char *create_tool,
int imgformat) int imgformat,
const char *secretPath)
{ {
virCommandPtr cmd = NULL; virCommandPtr cmd = NULL;
const char *type; const char *type;
@ -1162,7 +1242,10 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
.compat = vol->target.compat, .compat = vol->target.compat,
.features = vol->target.features, .features = vol->target.features,
.nocow = vol->target.nocow, .nocow = vol->target.nocow,
.secretPath = secretPath,
.secretAlias = NULL,
}; };
virStorageEncryptionInfoDefPtr enc = NULL;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL);
@ -1192,6 +1275,18 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
_("format features only available with qcow2")); _("format features only available with qcow2"));
return NULL; return NULL;
} }
if (info.format == VIR_STORAGE_FILE_LUKS) {
if (inputvol) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use inputvol with luks volume"));
return NULL;
}
if (!info.encryption) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing encryption description"));
return NULL;
}
}
if (inputvol && if (inputvol &&
virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0) virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0)
@ -1226,10 +1321,22 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
if (info.backingPath) if (info.backingPath)
virCommandAddArgList(cmd, "-b", info.backingPath, NULL); virCommandAddArgList(cmd, "-b", info.backingPath, NULL);
if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, info) < 0) { if (info.format == VIR_STORAGE_FILE_LUKS) {
if (virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) {
VIR_FREE(info.secretAlias);
virCommandFree(cmd); virCommandFree(cmd);
return NULL; return NULL;
} }
enc = &vol->target.encryption->encinfo;
}
if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat,
enc, info) < 0) {
VIR_FREE(info.secretAlias);
virCommandFree(cmd);
return NULL;
}
VIR_FREE(info.secretAlias);
if (info.inputPath) if (info.inputPath)
virCommandAddArg(cmd, info.inputPath); virCommandAddArg(cmd, info.inputPath);
@ -1240,6 +1347,77 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
return cmd; return cmd;
} }
static char *
virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn,
virStoragePoolObjPtr pool,
virStorageVolDefPtr vol)
{
virStorageEncryptionPtr enc = vol->target.encryption;
char *secretPath = NULL;
int fd = -1;
uint8_t *secret = NULL;
size_t secretlen = 0;
if (!enc) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing encryption description"));
return NULL;
}
if (!conn || !conn->secretDriver ||
!conn->secretDriver->secretLookupByUUID ||
!conn->secretDriver->secretLookupByUsage ||
!conn->secretDriver->secretGetValue) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unable to look up encryption secret"));
return NULL;
}
if (!(secretPath = virStoragePoolObjBuildTempFilePath(pool, vol)))
goto cleanup;
if ((fd = mkostemp(secretPath, O_CLOEXEC)) < 0) {
virReportSystemError(errno, "%s",
_("failed to open luks secret file for write"));
goto error;
}
if (virSecretGetSecretString(conn, &enc->secrets[0]->seclookupdef,
VIR_SECRET_USAGE_TYPE_VOLUME,
&secret, &secretlen) < 0)
goto error;
if (safewrite(fd, secret, secretlen) < 0) {
virReportSystemError(errno, "%s",
_("failed to write luks secret file"));
goto error;
}
VIR_FORCE_CLOSE(fd);
if ((vol->target.perms->uid != (uid_t) -1) &&
(vol->target.perms->gid != (gid_t) -1)) {
if (chown(secretPath, vol->target.perms->uid,
vol->target.perms->gid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to chown luks secret file"));
goto error;
}
}
cleanup:
VIR_DISPOSE_N(secret, secretlen);
VIR_FORCE_CLOSE(fd);
return secretPath;
error:
unlink(secretPath);
VIR_FREE(secretPath);
goto cleanup;
}
int int
virStorageBackendCreateQemuImg(virConnectPtr conn, virStorageBackendCreateQemuImg(virConnectPtr conn,
virStoragePoolObjPtr pool, virStoragePoolObjPtr pool,
@ -1251,6 +1429,7 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
char *create_tool; char *create_tool;
int imgformat; int imgformat;
virCommandPtr cmd; virCommandPtr cmd;
char *secretPath = NULL;
virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1);
@ -1266,8 +1445,15 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
if (imgformat < 0) if (imgformat < 0)
goto cleanup; goto cleanup;
if (vol->target.format == VIR_STORAGE_FILE_LUKS) {
if (!(secretPath =
virStorageBackendCreateQemuImgSecretPath(conn, pool, vol)))
goto cleanup;
}
cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol, cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol,
flags, create_tool, imgformat); flags, create_tool,
imgformat, secretPath);
if (!cmd) if (!cmd)
goto cleanup; goto cleanup;
@ -1275,6 +1461,10 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
virCommandFree(cmd); virCommandFree(cmd);
cleanup: cleanup:
if (secretPath) {
unlink(secretPath);
VIR_FREE(secretPath);
}
VIR_FREE(create_tool); VIR_FREE(create_tool);
return ret; return ret;
} }

View File

@ -241,7 +241,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn,
virStorageVolDefPtr inputvol, virStorageVolDefPtr inputvol,
unsigned int flags, unsigned int flags,
const char *create_tool, const char *create_tool,
int imgformat); int imgformat,
const char *secretPath);
/* ------- virStorageFile backends ------------ */ /* ------- virStorageFile backends ------------ */
typedef struct _virStorageFileBackend virStorageFileBackend; typedef struct _virStorageFileBackend virStorageFileBackend;

View File

@ -155,3 +155,61 @@ virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str)
{ {
virBufferEscape(buf, ',', ",", "%s", str); virBufferEscape(buf, ',', ",", "%s", str);
} }
/**
* virQEMUBuildLuksOpts:
* @buf: buffer to build the string into
* @enc: pointer to encryption info
* @alias: alias to use
*
* Generate the string for id=$alias and any encryption options for
* into the buffer.
*
* Important note, a trailing comma (",") is built into the return since
* it's expected other arguments are appended after the id=$alias string.
* So either turn something like:
*
* "key-secret=$alias,"
*
* or
* "key-secret=$alias,cipher-alg=twofish-256,cipher-mode=cbc,
* hash-alg=sha256,ivgen-alg=plain64,igven-hash-alg=sha256,"
*
*/
void
virQEMUBuildLuksOpts(virBufferPtr buf,
virStorageEncryptionInfoDefPtr enc,
const char *alias)
{
virBufferAsprintf(buf, "key-secret=%s,", alias);
if (!enc->cipher_name)
return;
virBufferAddLit(buf, "cipher-alg=");
virQEMUBuildBufferEscapeComma(buf, enc->cipher_name);
virBufferAsprintf(buf, "-%u,", enc->cipher_size);
if (enc->cipher_mode) {
virBufferAddLit(buf, "cipher-mode=");
virQEMUBuildBufferEscapeComma(buf, enc->cipher_mode);
virBufferAddLit(buf, ",");
}
if (enc->cipher_hash) {
virBufferAddLit(buf, "hash-alg=");
virQEMUBuildBufferEscapeComma(buf, enc->cipher_hash);
virBufferAddLit(buf, ",");
}
if (!enc->ivgen_name)
return;
virBufferAddLit(buf, "ivgen-alg=");
virQEMUBuildBufferEscapeComma(buf, enc->ivgen_name);
virBufferAddLit(buf, ",");
if (enc->ivgen_hash) {
virBufferAddLit(buf, "ivgen-hash-alg=");
virQEMUBuildBufferEscapeComma(buf, enc->ivgen_hash);
virBufferAddLit(buf, ",");
}
}

View File

@ -27,10 +27,16 @@
# include "internal.h" # include "internal.h"
# include "virbuffer.h" # include "virbuffer.h"
# include "virjson.h" # include "virjson.h"
# include "virstorageencryption.h"
char *virQEMUBuildObjectCommandlineFromJSON(const char *type, char *virQEMUBuildObjectCommandlineFromJSON(const char *type,
const char *alias, const char *alias,
virJSONValuePtr props); virJSONValuePtr props);
void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str); void virQEMUBuildBufferEscapeComma(virBufferPtr buf, const char *str);
void virQEMUBuildLuksOpts(virBufferPtr buf,
virStorageEncryptionInfoDefPtr enc,
const char *alias)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
#endif /* __VIR_QEMU_H_ */ #endif /* __VIR_QEMU_H_ */

View File

@ -83,7 +83,8 @@ testCompareXMLToArgvFiles(bool shouldFail,
cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, &poolobj, vol, cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, &poolobj, vol,
inputvol, flags, inputvol, flags,
create_tool, imgformat); create_tool, imgformat,
NULL);
if (!cmd) { if (!cmd) {
if (shouldFail) { if (shouldFail) {
virResetLastError(); virResetLastError();