[PATCH v3 0/5] Add TPM emulator <source type='file/dir' path='..'/>

From: Marc-André Lureau <marcandre.lureau@redhat.com> Hi, When swtpm capabilities reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored. --tpmstate must be backend-uri=file://. v3: - changed to <source type='file/dir' path='..'/> v2: - add <source dir='..'/> support as well (Daniel) Related: https://issues.redhat.com/browse/CNV-35250 Marc-André Lureau (5): util: check swtpm nvram-backend-{dir,file} capabilities tpm: rename 'storagepath' to 'source_path' schema: add TPM emulator <source type='file' path='..'> schema: add TPM emulator <source type='dir' path='..'> qemu_tpm: handle file/block storage source docs/formatdomain.rst | 20 ++++ src/conf/domain_conf.c | 31 ++++- src/conf/domain_conf.h | 12 +- src/conf/schemas/domaincommon.rng | 26 +++++ src/qemu/qemu_tpm.c | 110 +++++++++++++----- src/security/security_selinux.c | 4 +- src/util/virtpm.c | 2 + src/util/virtpm.h | 2 + .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 10 files changed, 176 insertions(+), 33 deletions(-) -- 2.45.2.827.g557ae147e6

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/util/virtpm.c | 2 ++ src/util/virtpm.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 81fd6166cf..298caaad80 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -40,6 +40,8 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature, VIR_TPM_SWTPM_FEATURE_LAST, "cmdarg-pwd-fd", "cmdarg-migration", + "nvram-backend-dir", + "nvram-backend-file", ); VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, diff --git a/src/util/virtpm.h b/src/util/virtpm.h index fb330effa8..99dbcc1dc8 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -31,6 +31,8 @@ bool virTPMHasSwtpm(void); typedef enum { VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD, VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_FILE, VIR_TPM_SWTPM_FEATURE_LAST } virTPMSwtpmFeature; -- 2.45.2.827.g557ae147e6

