Message ID | cover.1740142328.git.ludo@gnu.org |
---|---|
Headers |
Return-Path: <guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org> X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id E9A8C27BBEC; Fri, 21 Feb 2025 13:07:51 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED,RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE, SPF_HELO_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id 40A7B27BBE2 for <patchwork@mira.cbaines.net>; Fri, 21 Feb 2025 13:07:51 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from <guix-patches-bounces@gnu.org>) id 1tlSkP-0004uu-AL; Fri, 21 Feb 2025 08:07:17 -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 <Debian-debbugs@debbugs.gnu.org>) id 1tlSkL-0004uF-3A for guix-patches@gnu.org; Fri, 21 Feb 2025 08:07:13 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <Debian-debbugs@debbugs.gnu.org>) id 1tlSkJ-0004bO-ET for guix-patches@gnu.org; Fri, 21 Feb 2025 08:07:11 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:In-Reply-To:References:Subject; bh=FLgFZ91ZIbk+/ybIGvHytOF4LNHGgU0XyA12luQqJT8=; b=HH4YEYr5BBxR6Z8anoecmy9gKOH6I4MQ8CjzWiJw4fiYpTbUxwgoHoAl6c4yO6JG3ZhNpzeU7jnMlUDGt3xOQd+cXfW1JbSgSY2TbUj1cR+sk7frxXCdfvqUZint4jxSVeQVcIkRBXvkymcdnx4xJCbFFVqQCENfwweKz28lYPE8G3V5u73EHSuBmZgiWceKhDazaR6AolCkMztaJ9fivCfIjW8oceDmMWhtrx/IdXRsRXUuOTb8dhL424MNW0rlTKoJ2uGAVXf2Ti37VD39l69mjxA0IQEK6YiTUDKkrgq28B5T1kTyRsFYL2ej1T1wiympXgg88zsFHPSaGIiEGw==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from <Debian-debbugs@debbugs.gnu.org>) id 1tlSkJ-0005TI-5r for guix-patches@gnu.org; Fri, 21 Feb 2025 08:07:11 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#75810] [PATCH v3 00/11] Rootless guix-daemon References: <cover.1737738362.git.ludo@gnu.org> In-Reply-To: <cover.1737738362.git.ludo@gnu.org> Resent-From: Ludovic =?utf-8?q?Court=C3=A8s?= <ludo@gnu.org> Original-Sender: "Debbugs-submit" <debbugs-submit-bounces@debbugs.gnu.org> Resent-CC: guix-patches@gnu.org Resent-Date: Fri, 21 Feb 2025 13:07:11 +0000 Resent-Message-ID: <handler.75810.B75810.174014322320936@debbugs.gnu.org> Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 75810 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 75810@debbugs.gnu.org Cc: Ludovic =?utf-8?q?Court=C3=A8s?= <ludo@gnu.org>, Reepca Russelstein <reepca@russelstein.xyz> Received: via spool by 75810-submit@debbugs.gnu.org id=B75810.174014322320936 (code B ref 75810); Fri, 21 Feb 2025 13:07:11 +0000 Received: (at 75810) by debbugs.gnu.org; 21 Feb 2025 13:07:03 +0000 Received: from localhost ([127.0.0.1]:56656 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from <debbugs-submit-bounces@debbugs.gnu.org>) id 1tlSjw-0005Pg-Ns for submit@debbugs.gnu.org; Fri, 21 Feb 2025 08:07:01 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:48260) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from <ludo@gnu.org>) id 1tlSjn-0005Mw-B5 for 75810@debbugs.gnu.org; Fri, 21 Feb 2025 08:06:43 -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@gnu.org>) id 1tlSjg-0004Xo-6E; Fri, 21 Feb 2025 08:06: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:Date:Subject:To:From:in-reply-to: references; bh=FLgFZ91ZIbk+/ybIGvHytOF4LNHGgU0XyA12luQqJT8=; b=igemvpQq642Lm3 Z4vigLUyM2kfDBEpo1GIGyy9JhyEqcj9xpivy3fuWxQjYdSVlDR6j3tB8/IhjW9l/GE3rdhBP04cX QZKix/bSfW7iWWVn8Cr5PT/D5WT/5WPLN4PWdkzz1Ci6otfy2jV7tuhJltG1Kbh4tt0KdW43SryTV 3wBKpLOPUMHIK28Wv52gyr7bwNmXn3+9C5jTKsG6iPpkBEukmuNWIICe7YIQNHdWES7IzXpq7kim0 yyJ/fGIlvwDdd7U01t/n9qh9oIjbC+xNa6xhQFC+FJ4u6DOVWwbp4A43OQgqdiP8v4U+f64xr8Dwl q3jIadW+kUdJL1k0zbYQ==; From: Ludovic =?utf-8?q?Court=C3=A8s?= <ludo@gnu.org> Date: Fri, 21 Feb 2025 14:05:48 +0100 Message-ID: <cover.1740142328.git.ludo@gnu.org> X-Mailer: git-send-email 2.48.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: <guix-patches.gnu.org> List-Unsubscribe: <https://lists.gnu.org/mailman/options/guix-patches>, <mailto:guix-patches-request@gnu.org?subject=unsubscribe> List-Archive: <https://lists.gnu.org/archive/html/guix-patches> List-Post: <mailto:guix-patches@gnu.org> List-Help: <mailto:guix-patches-request@gnu.org?subject=help> List-Subscribe: <https://lists.gnu.org/mailman/listinfo/guix-patches>, <mailto:guix-patches-request@gnu.org?subject=subscribe> Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches |
Series |
Rootless guix-daemon
|
|
Message
Ludovic Courtès
Feb. 21, 2025, 1:05 p.m. UTC
Hello! Here’s an updated version, addressing most issues brought up by Reepca, also available from <https://codeberg.org/civodul/guix/src/branch/wip-rootless-daemon>. Main changes compared to v2: • Derivation inputs and / are mounted read-only; additional tests check the ability to write to these, to /tmp, to /dev/{full,null}, and to remount any of these as read-write. • Unit files for systemd tweaked so that (1) guix-daemon sees a private read-write mount of the store, and (2) gnu-store.mount actually remounts the store read-only after guix-daemon has started. • ‘DerivationGoal::deleteTmpDir’ bails out when it fails to chown ‘tmpDir’ (i.e., it does not try to “pivot” the /top sub-directory). Did I forget anything, Reepca? The one observable difference compared to current guix-daemon operational mode is that, in the build environment, writing to the root file system results in EROFS instead of EPERM, as you pointed out earlier. That’s not great but probably acceptable. We’ll only know whether this is a problem in practice once we’ve run the test suites of tens of thousands of packages. I tested this patch series by: • running ‘make check’; • manually running ‘guix-install.sh’ in a Debian VM, as explained before. Next up: • automating ‘guix-install.sh’ VM tests; • updating ‘guix-service-type’ to optionally support unprivileged guix-daemon. I think these two bits can come later though. Thoughts? Ludo’. Ludovic Courtès (11): daemon: Use ‘close_range’ where available. daemon: Bind-mount all the inputs, not just directories. daemon: Remount inputs as read-only. daemon: Remount root directory as read-only. daemon: Allow running as non-root with unprivileged user namespaces. 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. daemon: Move comments where they belong. 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/gnu-store.mount.in | 3 +- etc/guix-daemon.service.in | 20 +++- etc/guix-install.sh | 108 ++++++++++++++---- guix/substitutes.scm | 4 +- nix/libstore/build.cc | 219 ++++++++++++++++++++++++++---------- nix/libstore/local-store.cc | 30 +++-- nix/libutil/util.cc | 23 +++- tests/processes.scm | 9 +- tests/store.scm | 206 +++++++++++++++++++++++++++------ 11 files changed, 494 insertions(+), 147 deletions(-) base-commit: 00787cd61611d74d3e54b160e94176905d36ef39
Comments
Hi Ludo, On Fri, 21 Feb 2025 at 14:05, Ludovic Courtès <ludo@gnu.org> wrote: > The one observable difference compared to current guix-daemon > operational mode is that, in the build environment, writing to > the root file system results in EROFS instead of EPERM, as you > pointed out earlier. That’s not great but probably acceptable. > We’ll only know whether this is a problem in practice once we’ve > run the test suites of tens of thousands of packages. Clearly, I do not fully understand all the deep details of all the series. Quoting Janneke [1]: 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? I’m not sure to get what’s the answer now with the v3? Especially when connected to this other question: Will there be an option for users to choose between a non-root guix-daemon or a read-only store? Where the answer, IIUC, is no. Could you clarify the status about the store when running guix-daemon as root on foreign distros? Or maybe now, will guix-daemon always run as a regular user on foreign distros? From an user perspective, instead of running guix-daemon as root, now guix-daemon will run as the regular user named ’guix-daemon’ without any special privileges, right? User still need root privileges once at guix-install.sh time but not more. Therefore, for updating the guix-daemon, the user guix-daemon needs to run “guix pull“ and restart the service, right? If yes, cool! It’ll be a booster for cluster sysadmins. :-) Cheers, simon 1: [bug#75810] [PATCH 0/6] Rootless guix-daemon Janneke Nieuwenhuizen <janneke@gnu.org> Fri, 24 Jan 2025 20:20:42 +0100 id:87ikq49fxx.fsf@gnu.org https://issues.guix.gnu.org/75810 https://issues.guix.gnu.org/msgid/87ikq49fxx.fsf@gnu.org https://yhetil.org/guix/87ikq49fxx.fsf@gnu.org
Ludovic Courtès <ludo@gnu.org> writes: > Hello! > > Here’s an updated version, addressing most issues brought up > by Reepca, also available from > <https://codeberg.org/civodul/guix/src/branch/wip-rootless-daemon>. > Main changes compared to v2: > > • Derivation inputs and / are mounted read-only; additional > tests check the ability to write to these, to /tmp, to > /dev/{full,null}, and to remount any of these as read-write. > > • Unit files for systemd tweaked so that (1) guix-daemon sees > a private read-write mount of the store, and (2) gnu-store.mount > actually remounts the store read-only after guix-daemon has > started. I'm not familiar with how systemd does service dependencies, but does this mean that the store becomes writable when the daemon is stopped? > > • ‘DerivationGoal::deleteTmpDir’ bails out when it fails to > chown ‘tmpDir’ (i.e., it does not try to “pivot” the /top > sub-directory). > > Did I forget anything, Reepca? I believe that if you try a "--keep-failed" build that fails in the CAP_CHOWN case, you'll find that only root or the guix-daemon user can delete the kept build directory, though the user that started the build can delete everything inside it. This is because in that case the build directory was chown'ed back to guix-daemon so that it could be moved, but wasn't chown'ed to the client user afterward. If I recall correctly there was code included to perform this extra chown in the (getuid() != 0) case in the v2 series - was it accidentally forgotten? Also, there are potential issues with how wide the scope of the try block in DerivationGoal::deleteTmpDir is - _chown isn't the only place within it that can raise a SysError, and there are failure modes present that may merit more user attention than lvlInfo. For example, if rename((pivot + "/top").c_str(), top.c_str()) fails (which can be rather easily arranged by a local attacker), then the build directory path reported in the "note: keeping build directory" message remains up for grabs by anyone. If the user doesn't go out of their way to verify that the build directory isn't attacker-controlled, they could be rather easily tricked into executing malicious code. But currently the exception from this rename failing will be turned into a lvlInfo message, and I'm not sure how that interacts with the verbosity defaults in the various CLI programs. This does somewhat raise the question of why we're even doing the pivoting in a way that creates a window during which failure can be induced. For example, we could move the inner build directory to the pivot path, at which point the outer build directory should become empty, so it should work to then rename the pivot path to the outer build directory path, thereby atomically replacing it. Also, in the unprivileged case (non-root, no CAP_CHOWN), the build directory never gets pivoted out. This is better for security than the previous situation (which allowed setuid programs to be exposed), but it should be quite doable to simply secure the file permissions first and then carry on with the pivot. I believe I previously mentioned perhaps using secureFilePerms to do this? It may work well to use the v2 patch for this with a call to secureFilePerms added right before the try block and a have_cap_chown boolean flag being saved for later recall after the pivot instead of the (getuid() != 0) check. That way in the fully-unprivileged case it doesn't successfully pivot the now-sanitized build directory only to immediately fail to chown it. Actually, because that chown call doesn't result in an exception on failure, it would also work to only add the secureFilePerms call. Also, I've discovered that while mount(2) uses EPERM for both a locked mount point and insufficient privileges, umount(2) uses EINVAL for the former and EPERM for the latter. This may be a good way to test that we're triggering the mount-locking behavior as intended. > The one observable difference compared to current guix-daemon > operational mode is that, in the build environment, writing to > the root file system results in EROFS instead of EPERM, as you > pointed out earlier. That’s not great but probably acceptable. > We’ll only know whether this is a problem in practice once we’ve > run the test suites of tens of thousands of packages. Strictly speaking, it's also observable that the root file system, store, /tmp, etc is not owned by uid 0, and that the input store items are all mounted read-only. - reepca
Ludovic Courtès <ludo@gnu.org> skribis: > Next up: > > • automating ‘guix-install.sh’ VM tests; Done in <https://issues.guix.gnu.org/76488>. Ludo’.
Hi, Simon Tournier <zimon.toutoune@gmail.com> skribis: > Quoting Janneke [1]: > > I'm kind of afraid that having a writable /gnu/store, even if it's just > on foreign distributions, This problem is fixed in v3: the store will be remounted readonly as is currently the case. > Could you clarify the status about the store when running guix-daemon as > root on foreign distros? Or maybe now, will guix-daemon always run as a > regular user on foreign distros? As currently written, guix-daemon will always run as non-root on foreign distros (on systemd-based distros specifically.) >>From an user perspective, instead of running guix-daemon as root, now > guix-daemon will run as the regular user named ’guix-daemon’ without any > special privileges, right? Correct. > User still need root privileges once at guix-install.sh time but not > more. Therefore, for updating the guix-daemon, the user guix-daemon > needs to run “guix pull“ and restart the service, right? The upgrade procedure remains unchanged: you would run ‘guix pull’ as root and restart the service¹ (the service itself runs as user ‘guix-daemon’). > If yes, cool! It’ll be a booster for cluster sysadmins. :-) Yup! Ludo’. ¹ https://guix.gnu.org/manual/devel/en/html_node/Upgrading-Guix.html
Hi, Reepca Russelstein <reepca@russelstein.xyz> skribis: >> • Unit files for systemd tweaked so that (1) guix-daemon sees >> a private read-write mount of the store, and (2) gnu-store.mount >> actually remounts the store read-only after guix-daemon has >> started. > > I'm not familiar with how systemd does service dependencies, but does > this mean that the store becomes writable when the daemon is stopped? I had to check because it’s not crystal clear. ‘systemctl stop guix-daemon’ also stops ‘gnu-store.mount’. But then you can do ‘systemctl start gnu-store.mount’, which does *not* start guix-daemon; at that point, ‘systemctl start guix-daemon’ spawns guix-daemon, but it cannot write to the store. It’s messy, but I don’t know how to do better. [...] > It may work well to use the v2 patch for this with a call to > secureFilePerms added right before the try block and a have_cap_chown > boolean flag being saved for later recall after the pivot instead of the > (getuid() != 0) check. That way in the fully-unprivileged case it > doesn't successfully pivot the now-sanitized build directory only to > immediately fail to chown it. Actually, because that chown call doesn't > result in an exception on failure, it would also work to only add the > secureFilePerms call. I went back to v2 + ‘secureFilePerms’ call. > Also, I've discovered that while mount(2) uses EPERM for both a locked > mount point and insufficient privileges, umount(2) uses EINVAL for the > former and EPERM for the latter. This may be a good way to test that > we're triggering the mount-locking behavior as intended. The tests try to MS_REMOUNT the inputs, which is exactly what we want to prevent; we could test the low-level semantics you describe, but it’s quite obscure and maybe unnecessary given that we test MS_REMOUNT? >> The one observable difference compared to current guix-daemon >> operational mode is that, in the build environment, writing to >> the root file system results in EROFS instead of EPERM, as you >> pointed out earlier. That’s not great but probably acceptable. >> We’ll only know whether this is a problem in practice once we’ve >> run the test suites of tens of thousands of packages. > > Strictly speaking, it's also observable that the root file system, > store, /tmp, etc is not owned by uid 0, and that the input store items > are all mounted read-only. Right. I’ll send v4 shortly. Thanks again for your feedback! Ludo’.