Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon

From: Ludovic Courtès <[email-masked]>
Date: Fri, 24 Jan 2025 18:23:08 +0100

From: Ludovic Courtès <[email-masked]>

Hello Guix!

That guix-daemon runs as root is not confidence-inspiring for many.
Initially, the main reason for running it as root was, in the absence
of user namespaces, the fact that builders would be started under one
of the build user accounts, which only root can do.  Now that
unprivileged user namespaces are almost ubiquitous (even on HPC
clusters), this is no longer a good reason.

This patch changes guix-daemon so it can run as an unprivileged
user, using unprivileged user namespaces to still support isolated
builds.  There’s a couple of cases where root is/was still necessary:

  1. To create /var/guix/profiles/per-user/$USER and chown it
     as $USER (see CVE-2019-18192).

  2. To chown /tmp/guix-build-* when using ‘--keep-failed’.

Both can be addressed by giving CAP_CHOWN to guix-daemon, and this is
what this patch series does on distros using systemd.  (For some
reason CAP_CHOWN had to be added to the set of “ambient capabilities”,
which are inherited by child processes; this is why there’s a patch
to drop ambient capabilities in build processes.)

On Guix System (not implemented here), we could address (1) by
creating /var/guix/profiles/per-user/$USER upfront for all the
user accounts.  We could leave (2) unaddressed (so failed build
directories would be owned by guix-daemon:guix-daemon) or we’d
have to pass CAP_CHOWN as well.

There’s another issue: /gnu/store can no longer be remounted
read-only (like we do on Guix System and on systemd with
‘gnu-store.mount’) because then unprivileged guix-daemon would
be unable to remount it read-write (or at least I couldn’t find
a way to do that).  Thus ‘guix-install.sh’ no longer installs
‘gnu-store.mount’ in that case.  It’s a bit sad to lose that
so if anyone can think of a way to achieve it, that’d be great.

I tested all this in a Debian VM¹, along these lines:

  1. GUIX_ALLOW_ME_TO_USE_PRIVATE_COMMIT=yes make update-guix-package
  2. ./pre-inst-env guix pack -C zstd guix --without-tests=guix \
        --localstatedir --profile-name=current-guix
  3. Copy ‘guix-install.sh’ and the tarball to the VM over SSH.
  4. In the VM: GUIX_BINARY_FILE_NAME=pack.tar.zst ./guix-install.sh

The next step (in another patch series) would be Guix System support
with automatic transition (essentially “chown -R
guix-daemon:guix-daemon /gnu/store”).

Thoughts?

Ludo’.

¹ https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-12.9.0-amd64-standard.iso

Ludovic Courtès (6):
  daemon: Allow running as non-root with unprivileged user namespaces.
  DRAFT tests: Run in a chroot and unprivileged user namespaces.
  daemon: Create /var/guix/profiles/per-user unconditionally.
  daemon: Drop Linux ambient capabilities before executing builder.
  etc: systemd services: Run ‘guix-daemon’ as an unprivileged user.
  guix-install.sh: Support the unprivileged daemon where possible.

 build-aux/test-env.in       |  14 +++-
 config-daemon.ac            |   2 +-
 etc/guix-daemon.service.in  |  12 +++-
 etc/guix-install.sh         | 114 ++++++++++++++++++++++++-------
 guix/substitutes.scm        |   4 +-
 nix/libstore/build.cc       | 132 ++++++++++++++++++++++++++++++------
 nix/libstore/local-store.cc |  30 +++++---
 tests/store.scm             |  89 ++++++++++++++----------
 8 files changed, 300 insertions(+), 97 deletions(-)

base-commit: bc6769f1211104dbc9341c064275cd930f5dfa3a
-- 
2.47.1