On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
--- src/util/virtpm.c | 2 ++ src/util/virtpm.h | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/src/util/virtpm.c b/src/util/virtpm.c index 81fd6166cf..298caaad80 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -40,6 +40,8 @@ VIR_ENUM_IMPL(virTPMSwtpmFeature, VIR_TPM_SWTPM_FEATURE_LAST, "cmdarg-pwd-fd", "cmdarg-migration", + "nvram-backend-dir", + "nvram-backend-file", );
VIR_ENUM_IMPL(virTPMSwtpmSetupFeature, diff --git a/src/util/virtpm.h b/src/util/virtpm.h index fb330effa8..99dbcc1dc8 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -31,6 +31,8 @@ bool virTPMHasSwtpm(void); typedef enum { VIR_TPM_SWTPM_FEATURE_CMDARG_PWD_FD, VIR_TPM_SWTPM_FEATURE_CMDARG_MIGRATION, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR, + VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_FILE,
VIR_TPM_SWTPM_FEATURE_LAST } virTPMSwtpmFeature;

From: Marc-André Lureau <marcandre.lureau@redhat.com> Mechanically replace existing 'storagepath' with 'source_path', as the following patches introduce <source path='..'> configuration. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 2 +- src/qemu/qemu_tpm.c | 48 ++++++++++++++++----------------- src/security/security_selinux.c | 4 +-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6d7dee7956..284a3815b3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3461,7 +3461,7 @@ void virDomainTPMDefFree(virDomainTPMDef *def) break; case VIR_DOMAIN_TPM_TYPE_EMULATOR: virObjectUnref(def->data.emulator.source); - g_free(def->data.emulator.storagepath); + g_free(def->data.emulator.source_path); g_free(def->data.emulator.logfile); virBitmapFree(def->data.emulator.activePcrBanks); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index a15af4fae3..6b27322e3e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1478,7 +1478,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; - char *storagepath; + char *source_path; char *logfile; unsigned int debug; unsigned char secretuuid[VIR_UUID_BUFLEN]; diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index 2f17918cbb..fd633c7005 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -173,8 +173,8 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, uid_t swtpm_user, gid_t swtpm_group) { - const char *storagepath = tpm->data.emulator.storagepath; - g_autofree char *swtpmStorageDir = g_path_get_dirname(storagepath); + const char *source_path = tpm->data.emulator.source_path; + g_autofree char *swtpmStorageDir = g_path_get_dirname(source_path); /* allow others to cd into this dir */ if (g_mkdir_with_parents(swtpmStorageDir, 0711) < 0) { @@ -186,19 +186,19 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, *created = false; - if (!virFileExists(storagepath) || - virDirIsEmpty(storagepath, true) > 0) + if (!virFileExists(source_path) || + virDirIsEmpty(source_path, true) > 0) *created = true; - if (virDirCreate(storagepath, 0700, swtpm_user, swtpm_group, + if (virDirCreate(source_path, 0700, swtpm_user, swtpm_group, VIR_DIR_CREATE_ALLOW_EXIST) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create directory %1$s as %2$u:%3$d"), - storagepath, swtpm_user, swtpm_group); + source_path, swtpm_user, swtpm_group); return -1; } - if (virFileChownFiles(storagepath, swtpm_user, swtpm_group) < 0) + if (virFileChownFiles(source_path, swtpm_user, swtpm_group) < 0) return -1; return 0; @@ -214,7 +214,7 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, static void qemuTPMEmulatorDeleteStorage(virDomainTPMDef *tpm) { - g_autofree char *path = g_path_get_dirname(tpm->data.emulator.storagepath); + g_autofree char *path = g_path_get_dirname(tpm->data.emulator.source_path); ignore_value(virFileDeleteTree(path)); } @@ -343,7 +343,7 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, /* * qemuTPMEmulatorRunSetup * - * @storagepath: path to the directory for TPM state + * @source_path: path to the directory for TPM state * @vmname: the name of the VM * @vmuuid: the UUID of the VM * @privileged: whether we are running in privileged mode @@ -360,7 +360,7 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, * certificates for it. */ static int -qemuTPMEmulatorRunSetup(const char *storagepath, +qemuTPMEmulatorRunSetup(const char *source_path, const char *vmname, const unsigned char *vmuuid, bool privileged, @@ -413,7 +413,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -424,7 +424,7 @@ qemuTPMEmulatorRunSetup(const char *storagepath, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--overwrite", NULL); @@ -465,7 +465,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * qemuTPMEmulatorReconfigure * * - * @storagepath: path to the directory for TPM state + * @source_path: path to the directory for TPM state * @swtpm_user: The userid to switch to when setting up the TPM; * typically this should be the uid of 'tss' or 'root' * @swtpm_group: The group id to switch to @@ -478,7 +478,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * Reconfigure the active PCR banks of a TPM 2. */ static int -qemuTPMEmulatorReconfigure(const char *storagepath, +qemuTPMEmulatorReconfigure(const char *source_path, uid_t swtpm_user, gid_t swtpm_group, virBitmap *activePcrBanks, @@ -510,7 +510,7 @@ qemuTPMEmulatorReconfigure(const char *storagepath, return -1; virCommandAddArgList(cmd, - "--tpm-state", storagepath, + "--tpm-state", source_path, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -568,7 +568,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, /* Do not create storage and run swtpm_setup on incoming migration over * shared storage */ - on_shared_storage = virFileIsSharedFS(tpm->data.emulator.storagepath) == 1; + on_shared_storage = virFileIsSharedFS(tpm->data.emulator.source_path) == 1; if (incomingMigration && on_shared_storage) create_storage = false; @@ -580,7 +580,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, secretuuid = tpm->data.emulator.secretuuid; if (created && - qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid, + qemuTPMEmulatorRunSetup(tpm->data.emulator.source_path, vmname, vmuuid, privileged, swtpm_user, swtpm_group, tpm->data.emulator.logfile, tpm->data.emulator.version, @@ -588,7 +588,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(tpm->data.emulator.storagepath, + qemuTPMEmulatorReconfigure(tpm->data.emulator.source_path, swtpm_user, swtpm_group, tpm->data.emulator.activePcrBanks, tpm->data.emulator.logfile, @@ -608,7 +608,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, virCommandAddArg(cmd, "--tpmstate"); virCommandAddArgFormat(cmd, "dir=%s,mode=0600", - tpm->data.emulator.storagepath); + tpm->data.emulator.source_path); virCommandAddArg(cmd, "--log"); if (tpm->data.emulator.debug != 0) @@ -721,8 +721,8 @@ qemuTPMEmulatorInitPaths(virDomainTPMDef *tpm, virUUIDFormat(uuid, uuidstr); - if (!tpm->data.emulator.storagepath && - !(tpm->data.emulator.storagepath = + if (!tpm->data.emulator.source_path && + !(tpm->data.emulator.source_path = qemuTPMEmulatorStorageBuildPath(swtpmStorageDir, uuidstr, tpm->data.emulator.version))) return -1; @@ -753,7 +753,7 @@ qemuTPMEmulatorCleanupHost(virDomainTPMDef *tpm, * storage. */ if (outgoingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath) == 1) + virFileIsSharedFS(tpm->data.emulator.source_path) == 1) return; /* @@ -954,7 +954,7 @@ qemuTPMEmulatorStart(virQEMUDriver *driver, virCommandSetErrorFD(cmd, &errfd); if (incomingMigration && - virFileIsSharedFS(tpm->data.emulator.storagepath) == 1) { + virFileIsSharedFS(tpm->data.emulator.source_path) == 1) { /* security labels must have been set up on source already */ setTPMStateLabel = false; } @@ -1023,7 +1023,7 @@ qemuTPMHasSharedStorage(virDomainDef *def) switch (tpm->type) { case VIR_DOMAIN_TPM_TYPE_EMULATOR: - return virFileIsSharedFS(tpm->data.emulator.storagepath) == 1; + return virFileIsSharedFS(tpm->data.emulator.source_path) == 1; case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: case VIR_DOMAIN_TPM_TYPE_EXTERNAL: case VIR_DOMAIN_TPM_TYPE_LAST: diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 31df4d22db..470a73e740 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -3640,7 +3640,7 @@ virSecuritySELinuxSetTPMLabels(virSecurityManager *mgr, if (setTPMStateLabel) { ret = virSecuritySELinuxSetFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath, + def->tpms[i]->data.emulator.source_path, seclabel); } @@ -3670,7 +3670,7 @@ virSecuritySELinuxRestoreTPMLabels(virSecurityManager *mgr, if (restoreTPMStateLabel) { ret = virSecuritySELinuxRestoreFileLabels(mgr, - def->tpms[i]->data.emulator.storagepath); + def->tpms[i]->data.emulator.source_path); } if (ret == 0 && -- 2.45.2.827.g557ae147e6

From: Marc-André Lureau <marcandre.lureau@redhat.com> Learn to parse a file path for the TPM state. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend. +``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage. + + :since:`Since v10.8.0` + ``persistent_state`` The ``persistent_state`` attribute indicates whether 'swtpm' TPM state is kept or not when a transient domain is powered off or undefined. This diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 284a3815b3..9dd8b6b55d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1322,6 +1322,12 @@ VIR_ENUM_IMPL(virDomainTPMVersion, "2.0", ); +VIR_ENUM_IMPL(virDomainTPMSourceType, + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, + "default", + "file", +); + VIR_ENUM_IMPL(virDomainTPMPcrBank, VIR_DOMAIN_TPM_PCR_BANK_LAST, "sha1", @@ -10784,6 +10790,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, int nbackends; int nnodes; size_t i; + xmlNodePtr source_node = NULL; g_autofree char *path = NULL; g_autofree char *secretuuid = NULL; g_autofree char *persistent_state = NULL; @@ -10857,6 +10864,22 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, def->data.emulator.hassecretuuid = true; } + source_node = virXPathNode("./backend/source", ctxt); + if (source_node) { + if (virXMLPropEnum(source_node, "type", + virDomainTPMSourceTypeTypeFromString, + VIR_XML_PROP_NONZERO, + &def->data.emulator.source_type) < 0) + goto error; + path = virXMLPropString(source_node, "path"); + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TPM source path")); + goto error; + } + def->data.emulator.source_path = g_steal_pointer(&path); + } + persistent_state = virXMLPropString(backends[0], "persistent_state"); if (persistent_state) { if (virStringParseYesNo(persistent_state, @@ -25070,6 +25093,11 @@ virDomainTPMDefFormat(virBuffer *buf, virXMLFormatElement(&backendChildBuf, "active_pcr_banks", NULL, &activePcrBanksBuf); } + if (def->data.emulator.source_type != VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT) { + virBufferAsprintf(&backendChildBuf, "<source type='%s'", + virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type)); + virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path); + } break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: if (def->data.external.source->type == VIR_DOMAIN_CHR_TYPE_UNIX) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6b27322e3e..7a70f68177 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1463,6 +1463,13 @@ typedef enum { VIR_DOMAIN_TPM_PCR_BANK_LAST } virDomainPcrBank; +typedef enum { + VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, + VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST +} virDomainTPMSourceType; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0" struct _virDomainTPMDef { @@ -1478,6 +1485,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -4277,6 +4285,7 @@ VIR_ENUM_DECL(virDomainRNGBackend); VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); +VIR_ENUM_DECL(virDomainTPMSourceType); VIR_ENUM_DECL(virDomainTPMPcrBank); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index efb5f00d77..72c8b6c694 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5923,6 +5923,7 @@ <interleave> <ref name="tpm-backend-emulator-encryption"/> <ref name="tpm-backend-emulator-active-pcr-banks"/> + <ref name="tpm-backend-emulator-source"/> </interleave> <optional> <attribute name="persistent_state"> @@ -5981,6 +5982,19 @@ </optional> </define> + <define name="tpm-backend-emulator-source"> + <optional> + <element name="source"> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </attribute> + </element> + </optional> + </define> + <define name="tpm-backend-emulator-encryption"> <optional> <element name="encryption"> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml index 8a613db456..3d6300f544 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -34,6 +34,7 @@ <sha256/> <sha512/> </active_pcr_banks> + <source type='file' path='/path/to/state'/> </backend> </tpm> <audio id='1' type='none'/> -- 2.45.2.827.g557ae147e6

On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
+ + :since:`Since v10.8.0` +
``persistent_state`` The ``persistent_state`` attribute indicates whether 'swtpm' TPM state is kept or not when a transient domain is powered off or undefined. This diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 284a3815b3..9dd8b6b55d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1322,6 +1322,12 @@ VIR_ENUM_IMPL(virDomainTPMVersion, "2.0", );
+VIR_ENUM_IMPL(virDomainTPMSourceType, + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, + "default", + "file", +); + VIR_ENUM_IMPL(virDomainTPMPcrBank, VIR_DOMAIN_TPM_PCR_BANK_LAST, "sha1", @@ -10784,6 +10790,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, int nbackends; int nnodes; size_t i; + xmlNodePtr source_node = NULL; g_autofree char *path = NULL; g_autofree char *secretuuid = NULL; g_autofree char *persistent_state = NULL; @@ -10857,6 +10864,22 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, def->data.emulator.hassecretuuid = true; }
+ source_node = virXPathNode("./backend/source", ctxt); + if (source_node) { + if (virXMLPropEnum(source_node, "type", + virDomainTPMSourceTypeTypeFromString, + VIR_XML_PROP_NONZERO, + &def->data.emulator.source_type) < 0) + goto error; + path = virXMLPropString(source_node, "path"); + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TPM source path")); + goto error; + } + def->data.emulator.source_path = g_steal_pointer(&path); + } + persistent_state = virXMLPropString(backends[0], "persistent_state"); if (persistent_state) { if (virStringParseYesNo(persistent_state, @@ -25070,6 +25093,11 @@ virDomainTPMDefFormat(virBuffer *buf,
virXMLFormatElement(&backendChildBuf, "active_pcr_banks", NULL, &activePcrBanksBuf); } + if (def->data.emulator.source_type != VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT) { + virBufferAsprintf(&backendChildBuf, "<source type='%s'", + virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type)); + virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path); + } break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: if (def->data.external.source->type == VIR_DOMAIN_CHR_TYPE_UNIX) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6b27322e3e..7a70f68177 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1463,6 +1463,13 @@ typedef enum { VIR_DOMAIN_TPM_PCR_BANK_LAST } virDomainPcrBank;
+typedef enum { + VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, + VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST +} virDomainTPMSourceType; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0"
struct _virDomainTPMDef { @@ -1478,6 +1485,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -4277,6 +4285,7 @@ VIR_ENUM_DECL(virDomainRNGBackend); VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); +VIR_ENUM_DECL(virDomainTPMSourceType); VIR_ENUM_DECL(virDomainTPMPcrBank); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index efb5f00d77..72c8b6c694 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5923,6 +5923,7 @@ <interleave> <ref name="tpm-backend-emulator-encryption"/> <ref name="tpm-backend-emulator-active-pcr-banks"/> + <ref name="tpm-backend-emulator-source"/> </interleave> <optional> <attribute name="persistent_state"> @@ -5981,6 +5982,19 @@ </optional> </define>
+ <define name="tpm-backend-emulator-source"> + <optional> + <element name="source"> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </attribute> + </element> + </optional> + </define> + <define name="tpm-backend-emulator-encryption"> <optional> <element name="encryption"> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml index 8a613db456..3d6300f544 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -34,6 +34,7 @@ <sha256/> <sha512/> </active_pcr_banks> + <source type='file' path='/path/to/state'/> </backend> </tpm> <audio id='1' type='none'/>

Hi On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
+ + :since:`Since v10.8.0` +
``persistent_state`` The ``persistent_state`` attribute indicates whether 'swtpm' TPM state is kept or not when a transient domain is powered off or undefined. This diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 284a3815b3..9dd8b6b55d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1322,6 +1322,12 @@ VIR_ENUM_IMPL(virDomainTPMVersion, "2.0", );
+VIR_ENUM_IMPL(virDomainTPMSourceType, + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, + "default", + "file", +); + VIR_ENUM_IMPL(virDomainTPMPcrBank, VIR_DOMAIN_TPM_PCR_BANK_LAST, "sha1", @@ -10784,6 +10790,7 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, int nbackends; int nnodes; size_t i; + xmlNodePtr source_node = NULL; g_autofree char *path = NULL; g_autofree char *secretuuid = NULL; g_autofree char *persistent_state = NULL; @@ -10857,6 +10864,22 @@ virDomainTPMDefParseXML(virDomainXMLOption *xmlopt, def->data.emulator.hassecretuuid = true; }
+ source_node = virXPathNode("./backend/source", ctxt); + if (source_node) { + if (virXMLPropEnum(source_node, "type", + virDomainTPMSourceTypeTypeFromString, + VIR_XML_PROP_NONZERO, + &def->data.emulator.source_type) < 0) + goto error; + path = virXMLPropString(source_node, "path"); + if (!path) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing TPM source path")); + goto error; + } + def->data.emulator.source_path = g_steal_pointer(&path); + } + persistent_state = virXMLPropString(backends[0], "persistent_state"); if (persistent_state) { if (virStringParseYesNo(persistent_state, @@ -25070,6 +25093,11 @@ virDomainTPMDefFormat(virBuffer *buf,
virXMLFormatElement(&backendChildBuf, "active_pcr_banks", NULL, &activePcrBanksBuf); } + if (def->data.emulator.source_type != VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT) { + virBufferAsprintf(&backendChildBuf, "<source type='%s'", + virDomainTPMSourceTypeTypeToString(def->data.emulator.source_type)); + virBufferEscapeString(&backendChildBuf, " path='%s'/>\n", def->data.emulator.source_path); + } break; case VIR_DOMAIN_TPM_TYPE_EXTERNAL: if (def->data.external.source->type == VIR_DOMAIN_CHR_TYPE_UNIX) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6b27322e3e..7a70f68177 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1463,6 +1463,13 @@ typedef enum { VIR_DOMAIN_TPM_PCR_BANK_LAST } virDomainPcrBank;
+typedef enum { + VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, + VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + + VIR_DOMAIN_TPM_SOURCE_TYPE_LAST +} virDomainTPMSourceType; + #define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0"
struct _virDomainTPMDef { @@ -1478,6 +1485,7 @@ struct _virDomainTPMDef { struct { virDomainTPMVersion version; virDomainChrSourceDef *source; + virDomainTPMSourceType source_type; char *source_path; char *logfile; unsigned int debug; @@ -4277,6 +4285,7 @@ VIR_ENUM_DECL(virDomainRNGBackend); VIR_ENUM_DECL(virDomainTPMModel); VIR_ENUM_DECL(virDomainTPMBackend); VIR_ENUM_DECL(virDomainTPMVersion); +VIR_ENUM_DECL(virDomainTPMSourceType); VIR_ENUM_DECL(virDomainTPMPcrBank); VIR_ENUM_DECL(virDomainMemoryModel); VIR_ENUM_DECL(virDomainMemoryBackingModel); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index efb5f00d77..72c8b6c694 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5923,6 +5923,7 @@ <interleave> <ref name="tpm-backend-emulator-encryption"/> <ref name="tpm-backend-emulator-active-pcr-banks"/> + <ref name="tpm-backend-emulator-source"/> </interleave> <optional> <attribute name="persistent_state"> @@ -5981,6 +5982,19 @@ </optional> </define>
+ <define name="tpm-backend-emulator-source"> + <optional> + <element name="source"> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </attribute> + </element> + </optional> + </define> + <define name="tpm-backend-emulator-encryption"> <optional> <element name="encryption"> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml index 8a613db456..3d6300f544 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2.xml @@ -34,6 +34,7 @@ <sha256/> <sha512/> </active_pcr_banks> + <source type='file' path='/path/to/state'/> </backend> </tpm> <audio id='1' type='none'/>

On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.

Hi On Fri, Oct 11, 2024 at 6:17 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
The use-case is to let the user define a specific block device path. There isn't much interest in merely switching from directory to file backend otherwise, afaik. I think we could also address the missing locking in libvirt, since all the resources managed by libvirt should be under its control. Extra locking can be added by swtpm later.

On 10/11/24 10:32 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 6:17 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
The use-case is to let the user define a specific block device path.
Why would they store it on a block device rather than a file system?

On Fri, Oct 11, 2024 at 10:46:58AM -0400, Stefan Berger wrote:
On 10/11/24 10:32 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 6:17 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
The use-case is to let the user define a specific block device path.
Why would they store it on a block device rather than a file system?
If they want to make the storage available to multiple hosts, then using a block device is simpler, as most filesystems are unsafe to concurrently expose in multiple hosts. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 10/14/24 5:19 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:46:58AM -0400, Stefan Berger wrote:
On 10/11/24 10:32 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 6:17 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Learn to parse a file path for the TPM state. > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > docs/formatdomain.rst | 19 ++++++++++++++ > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > src/conf/domain_conf.h | 9 +++++++ > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > 5 files changed, 71 insertions(+) > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > index 4336cff3ac..992bb98730 100644 > --- a/docs/formatdomain.rst > +++ b/docs/formatdomain.rst > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > The default version used depends on the combination of hypervisor, guest > architecture, TPM model and backend. > > +``source`` > + The ``source`` element specifies the location of the TPM state storage . This > + element only works with the ``emulator`` backend. > + > + If not specified, the storage configuration is left to libvirt discretion. > + > + This element requires that swtpm v0.7 or later is installed. > + > + The following attributes are supported: > + > + ``type`` > + The type of storage. It's possible to provide "file" to utilize a single > + file or block device where the TPM state will be stored. > + > + ``path`` > + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
The use-case is to let the user define a specific block device path.
Why would they store it on a block device rather than a file system?
If they want to make the storage available to multiple hosts, then using a block device is simpler, as most filesystems are unsafe to concurrently expose in multiple hosts.
You then need n block devices for n swtpm instances?
With regards, Daniel

On Mon, Oct 14, 2024 at 09:15:16AM -0400, Stefan Berger wrote:
On 10/14/24 5:19 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:46:58AM -0400, Stefan Berger wrote:
On 10/11/24 10:32 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 6:17 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote: > > > > On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > Learn to parse a file path for the TPM state. > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > --- > > docs/formatdomain.rst | 19 ++++++++++++++ > > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > > src/conf/domain_conf.h | 9 +++++++ > > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > > 5 files changed, 71 insertions(+) > > > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > > index 4336cff3ac..992bb98730 100644 > > --- a/docs/formatdomain.rst > > +++ b/docs/formatdomain.rst > > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > > The default version used depends on the combination of hypervisor, guest > > architecture, TPM model and backend. > > > > +``source`` > > + The ``source`` element specifies the location of the TPM state storage . This > > + element only works with the ``emulator`` backend. > > + > > + If not specified, the storage configuration is left to libvirt discretion. > > + > > + This element requires that swtpm v0.7 or later is installed. > > + > > + The following attributes are supported: > > + > > + ``type`` > > + The type of storage. It's possible to provide "file" to utilize a single > > + file or block device where the TPM state will be stored. > > + > > + ``path`` > > + The path to the TPM state storage. > > The file backend of swtpm does not do the locking similar to what the > dir backend does because those who added the file backend didn't > need/want it. If we now give full control to the path of the TPM state > file to the user via the domain XML then whose fault is it if two VMs > use the same path to a file backend and stomp on the TPM state file? Is > it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
The use-case is to let the user define a specific block device path.
Why would they store it on a block device rather than a file system?
If they want to make the storage available to multiple hosts, then using a block device is simpler, as most filesystems are unsafe to concurrently expose in multiple hosts.
You then need n block devices for n swtpm instances?
Yes. In the context of something like KubeVirt this is fine, each VM runs inside a POD, and each POD can be allocated a block device by Kubernetes. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
With regards, Daniel

On Mon, Oct 14, 2024 at 09:35:14AM -0400, Stefan Berger wrote:
On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Learn to parse a file path for the TPM state.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 19 ++++++++++++++ src/conf/domain_conf.c | 28 +++++++++++++++++++++ src/conf/domain_conf.h | 9 +++++++ src/conf/schemas/domaincommon.rng | 14 +++++++++++ tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + 5 files changed, 71 insertions(+)
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4336cff3ac..992bb98730 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator The default version used depends on the combination of hypervisor, guest architecture, TPM model and backend.
+``source`` + The ``source`` element specifies the location of the TPM state storage . This + element only works with the ``emulator`` backend. + + If not specified, the storage configuration is left to libvirt discretion. + + This element requires that swtpm v0.7 or later is installed. + + The following attributes are supported: + + ``type`` + The type of storage. It's possible to provide "file" to utilize a single + file or block device where the TPM state will be stored. + + ``path`` + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
fcntl(lock_fd, F_SETLK...) works fine when done on block device FDs. The scope of any such locks is local to the OS though, it won't lock across hosts, if the same blockdev is exposed to many hosts, so mgmt apps still need to be careful not todo stupid things. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 10/14/24 9:41 AM, Daniel P. Berrangé wrote:
On Mon, Oct 14, 2024 at 09:35:14AM -0400, Stefan Berger wrote:
On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Learn to parse a file path for the TPM state. > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > docs/formatdomain.rst | 19 ++++++++++++++ > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > src/conf/domain_conf.h | 9 +++++++ > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > 5 files changed, 71 insertions(+) > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > index 4336cff3ac..992bb98730 100644 > --- a/docs/formatdomain.rst > +++ b/docs/formatdomain.rst > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > The default version used depends on the combination of hypervisor, guest > architecture, TPM model and backend. > > +``source`` > + The ``source`` element specifies the location of the TPM state storage . This > + element only works with the ``emulator`` backend. > + > + If not specified, the storage configuration is left to libvirt discretion. > + > + This element requires that swtpm v0.7 or later is installed. > + > + The following attributes are supported: > + > + ``type`` > + The type of storage. It's possible to provide "file" to utilize a single > + file or block device where the TPM state will be stored. > + > + ``path`` > + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
fcntl(lock_fd, F_SETLK...) works fine when done on block device FDs. The scope of any such locks is local to the OS though, it won't lock across hosts, if the same blockdev is exposed to many hosts, so mgmt apps still need to be careful not todo stupid things.
Can non-careful users destroy any partition now when specifying it for TPM state storage?
With regards, Daniel

On Mon, Oct 14, 2024 at 10:41:56AM -0400, Stefan Berger wrote:
On 10/14/24 9:41 AM, Daniel P. Berrangé wrote:
On Mon, Oct 14, 2024 at 09:35:14AM -0400, Stefan Berger wrote:
On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote: > > > > On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > Learn to parse a file path for the TPM state. > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > --- > > docs/formatdomain.rst | 19 ++++++++++++++ > > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > > src/conf/domain_conf.h | 9 +++++++ > > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > > 5 files changed, 71 insertions(+) > > > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > > index 4336cff3ac..992bb98730 100644 > > --- a/docs/formatdomain.rst > > +++ b/docs/formatdomain.rst > > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > > The default version used depends on the combination of hypervisor, guest > > architecture, TPM model and backend. > > > > +``source`` > > + The ``source`` element specifies the location of the TPM state storage . This > > + element only works with the ``emulator`` backend. > > + > > + If not specified, the storage configuration is left to libvirt discretion. > > + > > + This element requires that swtpm v0.7 or later is installed. > > + > > + The following attributes are supported: > > + > > + ``type`` > > + The type of storage. It's possible to provide "file" to utilize a single > > + file or block device where the TPM state will be stored. > > + > > + ``path`` > > + The path to the TPM state storage. > > The file backend of swtpm does not do the locking similar to what the > dir backend does because those who added the file backend didn't > need/want it. If we now give full control to the path of the TPM state > file to the user via the domain XML then whose fault is it if two VMs > use the same path to a file backend and stomp on the TPM state file? Is > it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
fcntl(lock_fd, F_SETLK...) works fine when done on block device FDs. The scope of any such locks is local to the OS though, it won't lock across hosts, if the same blockdev is exposed to many hosts, so mgmt apps still need to be careful not todo stupid things.
Can non-careful users destroy any partition now when specifying it for TPM state storage?
Anything is possible, but that's not our problem. Libvirt provides the mechanism to do things, but aims to stay out of "usage policy" decisions as much as practical. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Mon, Oct 14, 2024 at 5:41 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Mon, Oct 14, 2024 at 09:35:14AM -0400, Stefan Berger wrote:
On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote:
On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > Learn to parse a file path for the TPM state. > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > docs/formatdomain.rst | 19 ++++++++++++++ > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > src/conf/domain_conf.h | 9 +++++++ > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > 5 files changed, 71 insertions(+) > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > index 4336cff3ac..992bb98730 100644 > --- a/docs/formatdomain.rst > +++ b/docs/formatdomain.rst > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > The default version used depends on the combination of hypervisor, guest > architecture, TPM model and backend. > > +``source`` > + The ``source`` element specifies the location of the TPM state storage . This > + element only works with the ``emulator`` backend. > + > + If not specified, the storage configuration is left to libvirt discretion. > + > + This element requires that swtpm v0.7 or later is installed. > + > + The following attributes are supported: > + > + ``type`` > + The type of storage. It's possible to provide "file" to utilize a single > + file or block device where the TPM state will be stored. > + > + ``path`` > + The path to the TPM state storage.
The file backend of swtpm does not do the locking similar to what the dir backend does because those who added the file backend didn't need/want it. If we now give full control to the path of the TPM state file to the user via the domain XML then whose fault is it if two VMs use the same path to a file backend and stomp on the TPM state file? Is it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
fcntl(lock_fd, F_SETLK...) works fine when done on block device FDs. The scope of any such locks is local to the OS though, it won't lock across hosts, if the same blockdev is exposed to many hosts, so mgmt apps still need to be careful not todo stupid things.
Now that tpmstate-opt-lock is provided by swtpm (https://github.com/stefanberger/swtpm/commit/aa483aeb6df87ed56ccf3d5778d6fd8...), should we make the file backend feature depend on it? Or should libvirt just warn if locking isn't available? Looking for advice on what to do to get this series merged. thanks

On Mon, Oct 21, 2024 at 03:06:13PM +0400, Marc-André Lureau wrote:
Hi
On Mon, Oct 14, 2024 at 5:41 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Mon, Oct 14, 2024 at 09:35:14AM -0400, Stefan Berger wrote:
On 10/14/24 5:17 AM, Daniel P. Berrangé wrote:
On Fri, Oct 11, 2024 at 10:16:51AM -0400, Stefan Berger wrote:
On 10/11/24 10:10 AM, Marc-André Lureau wrote:
Hi
On Fri, Oct 11, 2024 at 5:49 PM Stefan Berger <stefanb@linux.ibm.com> wrote: > > > > On 10/4/24 9:32 AM, marcandre.lureau@redhat.com wrote: > > From: Marc-André Lureau <marcandre.lureau@redhat.com> > > > > Learn to parse a file path for the TPM state. > > > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > > --- > > docs/formatdomain.rst | 19 ++++++++++++++ > > src/conf/domain_conf.c | 28 +++++++++++++++++++++ > > src/conf/domain_conf.h | 9 +++++++ > > src/conf/schemas/domaincommon.rng | 14 +++++++++++ > > tests/qemuxmlconfdata/tpm-emulator-tpm2.xml | 1 + > > 5 files changed, 71 insertions(+) > > > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > > index 4336cff3ac..992bb98730 100644 > > --- a/docs/formatdomain.rst > > +++ b/docs/formatdomain.rst > > @@ -8173,6 +8173,25 @@ Example: usage of the TPM Emulator > > The default version used depends on the combination of hypervisor, guest > > architecture, TPM model and backend. > > > > +``source`` > > + The ``source`` element specifies the location of the TPM state storage . This > > + element only works with the ``emulator`` backend. > > + > > + If not specified, the storage configuration is left to libvirt discretion. > > + > > + This element requires that swtpm v0.7 or later is installed. > > + > > + The following attributes are supported: > > + > > + ``type`` > > + The type of storage. It's possible to provide "file" to utilize a single > > + file or block device where the TPM state will be stored. > > + > > + ``path`` > > + The path to the TPM state storage. > > The file backend of swtpm does not do the locking similar to what the > dir backend does because those who added the file backend didn't > need/want it. If we now give full control to the path of the TPM state > file to the user via the domain XML then whose fault is it if two VMs > use the same path to a file backend and stomp on the TPM state file? Is > it the fault of the user because of how he defined the path in the XMLs?
Imho, it's desirable to have a similar locking behaviour regardless of the backend and prevent users for mistakenly using the same file.
We will only be able to support the locking with an option on the command line for swtpm (refelected by a new capability verb) and support this series here once that has become available with a new version of swtpm. Otherwise I would avoid giving full control to the path to the users but let libvirt choose a per-VM unique name for the state file.
Relying on libvirt to give a unique path does not avoid the need for locking, because IME users are liable to do unexpected things like putting a shared filesystem underneath, and libvirt won't guarantee any uniqueness across hosts - locking is required for that.
Can we just lock shared block devices without a shared filesystem somehow supporting the distributed locking? So far swtpm has been using fcntl(lock_fd, F_SETLK, ...) on a .lock file.
fcntl(lock_fd, F_SETLK...) works fine when done on block device FDs. The scope of any such locks is local to the OS though, it won't lock across hosts, if the same blockdev is exposed to many hosts, so mgmt apps still need to be careful not todo stupid things.
Now that tpmstate-opt-lock is provided by swtpm (https://github.com/stefanberger/swtpm/commit/aa483aeb6df87ed56ccf3d5778d6fd8...), should we make the file backend feature depend on it? Or should libvirt just warn if locking isn't available?
I don't mind either way, as both options work. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Learn to parse a directory for the TPM state. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 3 ++- src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 1 + src/conf/schemas/domaincommon.rng | 24 ++++++++++++++----- .../qemuxmlconfdata/tpm-emulator-tpm2-enc.xml | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 992bb98730..b1f9471670 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -8185,7 +8185,8 @@ Example: usage of the TPM Emulator ``type`` The type of storage. It's possible to provide "file" to utilize a single - file or block device where the TPM state will be stored. + file or block device where the TPM state will be stored, or "dir" for the + directory where the files will be stored. ``path`` The path to the TPM state storage. diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9dd8b6b55d..3a32e50890 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1326,6 +1326,7 @@ VIR_ENUM_IMPL(virDomainTPMSourceType, VIR_DOMAIN_TPM_SOURCE_TYPE_LAST, "default", "file", + "dir", ); VIR_ENUM_IMPL(virDomainTPMPcrBank, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7a70f68177..45c52107e8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1466,6 +1466,7 @@ typedef enum { typedef enum { VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT = 0, VIR_DOMAIN_TPM_SOURCE_TYPE_FILE, + VIR_DOMAIN_TPM_SOURCE_TYPE_DIR, VIR_DOMAIN_TPM_SOURCE_TYPE_LAST } virDomainTPMSourceType; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 72c8b6c694..96a4975075 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5985,12 +5985,24 @@ <define name="tpm-backend-emulator-source"> <optional> <element name="source"> - <attribute name="type"> - <value>file</value> - </attribute> - <attribute name="path"> - <ref name="filePath"/> - </attribute> + <choice> + <group> + <attribute name="type"> + <value>file</value> + </attribute> + <attribute name="path"> + <ref name="filePath"/> + </attribute> + </group> + <group> + <attribute name="type"> + <value>dir</value> + </attribute> + <attribute name="path"> + <ref name="absDirPath"/> + </attribute> + </group> + </choice> </element> </optional> </define> diff --git a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml index 9c2279b28b..e0c657645d 100644 --- a/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml +++ b/tests/qemuxmlconfdata/tpm-emulator-tpm2-enc.xml @@ -30,6 +30,7 @@ <tpm model='tpm-tis'> <backend type='emulator' version='2.0'> <encryption secret='32ee7e76-2178-47a1-ab7b-269e6e348015'/> + <source type='dir' path='/some/dir'/> </backend> </tpm> <audio id='1' type='none'/> -- 2.45.2.827.g557ae147e6

From: Marc-André Lureau <marcandre.lureau@redhat.com> When swtpm reports "nvram-backend-dir", it can accepts a single file or block device where TPM state will be stored. --tpmstate must be backend-uri=file://<path>. Teach the storage to use custom directory or file source location. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_tpm.c | 78 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index fd633c7005..582351d352 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -340,9 +340,29 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, } +static char * +qemuTPMGetSwtpmSetupStateArg(const virDomainTPMSourceType source_type, + const char *source_path) +{ + switch (source_type) { + case VIR_DOMAIN_TPM_SOURCE_TYPE_FILE: + /* the file:// prefix is supported since swtpm_setup 0.7.0 */ + /* assume the capability check for swtpm is redundant. */ + return g_strdup_printf("file://%s", source_path); + case VIR_DOMAIN_TPM_SOURCE_TYPE_DIR: + case VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT: + case VIR_DOMAIN_TPM_SOURCE_TYPE_LAST: + return g_strdup_printf("%s", source_path); + } + + return NULL; +} + + /* * qemuTPMEmulatorRunSetup * + * @source_type: type of storage * @source_path: path to the directory for TPM state * @vmname: the name of the VM * @vmuuid: the UUID of the VM @@ -360,7 +380,8 @@ qemuTPMVirCommandAddEncryption(virCommand *cmd, * certificates for it. */ static int -qemuTPMEmulatorRunSetup(const char *source_path, +qemuTPMEmulatorRunSetup(const virDomainTPMSourceType source_type, + const char *source_path, const char *vmname, const unsigned char *vmuuid, bool privileged, @@ -376,6 +397,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, char uuid[VIR_UUID_STRING_BUFLEN]; g_autofree char *vmid = NULL; g_autofree char *swtpm_setup = virTPMGetSwtpmSetup(); + g_autofree char *tpm_state = qemuTPMGetSwtpmSetupStateArg(source_type, source_path); if (!swtpm_setup) return -1; @@ -413,7 +435,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, if (!incomingMigration) { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--vmid", vmid, "--logfile", logfile, "--createek", @@ -424,7 +446,7 @@ qemuTPMEmulatorRunSetup(const char *source_path, NULL); } else { virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--overwrite", NULL); @@ -465,6 +487,7 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * qemuTPMEmulatorReconfigure * * + * @source_type: type of storage * @source_path: path to the directory for TPM state * @swtpm_user: The userid to switch to when setting up the TPM; * typically this should be the uid of 'tss' or 'root' @@ -478,7 +501,8 @@ qemuTPMPcrBankBitmapToStr(virBitmap *activePcrBanks) * Reconfigure the active PCR banks of a TPM 2. */ static int -qemuTPMEmulatorReconfigure(const char *source_path, +qemuTPMEmulatorReconfigure(const virDomainTPMSourceType source_type, + const char *source_path, uid_t swtpm_user, gid_t swtpm_group, virBitmap *activePcrBanks, @@ -490,6 +514,7 @@ qemuTPMEmulatorReconfigure(const char *source_path, int exitstatus; g_autofree char *activePcrBanksStr = NULL; g_autofree char *swtpm_setup = virTPMGetSwtpmSetup(); + g_autofree char *tpm_state = qemuTPMGetSwtpmSetupStateArg(source_type, source_path); if (!swtpm_setup) return -1; @@ -510,7 +535,7 @@ qemuTPMEmulatorReconfigure(const char *source_path, return -1; virCommandAddArgList(cmd, - "--tpm-state", source_path, + "--tpm-state", tpm_state, "--logfile", logfile, "--pcr-banks", activePcrBanksStr, "--reconfigure", @@ -555,6 +580,7 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, { g_autoptr(virCommand) cmd = NULL; bool created = false; + bool run_setup = false; g_autofree char *swtpm = virTPMGetSwtpm(); int pwdfile_fd = -1; int migpwdfile_fd = -1; @@ -565,6 +591,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, if (!swtpm) return NULL; + if (tpm->data.emulator.source_type == VIR_DOMAIN_TPM_SOURCE_TYPE_FILE) { + if (!virTPMSwtpmCapsGet(VIR_TPM_SWTPM_FEATURE_NVRAM_BACKEND_DIR)) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, + _("%1$s does not support file storage"), + swtpm); + goto error; + } + create_storage = false; + /* setup is run with --not-overwrite */ + run_setup = true; + } + /* Do not create storage and run swtpm_setup on incoming migration over * shared storage */ @@ -572,15 +610,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, if (incomingMigration && on_shared_storage) create_storage = false; - if (create_storage && - qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0) - return NULL; + if (create_storage) { + if (qemuTPMEmulatorCreateStorage(tpm, &created, swtpm_user, swtpm_group) < 0) + return NULL; + run_setup = created; + } if (tpm->data.emulator.hassecretuuid) secretuuid = tpm->data.emulator.secretuuid; - if (created && - qemuTPMEmulatorRunSetup(tpm->data.emulator.source_path, vmname, vmuuid, + if (run_setup && + qemuTPMEmulatorRunSetup(tpm->data.emulator.source_type, + tpm->data.emulator.source_path, vmname, vmuuid, privileged, swtpm_user, swtpm_group, tpm->data.emulator.logfile, tpm->data.emulator.version, @@ -588,7 +629,8 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, goto error; if (!incomingMigration && - qemuTPMEmulatorReconfigure(tpm->data.emulator.source_path, + qemuTPMEmulatorReconfigure(tpm->data.emulator.source_type, + tpm->data.emulator.source_path, swtpm_user, swtpm_group, tpm->data.emulator.activePcrBanks, tpm->data.emulator.logfile, @@ -607,8 +649,18 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm, tpm->data.emulator.source->data.nix.path); virCommandAddArg(cmd, "--tpmstate"); - virCommandAddArgFormat(cmd, "dir=%s,mode=0600", - tpm->data.emulator.source_path); + switch (tpm->data.emulator.source_type) { + case VIR_DOMAIN_TPM_SOURCE_TYPE_FILE: + virCommandAddArgFormat(cmd, "backend-uri=file://%s", + tpm->data.emulator.source_path); + break; + case VIR_DOMAIN_TPM_SOURCE_TYPE_DIR: + case VIR_DOMAIN_TPM_SOURCE_TYPE_DEFAULT: + case VIR_DOMAIN_TPM_SOURCE_TYPE_LAST: + virCommandAddArgFormat(cmd, "dir=%s,mode=0600", + tpm->data.emulator.source_path); + break; + } virCommandAddArg(cmd, "--log"); if (tpm->data.emulator.debug != 0) -- 2.45.2.827.g557ae147e6
participants (4)
-
Daniel P. Berrangé
-
Marc-André Lureau
-
marcandre.lureau@redhat.com
-
Stefan Berger