command within timeout (Since: 11.2.0) */
VIR_ERR_AGENT_COMMAND_FAILED = 113, /* guest agent responded with failure
to a command (Since: 11.2.0) */
+ VIR_ERR_INVALID_ENCR_KEY_SECRET = 114, /* encryption key is invalid (Since: 12.1.0) */
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_NUMBER_LAST /* (Since: 5.0.0) */
%config(noreplace) %{_sysconfdir}/libvirt/virtsecretd.conf
%{_datadir}/augeas/lenses/virtsecretd.aug
%{_datadir}/augeas/lenses/tests/test_virtsecretd.aug
+%{_datadir}/augeas/lenses/libvirt_secrets.aug
+%{_datadir}/augeas/lenses/tests/test_libvirt_secrets.aug
+%config(noreplace) %{_sysconfdir}/libvirt/secret.conf
%{_unitdir}/virtsecretd.service
%{_unitdir}/virt-secret-init-encryption.service
%{_unitdir}/virtsecretd.socket
src/rpc/virnettlscert.c
src/rpc/virnettlsconfig.c
src/rpc/virnettlscontext.c
+src/secret/secret_config.c
src/secret/secret_driver.c
src/security/security_apparmor.c
src/security/security_dac.c
--- /dev/null
+(* /etc/libvirt/secret.conf *)
+
+module Libvirt_secrets =
+ autoload xfm
+
+ let eol = del /[ \t]*\n/ "\n"
+ let value_sep = del /[ \t]*=[ \t]*/ " = "
+ let indent = del /[ \t]*/ ""
+
+ let array_sep = del /,[ \t\n]*/ ", "
+ let array_start = del /\[[ \t\n]*/ "[ "
+ let array_end = del /\]/ "]"
+
+ let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\""
+ let bool_val = store /0|1/
+ let int_val = store /[0-9]+/
+ let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/ ""
+ let str_array_val = counter "el" . array_start . ( str_array_element . ( array_sep . str_array_element ) * ) ? . array_end
+
+ let str_entry (kw:string) = [ key kw . value_sep . str_val ]
+ let bool_entry (kw:string) = [ key kw . value_sep . bool_val ]
+ let int_entry (kw:string) = [ key kw . value_sep . int_val ]
+ let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
+
+ let secrets_entry = str_entry "secrets_encryption_key"
+ | bool_entry "encrypt_data"
+
+ (* Each entry in the config is one of the following three ... *)
+ let entry = secrets_entry
+ let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
+ let empty = [ label "#empty" . eol ]
+
+ let record = indent . entry . eol
+
+ let lns = ( record | comment | empty ) *
+
+ let filter = incl "/etc/libvirt/secret.conf"
+ . Util.stdexcl
+
+ let xfm = transform lns filter
secret_driver_sources = [
'secret_driver.c',
+ 'secret_config.c',
]
driver_source_files += files(secret_driver_sources)
],
}
+ secret_conf = configure_file(
+ input: 'secret.conf.in',
+ output: 'secret.conf',
+ copy: true
+ )
+ virt_conf_files += secret_conf
+
+ virt_aug_files += files('libvirt_secrets.aug')
+
+ virt_test_aug_files += {
+ 'name': 'test_libvirt_secrets.aug',
+ 'aug': files('test_libvirt_secrets.aug.in'),
+ 'conf': files('secret.conf.in'),
+ 'test_name': 'libvirt_secrets',
+ 'test_srcdir': meson.current_source_dir(),
+ 'test_builddir': meson.current_build_dir(),
+ }
+
virt_daemon_confs += {
'name': 'virtsecretd',
}
--- /dev/null
+#
+# Configuration file for the secrets driver.
+#
+# The secret encryption key is used to override default encryption
+# key path. The user can create an encryption key and set the secret_encryption_key
+# to the path on which it resides.
+# The key must be 32-bytes long.
+#secrets_encryption_key = "/run/libvirt/secrets/secret-encryption-key"
+
+# The encrypt_data setting is used to indicate if the encryption is on or off.
+# 0 indicates off and 1 indicates on. By default it is on
+# if secrets_encryption_key is set to a non-NULL
+# path, or if a systemd credential named "secrets-encryption-key" exists.
+#encrypt_data = 1
--- /dev/null
+/*
+ * secret_config.c: secret.conf config file handling
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <config.h>
+#include <fcntl.h>
+#include "configmake.h"
+#include "datatypes.h"
+#include "virlog.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virutil.h"
+#include "virsecureerase.h"
+#include "secret_config.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_SECRET
+
+VIR_LOG_INIT("secret.secret_config");
+
+static virClass *virSecretDaemonConfigClass;
+static void virSecretDaemonConfigDispose(void *obj);
+
+static int
+virSecretConfigOnceInit(void)
+{
+ if (!VIR_CLASS_NEW(virSecretDaemonConfig, virClassForObject()))
+ return -1;
+
+ return 0;
+}
+
+
+VIR_ONCE_GLOBAL_INIT(virSecretConfig);
+
+
+int
+virSecretDaemonConfigFilePath(bool privileged, char **configfile)
+{
+ if (privileged) {
+ *configfile = g_strdup(SYSCONFDIR "/libvirt/secret.conf");
+ } else {
+ g_autofree char *configdir = NULL;
+
+ configdir = virGetUserConfigDirectory();
+
+ *configfile = g_strdup_printf("%s/secret.conf", configdir);
+ }
+
+ return 0;
+}
+
+
+static int
+virSecretLoadDaemonConfig(virSecretDaemonConfig *cfg,
+ const char *filename)
+{
+ g_autoptr(virConf) conf = NULL;
+ int res;
+
+ if (virFileExists(filename)) {
+ conf = virConfReadFile(filename, 0);
+ if (!conf)
+ return -1;
+ res = virConfGetValueBool(conf, "encrypt_data", &cfg->encryptData);
+ if (res < 0) {
+ return -1;
+ } else if (res == 1) {
+ cfg->encryptDataWasSet = true;
+ } else {
+ cfg->encryptDataWasSet = false;
+ }
+
+ if (virConfGetValueString(conf, "secrets_encryption_key",
+ &cfg->secretsEncryptionKeyPath) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static int
+virGetSecretsEncryptionKey(virSecretDaemonConfig *cfg,
+ uint8_t **secretsEncryptionKey,
+ size_t *secretsKeyLen)
+{
+ VIR_AUTOCLOSE fd = -1;
+ int encryptionKeyLength;
+
+ if ((encryptionKeyLength = virFileReadAll(cfg->secretsEncryptionKeyPath,
+ VIR_SECRETS_ENCRYPTION_KEY_LEN,
+ (char**)secretsEncryptionKey)) < 0) {
+ return -1;
+ }
+ if (encryptionKeyLength != VIR_SECRETS_ENCRYPTION_KEY_LEN) {
+ virReportError(VIR_ERR_INVALID_ENCR_KEY_SECRET,
+ _("Encryption key length must be '%1$d' '%2$s'"),
+ VIR_SECRETS_ENCRYPTION_KEY_LEN,
+ cfg->secretsEncryptionKeyPath);
+ return -1;
+ }
+
+ *secretsKeyLen = (size_t)encryptionKeyLength;
+ return 0;
+}
+
+
+virSecretDaemonConfig *
+virSecretDaemonConfigNew(bool privileged)
+{
+ g_autoptr(virSecretDaemonConfig) cfg = NULL;
+ g_autofree char *configdir = NULL;
+ g_autofree char *configfile = NULL;
+ g_autofree char *rundir = NULL;
+ const char *credentialsDirectory;
+
+ if (virSecretConfigInitialize() < 0)
+ return NULL;
+
+ if (!(cfg = virObjectNew(virSecretDaemonConfigClass)))
+ return NULL;
+
+ if (virSecretDaemonConfigFilePath(privileged, &configfile) < 0)
+ return NULL;
+
+ if (virSecretLoadDaemonConfig(cfg, configfile) < 0)
+ return NULL;
+
+ credentialsDirectory = getenv("CREDENTIALS_DIRECTORY");
+
+ if (!cfg->secretsEncryptionKeyPath && credentialsDirectory) {
+ cfg->secretsEncryptionKeyPath = g_strdup_printf("%s/secrets-encryption-key",
+ credentialsDirectory);
+ if (!virFileExists(cfg->secretsEncryptionKeyPath)) {
+ g_clear_pointer(&cfg->secretsEncryptionKeyPath, g_free);
+ }
+ }
+
+ if (!cfg->encryptDataWasSet) {
+ if (!cfg->secretsEncryptionKeyPath) {
+ /* No path specified by user or environment, disable encryption */
+ cfg->encryptData = false;
+ } else {
+ cfg->encryptData = true;
+ }
+ } else {
+ if (cfg->encryptData) {
+ if (!cfg->secretsEncryptionKeyPath) {
+ /* Built-in default path must be used */
+ rundir = virGetUserRuntimeDirectory();
+ cfg->secretsEncryptionKeyPath = g_strdup_printf("%s/secrets/encryption-key",
+ rundir);
+ }
+ }
+ }
+ VIR_DEBUG("Secrets encryption key path: %s", NULLSTR(cfg->secretsEncryptionKeyPath));
+
+ if (cfg->encryptData) {
+ if (virGetSecretsEncryptionKey(cfg,
+ &cfg->secretsEncryptionKey,
+ &cfg->secretsKeyLen) < 0) {
+ return NULL;
+ }
+ }
+ return g_steal_pointer(&cfg);
+}
+
+
+static void
+virSecretDaemonConfigDispose(void *obj)
+{
+ virSecretDaemonConfig *cfg = obj;
+
+ virSecureErase(cfg->secretsEncryptionKey, cfg->secretsKeyLen);
+ g_free(cfg->secretsEncryptionKeyPath);
+}
--- /dev/null
+/*
+ * secret_config.h: secret.conf config file handling
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "internal.h"
+#include "virinhibitor.h"
+#include "secret_event.h"
+#define VIR_SECRETS_ENCRYPTION_KEY_LEN 32
+
+typedef struct _virSecretDaemonConfig virSecretDaemonConfig;
+struct _virSecretDaemonConfig {
+ virObject parent;
+ /* secrets encryption key path from secret.conf file */
+ char *secretsEncryptionKeyPath;
+
+ /* Store the key to encrypt secrets on the disk */
+ unsigned char *secretsEncryptionKey;
+
+ size_t secretsKeyLen;
+
+ /* Indicates if the newly written secrets are encrypted or not.
+ */
+ bool encryptData;
+
+ /* Indicates if the config file has encrypt_data set or not.
+ */
+ bool encryptDataWasSet;
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSecretDaemonConfig, virObjectUnref);
+
+int virSecretDaemonConfigFilePath(bool privileged, char **configfile);
+virSecretDaemonConfig *virSecretDaemonConfigNew(bool privileged);
+int virSecretDaemonConfigLoadFile(virSecretDaemonConfig *data,
+ const char *filename,
+ bool allow_missing);
#include "secret_event.h"
#include "virutil.h"
#include "virinhibitor.h"
+#include "secret_config.h"
#define VIR_FROM_THIS VIR_FROM_SECRET
/* Immutable pointer, self-locking APIs */
virInhibitor *inhibitor;
+
+ /* Require lock to get reference on 'config',
+ * then lockless thereafter */
+ virSecretDaemonConfig *config;
};
static virSecretDriverState *driver;
VIR_FREE(driver->configDir);
virObjectUnref(driver->secretEventState);
+ virObjectUnref(driver->config);
virInhibitorFree(driver->inhibitor);
if (driver->lockFD != -1)
driver->stateDir);
goto error;
}
+ if (!(driver->config = virSecretDaemonConfigNew(driver->privileged)))
+ goto error;
driver->inhibitor = virInhibitorNew(
VIR_INHIBITOR_WHAT_NONE,
if (!driver)
return -1;
+ if (!(driver->config = virSecretDaemonConfigNew(driver->privileged)))
+ return -1;
+
ignore_value(virSecretLoadAllConfigs(driver->secrets, driver->configDir));
return 0;
--- /dev/null
+module Test_libvirt_secrets =
+ @CONFIG@
+
+ test Libvirt_secrets.lns get conf =
+{ "secrets_encryption_key" = "/run/libvirt/secrets/secret-encryption-key" }
+{ "encrypt_data" = "1" }
[VIR_ERR_AGENT_COMMAND_FAILED] = {
N_("guest agent command failed"),
N_("guest agent command failed: %1$s") },
+ [VIR_ERR_INVALID_ENCR_KEY_SECRET] = {
+ N_("Invalid encryption key for the secret"),
+ N_("Invalid encryption key for the secret: %1$s") },
};
G_STATIC_ASSERT(G_N_ELEMENTS(virErrorMsgStrings) == VIR_ERR_NUMBER_LAST);