]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
remote: support specifying multiple keys/certs in libvirtd.conf
authorDaniel P. Berrangé <berrange@redhat.com>
Mon, 3 Nov 2025 17:27:26 +0000 (17:27 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Mon, 24 Nov 2025 15:05:09 +0000 (15:05 +0000)
The 'cert_file' and 'key_file' parameters in libvirtd.conf only
permit a single cert/key. To support hybrid deployments for PQC,
we need to be able to request multiple certs/keys. This involves
new 'cert_files' and 'key_files' config parameters that accept a
list of filenames. The new parameters are mutually exclusive with
the old parameters.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/remote/libvirtd.aug.in
src/remote/libvirtd.conf.in
src/remote/remote_daemon.c
src/remote/remote_daemon_config.c
src/remote/remote_daemon_config.h
src/remote/test_libvirtd.aug.in

index d744548f4126da99fd92e6a1fc095fe20dfdb974..1f3bb5d0e212daa2910074b90efa671e77d39ed2 100644 (file)
@@ -47,6 +47,8 @@ module @DAEMON_NAME_UC@ =
 
    let certificate_entry = str_entry "key_file"
                          | str_entry "cert_file"
+                         | str_array_entry "key_files"
+                         | str_array_entry "cert_files"
                          | str_entry "ca_file"
                          | str_entry "crl_file"
 
index 32a680317acc7d7fd7f30f4b83078d6030d7f38a..e4460e61ef1ab070ffb87470e4333d96abe6595a 100644 (file)
 
 # Override the default server key file path
 #
+# This parameter is mutually exclusive with 'key_files'
+#
 #key_file = "@sysconfdir@/pki/libvirt/private/serverkey.pem"
 
+# Override the default server key file path(s)
+#
+# This parameter is mutually exclusive with 'key_file'
+#
+#key_files = ["@sysconfdir@/pki/libvirt/private/serverkey-0.pem", "@sysconfdir@/pki/libvirt/private/serverkey-1.pem"]
+
 # Override the default server certificate file path
 #
+# This parameter is mutually exclusive with 'cert_files'
+#
 #cert_file = "@sysconfdir@/pki/libvirt/servercert.pem"
 
+# Override the default server certificate file path(s)
+#
+# This parameter is mutually exclusive with 'cert_file'
+#
+#cert_files = ["@sysconfdir@/pki/libvirt/servercert-0.pem", "@sysconfdir@/pki/libvirt/servercert-1.pem"]
+
 # Override the default CA certificate path
 #
 #ca_file = "@sysconfdir@/pki/CA/cacert.pem"
index e7c8f587c4a23ac29eb5ba9849b199ce62609fdc..ee3d10bc2312624d80576eca1160169d24884f2f 100644 (file)
@@ -325,31 +325,31 @@ daemonSetupNetworking(virNetServer *srv,
         virNetTLSContext *ctxt = NULL;
 
         if (config->ca_file ||
-            config->cert_file ||
-            config->key_file) {
-            const char *certs[] = { config->cert_file, NULL };
-            const char *keys[] = { config->key_file, NULL };
-
+            config->cert_files ||
+            config->key_files) {
+            g_autofree char *certs = g_strjoinv(", ", config->cert_files);
+            g_autofree char *keys = g_strjoinv(", ", config->key_files);
             if (!config->ca_file) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("No CA certificate path set to match server key/cert"));
+                               _("No CA certificate path set to match server key(s)/cert(s)"));
                 return -1;
             }
-            if (!config->cert_file) {
+            if (!config->cert_files) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("No server certificate path set to match server key"));
+                               _("No server certificate path(s) set to match server key(s)"));
                 return -1;
             }
-            if (!config->key_file) {
+            if (!config->key_files) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("No server key path set to match server cert"));
+                               _("No server key path(s) set to match server cert(s)"));
                 return -1;
             }
