[RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service

Arun Menon via Devel posted 5 patches 2 weeks, 1 day ago
[RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Arun Menon via Devel 2 weeks, 1 day ago
This commit sets the foundation for encrypting the libvirt secrets by providing a
secure way to pass a secret encryption key to the virtsecretd service.

A random secret key is generated using the new virt-secret-init-encryption
service. This key can be consumed by the virtsecretd service.

By using the "Before=" directive in the new secret-init-encryption
service and using "Requires=" directive in the virtsecretd service,
we make sure that the daemon is run only after we have an encrypted
secret key file generated and placed in /var/lib/libvirt/secrets.
The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [1]

This setup therefore provides a default key out-of-the-box for initial use.
A subsequent commit will introduce the logic for virtsecretd
to access and use this key via the $CREDENTIALS_DIRECTORY environment variable. [2]

[1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html
[2] https://systemd.io/CREDENTIALS/

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 libvirt.spec.in                         |  4 ++++
 src/meson.build                         |  1 +
 src/secret/meson.build                  | 24 ++++++++++++++++++++++++
 src/secret/secret-init-encryption.in    | 10 ++++++++++
 src/secret/virtsecretd.service.extra.in |  8 ++++++++
 5 files changed, 47 insertions(+)
 create mode 100644 src/secret/secret-init-encryption.in

diff --git a/libvirt.spec.in b/libvirt.spec.in
index 62af7fb517..dba8a71311 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1880,13 +1880,16 @@ exit 0
 %pre daemon-driver-secret
 %libvirt_sysconfig_pre virtsecretd
 %libvirt_systemd_unix_pre virtsecretd
+%libvirt_systemd_oneshot_pre virt-secret-init-encryption
 
 %posttrans daemon-driver-secret
 %libvirt_sysconfig_posttrans virtsecretd
 %libvirt_systemd_unix_posttrans virtsecretd
+%libvirt_systemd_unix_posttrans virt-secret-init-encryption
 
 %preun daemon-driver-secret
 %libvirt_systemd_unix_preun virtsecretd
+%libvirt_systemd_unix_preun virt-secret-init-encryption
 
 %pre daemon-driver-storage-core
 %libvirt_sysconfig_pre virtstoraged
@@ -2238,6 +2241,7 @@ exit 0
 %{_datadir}/augeas/lenses/virtsecretd.aug
 %{_datadir}/augeas/lenses/tests/test_virtsecretd.aug
 %{_unitdir}/virtsecretd.service
+%{_unitdir}/virt-secret-init-encryption.service
 %{_unitdir}/virtsecretd.socket
 %{_unitdir}/virtsecretd-ro.socket
 %{_unitdir}/virtsecretd-admin.socket
diff --git a/src/meson.build b/src/meson.build
index 47c978cc1f..f18f562fd9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -837,6 +837,7 @@ if conf.has('WITH_LIBVIRTD')
         'sbindir': sbindir,
         'sysconfdir': sysconfdir,
         'initconfdir': initconfdir,
+        'localstatedir': localstatedir,
         'name': unit['name'],
         'service': unit['service'],
         'SERVICE': unit['service'].to_upper(),
diff --git a/src/secret/meson.build b/src/secret/meson.build
index 3b859ea7b4..c02d1064a9 100644
--- a/src/secret/meson.build
+++ b/src/secret/meson.build
@@ -31,6 +31,30 @@ if conf.has('WITH_SECRETS')
     'name': 'virtsecretd',
   }
 
+  secret_init_encryption_unit = {
+    'service': 'virt-secret-init-encryption',
+    'name': 'Libvirt Secret Encryption Init',
+    'input': 'secret-init-encryption.in',
+  }
+
+  unit_conf = configuration_data()
+  unit_conf.set('runstatedir', runstatedir)
+  unit_conf.set('sbindir', sbindir)
+  unit_conf.set('localstatedir', localstatedir)
+  unit_conf.set('sysconfdir', sysconfdir)
+  unit_conf.set('initconfdir', initconfdir)
+  unit_conf.set('name', secret_init_encryption_unit['name'])
+  unit_conf.set('service', secret_init_encryption_unit['service'])
+  unit_conf.set('SERVICE', secret_init_encryption_unit['service'].to_upper())
+
+  configure_file(
+    input: secret_init_encryption_unit['input'],
+    output: '@0@.service'.format(secret_init_encryption_unit['service']),
+    configuration: unit_conf,
+    install: true,
+    install_dir: unitdir,
+  )
+
   virt_daemon_units += {
     'service': 'virtsecretd',
     'name': 'secret',
diff --git a/src/secret/secret-init-encryption.in b/src/secret/secret-init-encryption.in
new file mode 100644
index 0000000000..29da6dbcce
--- /dev/null
+++ b/src/secret/secret-init-encryption.in
@@ -0,0 +1,10 @@
+[Unit]
+Before=virtsecretd.service
+ConditionPathExists=!@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/sh -c 'umask 0066 && (dd if=/dev/urandom status=none bs=32 count=1 | systemd-creds encrypt --name=secrets-encryption-key - @localstatedir@/lib/libvirt/secrets/secrets-encryption-key)'
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/secret/virtsecretd.service.extra.in b/src/secret/virtsecretd.service.extra.in
index 1fc8c672f7..116458b22a 100644
--- a/src/secret/virtsecretd.service.extra.in
+++ b/src/secret/virtsecretd.service.extra.in
@@ -1,2 +1,10 @@
 # The contents of this unit will be merged into a base template.
 # Additional units might be merged as well. See meson.build for details.
+#
+[Unit]
+Requires=virt-secret-init-encryption.service
+After=virt-secret-init-encryption.service
+
+[Service]
+LoadCredentialEncrypted=secrets-encryption-key:@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
+Environment=SECRETS_ENCRYPTION_KEY=%d/secrets-encryption-key
-- 
2.51.1
Re: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Peter Krempa via Devel 2 weeks ago
On Thu, Nov 27, 2025 at 12:52:29 +0530, Arun Menon via Devel wrote:
> This commit sets the foundation for encrypting the libvirt secrets by providing a
> secure way to pass a secret encryption key to the virtsecretd service.
> 
> A random secret key is generated using the new virt-secret-init-encryption
> service. This key can be consumed by the virtsecretd service.
> 
> By using the "Before=" directive in the new secret-init-encryption
> service and using "Requires=" directive in the virtsecretd service,
> we make sure that the daemon is run only after we have an encrypted
> secret key file generated and placed in /var/lib/libvirt/secrets.
> The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [1]
> 
> This setup therefore provides a default key out-of-the-box for initial use.
> A subsequent commit will introduce the logic for virtsecretd
> to access and use this key via the $CREDENTIALS_DIRECTORY environment variable. [2]
> 
> [1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html
> [2] https://systemd.io/CREDENTIALS/
> 
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  libvirt.spec.in                         |  4 ++++
>  src/meson.build                         |  1 +
>  src/secret/meson.build                  | 24 ++++++++++++++++++++++++
>  src/secret/secret-init-encryption.in    | 10 ++++++++++
>  src/secret/virtsecretd.service.extra.in |  8 ++++++++
>  5 files changed, 47 insertions(+)
>  create mode 100644 src/secret/secret-init-encryption.in
> 
> diff --git a/libvirt.spec.in b/libvirt.spec.in
> index 62af7fb517..dba8a71311 100644
> --- a/libvirt.spec.in
> +++ b/libvirt.spec.in
> @@ -1880,13 +1880,16 @@ exit 0
>  %pre daemon-driver-secret
>  %libvirt_sysconfig_pre virtsecretd
>  %libvirt_systemd_unix_pre virtsecretd
> +%libvirt_systemd_oneshot_pre virt-secret-init-encryption
>  
>  %posttrans daemon-driver-secret
>  %libvirt_sysconfig_posttrans virtsecretd
>  %libvirt_systemd_unix_posttrans virtsecretd
> +%libvirt_systemd_unix_posttrans virt-secret-init-encryption
>  
>  %preun daemon-driver-secret
>  %libvirt_systemd_unix_preun virtsecretd
> +%libvirt_systemd_unix_preun virt-secret-init-encryption
>  
>  %pre daemon-driver-storage-core
>  %libvirt_sysconfig_pre virtstoraged
> @@ -2238,6 +2241,7 @@ exit 0
>  %{_datadir}/augeas/lenses/virtsecretd.aug
>  %{_datadir}/augeas/lenses/tests/test_virtsecretd.aug
>  %{_unitdir}/virtsecretd.service
> +%{_unitdir}/virt-secret-init-encryption.service
>  %{_unitdir}/virtsecretd.socket
>  %{_unitdir}/virtsecretd-ro.socket
>  %{_unitdir}/virtsecretd-admin.socket




> diff --git a/src/secret/meson.build b/src/secret/meson.build
> index 3b859ea7b4..c02d1064a9 100644
> --- a/src/secret/meson.build
> +++ b/src/secret/meson.build
> @@ -31,6 +31,30 @@ if conf.has('WITH_SECRETS')
>      'name': 'virtsecretd',
>    }
>  
> +  secret_init_encryption_unit = {
> +    'service': 'virt-secret-init-encryption',
> +    'name': 'Libvirt Secret Encryption Init',
> +    'input': 'secret-init-encryption.in',
> +  }

You can put these values directly inline rather than have this extra
object.


> +  unit_conf = configuration_data()

Preferrably use a more descriptive name for the variable.

e.g. virt_secret_init_encryption_conf

> +  unit_conf.set('runstatedir', runstatedir)
> +  unit_conf.set('sbindir', sbindir)
> +  unit_conf.set('localstatedir', localstatedir)
> +  unit_conf.set('sysconfdir', sysconfdir)
> +  unit_conf.set('initconfdir', initconfdir)
> +  unit_conf.set('name', secret_init_encryption_unit['name'])
> +  unit_conf.set('service', secret_init_encryption_unit['service'])
> +  unit_conf.set('SERVICE', secret_init_encryption_unit['service'].to_upper())

The secret-init-encryption.in uses only 'localstatedir' replacement,
anything else isn't used here.


> +  configure_file(
> +    input: secret_init_encryption_unit['input'],
> +    output: '@0@.service'.format(secret_init_encryption_unit['service']),
> +    configuration: unit_conf,
> +    install: true,
> +    install_dir: unitdir,
> +  )
> +
>    virt_daemon_units += {
>      'service': 'virtsecretd',
>      'name': 'secret',


> diff --git a/src/secret/secret-init-encryption.in b/src/secret/secret-init-encryption.in

Please rename this to 'virt-secret-init-encryption.service.in'

> new file mode 100644
> index 0000000000..29da6dbcce
> --- /dev/null
> +++ b/src/secret/secret-init-encryption.in
> @@ -0,0 +1,10 @@
> +[Unit]
> +Before=virtsecretd.service
> +ConditionPathExists=!@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> +
> +[Service]
> +Type=oneshot
> +ExecStart=/usr/bin/sh -c 'umask 0066 && (dd if=/dev/urandom status=none bs=32 count=1 | systemd-creds encrypt --name=secrets-encryption-key - @localstatedir@/lib/libvirt/secrets/secrets-encryption-key)'
> +
> +[Install]
> +WantedBy=multi-user.target

What does this actually do?


> diff --git a/src/secret/virtsecretd.service.extra.in b/src/secret/virtsecretd.service.extra.in
> index 1fc8c672f7..116458b22a 100644
> --- a/src/secret/virtsecretd.service.extra.in
> +++ b/src/secret/virtsecretd.service.extra.in
> @@ -1,2 +1,10 @@
>  # The contents of this unit will be merged into a base template.
>  # Additional units might be merged as well. See meson.build for details.
> +#
> +[Unit]
> +Requires=virt-secret-init-encryption.service
> +After=virt-secret-init-encryption.service
> +
> +[Service]
> +LoadCredentialEncrypted=secrets-encryption-key:@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> +Environment=SECRETS_ENCRYPTION_KEY=%d/secrets-encryption-key

This will likely be needed also for the monolithic daemon's unit
(libvirtd.service) as that can also start the secret driver in those
setups.
Re: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Daniel P. Berrangé via Devel 2 weeks ago
On Thu, Nov 27, 2025 at 03:42:13PM +0100, Peter Krempa via Devel wrote:
> On Thu, Nov 27, 2025 at 12:52:29 +0530, Arun Menon via Devel wrote:
> > This commit sets the foundation for encrypting the libvirt secrets by providing a
> > secure way to pass a secret encryption key to the virtsecretd service.
> > 
> > A random secret key is generated using the new virt-secret-init-encryption
> > service. This key can be consumed by the virtsecretd service.
> > 
> > By using the "Before=" directive in the new secret-init-encryption
> > service and using "Requires=" directive in the virtsecretd service,
> > we make sure that the daemon is run only after we have an encrypted
> > secret key file generated and placed in /var/lib/libvirt/secrets.
> > The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [1]
> > 
> > This setup therefore provides a default key out-of-the-box for initial use.
> > A subsequent commit will introduce the logic for virtsecretd
> > to access and use this key via the $CREDENTIALS_DIRECTORY environment variable. [2]
> > 
> > [1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html
> > [2] https://systemd.io/CREDENTIALS/
> > 
> > Signed-off-by: Arun Menon <armenon@redhat.com>

> > new file mode 100644
> > index 0000000000..29da6dbcce
> > --- /dev/null
> > +++ b/src/secret/secret-init-encryption.in
> > @@ -0,0 +1,10 @@
> > +[Unit]
> > +Before=virtsecretd.service
> > +ConditionPathExists=!@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> > +
> > +[Service]
> > +Type=oneshot
> > +ExecStart=/usr/bin/sh -c 'umask 0066 && (dd if=/dev/urandom status=none bs=32 count=1 | systemd-creds encrypt --name=secrets-encryption-key - @localstatedir@/lib/libvirt/secrets/secrets-encryption-key)'
> > +
> > +[Install]
> > +WantedBy=multi-user.target
> 
> What does this actually do?

This should be redundant, as once we make it a depdendency on
virtsecretd.service, it'll get enabled automatically in any
scenario were virtsecretd is active.

> > diff --git a/src/secret/virtsecretd.service.extra.in b/src/secret/virtsecretd.service.extra.in
> > index 1fc8c672f7..116458b22a 100644
> > --- a/src/secret/virtsecretd.service.extra.in
> > +++ b/src/secret/virtsecretd.service.extra.in
> > @@ -1,2 +1,10 @@
> >  # The contents of this unit will be merged into a base template.
> >  # Additional units might be merged as well. See meson.build for details.
> > +#
> > +[Unit]
> > +Requires=virt-secret-init-encryption.service
> > +After=virt-secret-init-encryption.service
> > +
> > +[Service]
> > +LoadCredentialEncrypted=secrets-encryption-key:@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> > +Environment=SECRETS_ENCRYPTION_KEY=%d/secrets-encryption-key
> 
> This will likely be needed also for the monolithic daemon's unit
> (libvirtd.service) as that can also start the secret driver in those
> setups.

Hmm, its been a whle since we introduced the modular daemons, and we've
changed Fedora 4 years ago, and changed it in RHEL-9 too. Wonder about
status of other distros ?

Perhaps not right now, but we should likely consider whether we want to
eventually remove libvirtd or keep it around forever. Preferrably the
former so we stop having to think about both deployment scenarios for
changes like this in future.

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 :|
Re: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Peter Krempa via Devel 2 weeks ago
On Thu, Nov 27, 2025 at 15:00:08 +0000, Daniel P. Berrangé wrote:
> On Thu, Nov 27, 2025 at 03:42:13PM +0100, Peter Krempa via Devel wrote:
> > On Thu, Nov 27, 2025 at 12:52:29 +0530, Arun Menon via Devel wrote:
> > > This commit sets the foundation for encrypting the libvirt secrets by providing a
> > > secure way to pass a secret encryption key to the virtsecretd service.
> > > 
> > > A random secret key is generated using the new virt-secret-init-encryption
> > > service. This key can be consumed by the virtsecretd service.
> > > 
> > > By using the "Before=" directive in the new secret-init-encryption
> > > service and using "Requires=" directive in the virtsecretd service,
> > > we make sure that the daemon is run only after we have an encrypted
> > > secret key file generated and placed in /var/lib/libvirt/secrets.
> > > The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [1]
> > > 
> > > This setup therefore provides a default key out-of-the-box for initial use.
> > > A subsequent commit will introduce the logic for virtsecretd
> > > to access and use this key via the $CREDENTIALS_DIRECTORY environment variable. [2]
> > > 
> > > [1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html
> > > [2] https://systemd.io/CREDENTIALS/
> > > 
> > > Signed-off-by: Arun Menon <armenon@redhat.com>
> 

[...]

> > > +[Unit]
> > > +Requires=virt-secret-init-encryption.service
> > > +After=virt-secret-init-encryption.service
> > > +
> > > +[Service]
> > > +LoadCredentialEncrypted=secrets-encryption-key:@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> > > +Environment=SECRETS_ENCRYPTION_KEY=%d/secrets-encryption-key
> > 
> > This will likely be needed also for the monolithic daemon's unit
> > (libvirtd.service) as that can also start the secret driver in those
> > setups.
> 
> Hmm, its been a whle since we introduced the modular daemons, and we've
> changed Fedora 4 years ago, and changed it in RHEL-9 too. Wonder about
> status of other distros ?
> 
> Perhaps not right now, but we should likely consider whether we want to
> eventually remove libvirtd or keep it around forever. Preferrably the
> former so we stop having to think about both deployment scenarios for
> changes like this in future.

Well, if you ask me I prefer libvirtd for my dev setup (and thus run it
on almost all my machines) as it's easier to setup debuggers.

Obviously it's just a mild inconvenience though :D
Re: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Daniel P. Berrangé via Devel 2 weeks ago
On Thu, Nov 27, 2025 at 04:12:59PM +0100, Peter Krempa wrote:
> On Thu, Nov 27, 2025 at 15:00:08 +0000, Daniel P. Berrangé wrote:
> > On Thu, Nov 27, 2025 at 03:42:13PM +0100, Peter Krempa via Devel wrote:
> > > On Thu, Nov 27, 2025 at 12:52:29 +0530, Arun Menon via Devel wrote:
> > > > This commit sets the foundation for encrypting the libvirt secrets by providing a
> > > > secure way to pass a secret encryption key to the virtsecretd service.
> > > > 
> > > > A random secret key is generated using the new virt-secret-init-encryption
> > > > service. This key can be consumed by the virtsecretd service.
> > > > 
> > > > By using the "Before=" directive in the new secret-init-encryption
> > > > service and using "Requires=" directive in the virtsecretd service,
> > > > we make sure that the daemon is run only after we have an encrypted
> > > > secret key file generated and placed in /var/lib/libvirt/secrets.
> > > > The virtsecretd service can then read the key from CREDENTIALS_DIRECTORY. [1]
> > > > 
> > > > This setup therefore provides a default key out-of-the-box for initial use.
> > > > A subsequent commit will introduce the logic for virtsecretd
> > > > to access and use this key via the $CREDENTIALS_DIRECTORY environment variable. [2]
> > > > 
> > > > [1] https://www.freedesktop.org/software/systemd/man/latest/systemd-creds.html
> > > > [2] https://systemd.io/CREDENTIALS/
> > > > 
> > > > Signed-off-by: Arun Menon <armenon@redhat.com>
> > 
> 
> [...]
> 
> > > > +[Unit]
> > > > +Requires=virt-secret-init-encryption.service
> > > > +After=virt-secret-init-encryption.service
> > > > +
> > > > +[Service]
> > > > +LoadCredentialEncrypted=secrets-encryption-key:@localstatedir@/lib/libvirt/secrets/secrets-encryption-key
> > > > +Environment=SECRETS_ENCRYPTION_KEY=%d/secrets-encryption-key
> > > 
> > > This will likely be needed also for the monolithic daemon's unit
> > > (libvirtd.service) as that can also start the secret driver in those
> > > setups.
> > 
> > Hmm, its been a whle since we introduced the modular daemons, and we've
> > changed Fedora 4 years ago, and changed it in RHEL-9 too. Wonder about
> > status of other distros ?
> > 
> > Perhaps not right now, but we should likely consider whether we want to
> > eventually remove libvirtd or keep it around forever. Preferrably the
> > former so we stop having to think about both deployment scenarios for
> > changes like this in future.
> 
> Well, if you ask me I prefer libvirtd for my dev setup (and thus run it
> on almost all my machines) as it's easier to setup debuggers.

I'm curious what makes it easier ?  Are you debugging across many drivers
concurrently ?

Normally I'm only focusing on just 1 of the daemons, and so I tend to
use

  sudo  ./run  virtqemud

which stops virtqemud.service & runs the in-tree build, while leaving
the other virtnetworkd, virtnodedevd, etc runnnig the RPM builds.

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 :|

Re: [RFC v3 2/5] secret: Set up default encryption secret key for the virtsecretd service
Posted by Peter Krempa via Devel 2 weeks ago
On Thu, Nov 27, 2025 at 15:15:37 +0000, Daniel P. Berrangé wrote:
> On Thu, Nov 27, 2025 at 04:12:59PM +0100, Peter Krempa wrote:
> > On Thu, Nov 27, 2025 at 15:00:08 +0000, Daniel P. Berrangé wrote:

[...]

> > Well, if you ask me I prefer libvirtd for my dev setup (and thus run it
> > on almost all my machines) as it's easier to setup debuggers.
> 
> I'm curious what makes it easier ?  Are you debugging across many drivers
> concurrently ?
> 
> Normally I'm only focusing on just 1 of the daemons, and so I tend to
> use
> 
>   sudo  ./run  virtqemud
> 
> which stops virtqemud.service & runs the in-tree build, while leaving
> the other virtnetworkd, virtnodedevd, etc runnnig the RPM builds.

Well ... the times when I was debugging cross-daemon interaction (which
can be counted by fingers on one hand) it made stuff much easier.

Indeed with the run script it's much easier nowadays to run in-tree
builds instead of the system without much hassle.

Actually the currently worst hurdle for me is muscle memory (and some
nostalgia).