On Thu, Nov 13, 2025 at 07:02:22PM +0530, Arun Menon wrote:
> A new configuration file called secrets.conf is introduced to
> let the user configure the path to the master encryption key.
> This key will be used to encrypt/decrypt the secrets in libvirt.
>
> By default the path is set to the runtime directory
> /run/libvirt/secrets, and it is commented in the config file.
> The virtsecretd driver checks if the credentials are available
> in the CREDENTIALS_DIRECTORY. In case it is not present, then the
> user is expected to provide the encryption key path in secrets.conf
>
> Add logic to parse the encryption key file and store the key.
> When systemd will start the secrets driver, it will read the secret.conf
> file and check if encrypt_data flag is set to 1. In that case, the secrets
> will be stored in encrypted format on the disk. The encryption and decryption
> logic will be added in the subsequent patches.
>
> Signed-off-by: Arun Menon <[email protected]>
> ---
> libvirt.spec.in | 1 +
> src/secret/meson.build | 7 +++
> src/secret/secret_driver.c | 96 ++++++++++++++++++++++++++++++++++++++
> src/secret/secrets.conf.in | 14 ++++++
New config files need to be accompanied with augeas files for
processing them. See the simple examples in
src/network/libvirtd_network.aug
src/network/test_libvirtd_network.aug.in
and the src/network/meson.build file for how to wire them up.
They also need to be in the RPM spec.
> diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c
> index 04c3ca49f1..0b415e5ef3 100644
> --- a/src/secret/secret_driver.c
> +++ b/src/secret/secret_driver.c
> @@ -42,6 +42,7 @@
> #include "secret_event.h"
> #include "virutil.h"
> #include "virinhibitor.h"
> +#include "virfile.h"
>
> #define VIR_FROM_THIS VIR_FROM_SECRET
>
> @@ -70,6 +71,17 @@ struct _virSecretDriverState {
>
> /* Immutable pointer, self-locking APIs */
> virInhibitor *inhibitor;
> +
> + /* master encryption key value from secret.conf file */
> + char *masterKeyPath;
Per the previous patch, lets call this secretsEncryptionKeyPath
> +
> + /* Indicates if the secrets are encrypted or not. 0 if not encrypted
> + * and 1 if encrypted.
> + */
Lets specify "newly written secrets" to clarify that setting this
to '1' wouldn't make it encrypt any pre-existing plain text secrets
> + int encrypt_data;
> +
> + unsigned char* masterKey;
> + size_t masterKeyLen;
> };
None of these should be in this struct. Our normal practice
is to define a virSecretDriverConfig struct to hold all
config parameters, and an pointer to that config in the
state struct. See virNetworkDriverState/Config for a
simple example.
>
> static virSecretDriverState *driver;
> @@ -307,6 +319,44 @@ secretGetXMLDesc(virSecretPtr secret,
> return ret;
> }
>
> +static bool secretGetMasterKey(uint8_t **masterKey, size_t *masterKeyLen)
> +{
> + int fd = -1;
> + struct stat st;
> +
> + if ((fd = open(driver->masterKeyPath, O_RDONLY)) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot open master key
> file '%1$s'"),
> + driver->masterKeyPath);
> + return false;
> + }
> + if (fstat(fd, &st) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot stat master key
> file '%1$s'"),
> + driver->masterKeyPath);
> + VIR_FORCE_CLOSE(fd);
> + return false;
> + }
> + *masterKeyLen = st.st_size;
> + if (*masterKeyLen == 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, _("Master encryption key file
> %1$s is empty"),
> + driver->masterKeyPath);
> + VIR_FORCE_CLOSE(fd);
> + return false;
> + }
> + *masterKey = g_new0(uint8_t, *masterKeyLen);
> + if (saferead(fd, &masterKey, *masterKeyLen) != *masterKeyLen) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot read master key
> file '%1$s'"),
> + driver->masterKeyPath);
> + VIR_FORCE_CLOSE(fd);
> + return false;
> + }
> + VIR_FORCE_CLOSE(fd);
> + if (*masterKeyLen < 32) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, _("Master encryption key file
> %1$s must be atleast 32 bytes"),
> + driver->masterKeyPath);
> + return false;
> + }
> + return true;
> +}
>
> static int
> secretSetValue(virSecretPtr secret,
> @@ -482,6 +532,10 @@ secretStateInitialize(bool privileged,
> void *opaque)
> {
> VIR_LOCK_GUARD lock = virLockGuardLock(&mutex);
> + g_autofree char *secretsconf = NULL;
> + g_autofree char *credentials_directory = NULL;
> + g_autofree char *master_encryption_key_path = NULL;
> + g_autoptr(virConf) conf = NULL;
>
> driver = g_new0(virSecretDriverState, 1);
>
> @@ -537,6 +591,48 @@ secretStateInitialize(bool privileged,
> if (virSecretLoadAllConfigs(driver->secrets, driver->configDir) < 0)
> goto error;
>
> + secretsconf = g_strdup_printf("%s/libvirt/secrets.conf", SYSCONFDIR);
> + credentials_directory = getenv("CREDENTIALS_DIRECTORY");
> +
> + if (credentials_directory) {
> + VIR_DEBUG("Using credentials directory from environment: %s",
> + credentials_directory);
> + master_encryption_key_path =
> g_strdup_printf("%s/master-encryption-key", credentials_directory);
> + if (access(master_encryption_key_path, R_OK) == 0) {
> + driver->masterKeyPath = g_strdup(master_encryption_key_path);
> + }
> + } else if (access(secretsconf, R_OK) == 0) {
> + if (!(conf = virConfReadFile(secretsconf, 0))) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Failed to read secrets.conf from %1$s"),
> + secretsconf);
> + goto error;
> + }
> +
> + if (virConfGetValueString(conf, "master_encryption_key",
> &driver->masterKeyPath) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Failed to get master_encryption_key from
> %1$s"),
> + secretsconf);
> + goto error;
> + }
> + } else {
> + VIR_DEBUG("No secrets configuration found %s, skipping",
> driver->configDir);
> + driver->masterKeyPath = NULL;
> + driver->masterKeyLen = 0;
> + }
> + if (driver->masterKeyPath) {
> + if (!secretGetMasterKey(&driver->masterKey, &driver->masterKeyLen)) {
> + goto error;
> + }
> + VIR_DEBUG("Master encryption key loaded from %s",
> driver->masterKeyPath);
> + VIR_DEBUG("Master encryption key length: %zu bytes",
> driver->masterKeyLen);
> + }
> + if (virConfGetValueInt(conf, "encrypt_data", &driver->encrypt_data) < 0)
> {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Failed to get encrypt_data from %1$s"),
> + secretsconf);
> + goto error;
> + }
> return VIR_DRV_STATE_INIT_COMPLETE;
Config file handling code should not be put directly in the state
initialization method. All loading of the config should be isolated
in separate methods, and live in secret_driver_conf.{h,c} files.
Again take a look at bridge_driver_conf.{h,c} for a simple example
of prior art.
In terms of handling the 'encrypt_data" parameter we need this
psuedo-logic:
if encrypt_data is not set
if key path exists
encrypt_data = 1
else
encrypt_data = 1
else if encrypt_data == 1
if key path does not exist
..report error...
>
> error:
> diff --git a/src/secret/secrets.conf.in b/src/secret/secrets.conf.in
> new file mode 100644
> index 0000000000..80bb9654ce
> --- /dev/null
> +++ b/src/secret/secrets.conf.in
> @@ -0,0 +1,14 @@
> +#
> +# Master configuration file for the secrets driver.
> +#
> +
> +# The master encryption key is used to override default master encryption
> +# key path. The user can create an encryption key and set the
> master_encryption_key
> +# to the path on which it resides.
> +# The key must be atleast 32-bytes long.
> +#
> +# master_encryption_key = "/run/libvirt/secrets/master.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 set to on.
> +encrypt_data = 1
This setting should be commented out - the default value should be
set by the code, and should be 0 or 1, depending on whether or not
any encryption key exists on disk.
# This setting determines whether newly written secret values
# are encrypted or not. Setting this to 1 will not trigger
# encryption of existing plain text secrets on disk.
#
# Will default to 1 if the master_encryption_key path exists
# on disk, otherwise will default to 0. Uncomment this to
# force use of encryption and report an error if the key path
# is missing.
# encrypt_data = 1
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 :|