-            VIR_DEBUG("Using CA='%s' cert='%s' key='%s'",
-                      config->ca_file, config->cert_file, config->key_file);
+            VIR_DEBUG("Using CA='%s' certs='%s' keys='%s'",
+                      config->ca_file, certs, keys);
             if (!(ctxt = virNetTLSContextNewServer(config->ca_file,
                                                    config->crl_file,
-                                                   certs, keys,
+                                                   (const char *const*)config->cert_files,
+                                                   (const char *const*)config->key_files,
                                                    (const char *const*)config->tls_allowed_dn_list,
                                                    config->tls_priority,
                                                    config->tls_no_sanity_certificate ? false : true,
index c1e75444e1803ec51ee2da1a3961b44783cc9d53..bb6078967f9682ffe022e234f4ef028b423042c0 100644 (file)
@@ -192,9 +192,9 @@ daemonConfigFree(struct daemonConfig *data)
 
     g_free(data->tls_priority);
 
-    g_free(data->key_file);
+    g_strfreev(data->key_files);
     g_free(data->ca_file);
-    g_free(data->cert_file);
+    g_strfreev(data->cert_files);
     g_free(data->crl_file);
 #endif /* ! WITH_IP */
 
@@ -212,8 +212,12 @@ daemonConfigLoadOptions(struct daemonConfig *data,
                         virConf *conf)
 {
     int rc G_GNUC_UNUSED;
-
 #ifdef WITH_IP
+    g_autofree char *cert_file = NULL;
+    g_autofree char *key_file = NULL;
+    size_t ncerts;
+    size_t nkeys;
+
     if (virConfGetValueBool(conf, "listen_tcp", &data->listen_tcp) < 0)
         return -1;
     if (virConfGetValueBool(conf, "listen_tls", &data->listen_tls) < 0)
@@ -269,10 +273,39 @@ daemonConfigLoadOptions(struct daemonConfig *data,
     if (virConfGetValueBool(conf, "tls_no_verify_certificate", &data->tls_no_verify_certificate) < 0)
         return -1;
 
-    if (virConfGetValueString(conf, "key_file", &data->key_file) < 0)
+    if (virConfGetValueString(conf, "key_file", &key_file) < 0)
+        return -1;
+    if (virConfGetValueString(conf, "cert_file", &cert_file) < 0)
+        return -1;
+    if (virConfGetValueStringList(conf, "key_files", false, &data->key_files) < 0)
+        return -1;
+    if (virConfGetValueStringList(conf, "cert_files", false, &data->cert_files) < 0)
+        return -1;
+    if ((cert_file && data->cert_files) ||
+         (key_file && data->key_files)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("cert_file/key_file are mutually exclusive with cert_files/key_files"));
         return -1;
-    if (virConfGetValueString(conf, "cert_file", &data->cert_file) < 0)
+    }
+    if (cert_file) {
+        data->cert_files = g_new0(char *, 2);
+        data->cert_files[0] = g_steal_pointer(&cert_file);
+        data->cert_files[1] = NULL;
+    }
+    if (key_file) {
+        data->key_files = g_new0(char *, 2);
+        data->key_files[0] = g_steal_pointer(&key_file);
+        data->key_files[1] = NULL;
+    }
+    ncerts = data->cert_files ? g_strv_length(data->cert_files) : 0;
+    nkeys = data->key_files ? g_strv_length(data->key_files) : 0;
+    if (ncerts != nkeys) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Number of certificates (%1$zu) must match number of keys (%2$zu)"),
+                       ncerts, nkeys);
         return -1;
+    }
+
     if (virConfGetValueString(conf, "ca_file", &data->ca_file) < 0)
         return -1;
     if (virConfGetValueString(conf, "crl_file", &data->crl_file) < 0)
index 9f9e54e8381c1ecbacd3f4a644d421e569ca1ac7..e699889191cb25bd81cfc2385b064e669df6b1ed 100644 (file)
@@ -58,8 +58,8 @@ struct daemonConfig {
     char *tls_priority;
     unsigned int tcp_min_ssf;
 
-    char *key_file;
-    char *cert_file;
+    char **key_files;
+    char **cert_files;
     char *ca_file;
     char *crl_file;
 #endif /* ! WITH_IP */
index c27680e1306e791f4b9e7f455854463d0a026e67..a37b0daa55c9074d6a7aa79881a30af7059bf3f8 100644 (file)
@@ -26,7 +26,15 @@ module Test_@DAEMON_NAME@ =
         }
 @CUT_ENABLE_IP@
         { "key_file" = "@sysconfdir@/pki/libvirt/private/serverkey.pem" }
+        { "key_files"
+             { "1" = "@sysconfdir@/pki/libvirt/private/serverkey-0.pem" }
+             { "2" = "@sysconfdir@/pki/libvirt/private/serverkey-1.pem" }
+        }
         { "cert_file" = "@sysconfdir@/pki/libvirt/servercert.pem" }
+        { "cert_files"
+             { "1" = "@sysconfdir@/pki/libvirt/servercert-0.pem" }
+             { "2" = "@sysconfdir@/pki/libvirt/servercert-1.pem" }
+        }
         { "ca_file" = "@sysconfdir@/pki/CA/cacert.pem" }
         { "crl_file" = "@sysconfdir@/pki/CA/crl.pem" }
         { "tls_no_sanity_certificate" = "1" }