Message ID | 87sen55a86.fsf@gnu.org |
---|---|
State | New |
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 896F327BBE9; Sat, 22 Mar 2025 15:59:16 +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=unavailable 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 920C827BBE2 for <patchwork@mira.cbaines.net>; Sat, 22 Mar 2025 15:59:14 +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 1tw1Fc-00071Q-Vz; Sat, 22 Mar 2025 11:59:09 -0400 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 1tw1FX-00070v-8q for guix-patches@gnu.org; Sat, 22 Mar 2025 11:59:06 -0400 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 1tw1FW-0006fg-2U for guix-patches@gnu.org; Sat, 22 Mar 2025 11:59:02 -0400 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:References:In-Reply-To:From:To:Subject; bh=dejlCC7VuaOU8Tp+QpyWfbh4dWhHwyViELFNXJdoyUI=; b=dYm+i1ov1o4H/LNrwMnnWqVNUuzYtgbRPfDWW1zc4syBMdtM4G6NVgvsX+vw9l0B0oP/D57oHQaLeJonaCu/2vkho5PNCg+kfqis6Seu6tC1wSMBorvmKCxmu3L+LA48pLfkRolFvgRneZdkuH5Ekyrq900DGommQujqCgPy1d6p6iycOCSvoziCirRRfOKkU7AsnaQgqGTKCZqNExH59nNE+bGvQZvqdxkQwL54EAwRj+jtqb3xH1OD4E3PoRwg42mZjHPqJZ/QLDJMJtH1Ry2CDmuPegGnM3UTOPHXN9wLE1MiS0qHK7/SwU5dP6klnrfE2N6NYOobnq6bRrnQXA==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from <Debian-debbugs@debbugs.gnu.org>) id 1tw1FV-0000Np-UR for guix-patches@gnu.org; Sat, 22 Mar 2025 11:59:01 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#75810] [PATCH v6 00/16] Rootless guix-daemon 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: Sat, 22 Mar 2025 15:59:01 +0000 Resent-Message-ID: <handler.75810.B75810.17426590831406@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: Reepca Russelstein <reepca@russelstein.xyz> Cc: 75810@debbugs.gnu.org Received: via spool by 75810-submit@debbugs.gnu.org id=B75810.17426590831406 (code B ref 75810); Sat, 22 Mar 2025 15:59:01 +0000 Received: (at 75810) by debbugs.gnu.org; 22 Mar 2025 15:58:03 +0000 Received: from localhost ([127.0.0.1]:44829 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from <debbugs-submit-bounces@debbugs.gnu.org>) id 1tw1EY-0000Mb-NQ for submit@debbugs.gnu.org; Sat, 22 Mar 2025 11:58:03 -0400 Received: from hera.aquilenet.fr ([185.233.100.1]:49184) 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 1tw1ER-0000Lv-OJ for 75810@debbugs.gnu.org; Sat, 22 Mar 2025 11:58:00 -0400 Received: from localhost (localhost [127.0.0.1]) by hera.aquilenet.fr (Postfix) with ESMTP id 0BB74D5D; Sat, 22 Mar 2025 16:57:48 +0100 (CET) Authentication-Results: hera.aquilenet.fr; none X-Virus-Scanned: Debian amavis at hera.aquilenet.fr Received: from hera.aquilenet.fr ([127.0.0.1]) by localhost (hera.aquilenet.fr [127.0.0.1]) (amavis, port 10024) with ESMTP id WdJmg-tfMJ3f; Sat, 22 Mar 2025 16:57:47 +0100 (CET) Received: from ribbon (91-160-117-201.subs.proxad.net [91.160.117.201]) by hera.aquilenet.fr (Postfix) with ESMTPSA id A4C4A7E7; Sat, 22 Mar 2025 16:57:45 +0100 (CET) From: Ludovic =?utf-8?q?Court=C3=A8s?= <ludo@gnu.org> In-Reply-To: <87iko2gpgd.fsf@russelstein.xyz> (Reepca Russelstein's message of "Fri, 21 Mar 2025 14:21:06 -0500") References: <cover.1742230219.git.ludo@gnu.org> <875xk7594u.fsf@russelstein.xyz> <87v7s6h21b.fsf@gnu.org> <871puu53mf.fsf@russelstein.xyz> <87tt7na08n.fsf@gnu.org> <87iko2gpgd.fsf@russelstein.xyz> Date: Sat, 22 Mar 2025 16:57:45 +0100 Message-ID: <87sen55a86.fsf@gnu.org> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Rspamd-Queue-Id: 0BB74D5D X-Spamd-Result: default: False [4.90 / 15.00]; SPAM_FLAG(5.00)[]; BAYES_HAM(-3.00)[100.00%]; NEURAL_SPAM(3.00)[1.000]; MIME_GOOD(-0.10)[multipart/mixed,text/plain,text/x-patch]; RCVD_COUNT_TWO(0.00)[2]; FROM_EQ_ENVFROM(0.00)[]; MIME_TRACE(0.00)[0:+,1:+,2:+,3:+]; RCPT_COUNT_TWO(0.00)[2]; ARC_NA(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; RCVD_TLS_ALL(0.00)[]; FROM_HAS_DN(0.00)[]; TO_DN_SOME(0.00)[]; MID_RHS_MATCH_FROM(0.00)[] X-Rspamd-Action: no action X-Spamd-Bar: ++++ X-Rspamd-Server: hera 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 |
Commit Message
Ludovic Courtès
March 22, 2025, 3:57 p.m. UTC
Hi Reepca, Reepca Russelstein <reepca@russelstein.xyz> skribis: > Ludovic Courtès <ludo@gnu.org> writes: > >>> While ensuring that what actually gets execve'd is in the store suffices >>> to eliminate the vulnerability, it may be "conceptually purer" to >>> require that the links pointing to it are all in the store as well. For >>> example, while a builder that is a symlink pointing to /proc/self/exe >>> wouldn't be able to modify the daemon binary, it's still a piece of >>> basically "undefined behavior" as far as the build environment is >>> concerned, which could be closed up. But that can come later just as >>> well. >> >> Yes. But in practice, “normal” symlinks (i.e., not /proc/self/exe) will >> lead ‘canonPath’ to throw if one component is outside the store, since >> ‘canonPath’ operates within the chroot. > > Unless the component actually exists and is outside of the store. If we > just rely on canonPath throwing an exception to be safe, then if there > ever arose a situation where a non-symlink executable existed outside of > the store, it would still be possible to convince the daemon to execute > it. [...] > I mention this because I see that patch 07/16 of v7 has left out the > isInStore check, and I think it should remain. Hmm right (I was very much assuming that /proc/self/exe was the only non-store executable, but better be safe than sorry). Re-adding this: > While researching container escape vulnerabilities, I recently came > across CAP_DAC_READ_SEARCH and open_by_handle_at, which is a system call > so insanely powerful it is outright banned in all but the root user > namespace. Or at least, it was. 10 months ago, in commit > 620c266f394932e5decc4b34683a75dfc59dc2f4 of > https://github.com/torvalds/linux, the requirements were relaxed so > that, in certain cases, processes in non-root user namespaces could use > open_by_handle_at. The way ‘open_by_handle_at’ is documented (“half” of ‘openat’) does not make it immediately obvious to me what makes it “powerful”. I see the risk of a confused deputy problem though because of the ‘mount_id’ argument in addition to ‘handle’. Is that what you have in mind? > The consequences of this for same-user containers are not clear to me > yet, as I haven't studied the kernel source enough to know what exactly > that commit message means by "privileges over the filesystem" or > "privileges over a subtree". I also haven't been able to test this > behavior yet, because my kernel is actually too old (I do my rebases and > upgrades rather less regularly than is recommended). I'll try to look > into this more once I update my system (and man-pages!), but figured I > should mention it, because aside from that, and the aforementioned > isInStore check, I can't think of any remaining concerns. Alright. I’ll send v8 with the change above. Thanks again! Ludo’.
Comments
Ludovic Courtès <ludo@gnu.org> writes: >> While researching container escape vulnerabilities, I recently came >> across CAP_DAC_READ_SEARCH and open_by_handle_at, which is a system call >> so insanely powerful it is outright banned in all but the root user >> namespace. Or at least, it was. 10 months ago, in commit >> 620c266f394932e5decc4b34683a75dfc59dc2f4 of >> https://github.com/torvalds/linux, the requirements were relaxed so >> that, in certain cases, processes in non-root user namespaces could use >> open_by_handle_at. > > The way ‘open_by_handle_at’ is documented (“half” of ‘openat’) does not > make it immediately obvious to me what makes it “powerful”. I see the > risk of a confused deputy problem though because of the ‘mount_id’ > argument in addition to ‘handle’. Is that what you have in mind? The handle is a purely user-space sequence of bytes, and is not namespaced whatsoever. In other words, the first "half" (that is, name_to_handle_at) is completely optional, as long as you have a good idea of what sort of handle values to try. This means that, if a process has this capability in the root user namespace, they can potentially access every file of any filesystem that has at least one file visible to them. Note that "filesystem" here is not the same thing as "mount point", so this means that if you have a bind mount from the root filesystem in the container (or the root filesystem itself in the container is on the out-of-container root filesystem), a process in the container but with CAP_DAC_READ_SEARCH in the root user namespace could access *every file on the real root filesystem*. This is how an exploit for Docker named "shocker" worked (http://stealth.openwall.net/xSports/shocker.c), caused by Docker leaving CAP_DAC_READ_SEARCH available by default in privileged containers. I of course hope that the kernel's relaxing of the rules to also allow open_by_handle_at in some situations in non-root user namespaces has been carefully thought through to not open any holes like this, but it would be good to keep an eye on it regardless. - reepca
Hello, Reepca Russelstein <reepca@russelstein.xyz> skribis: > The handle is a purely user-space sequence of bytes, and is not > namespaced whatsoever. In other words, the first "half" (that is, > name_to_handle_at) is completely optional, as long as you have a good > idea of what sort of handle values to try. This means that, if a > process has this capability in the root user namespace, they can > potentially access every file of any filesystem that has at least one > file visible to them. Note that "filesystem" here is not the same thing > as "mount point", so this means that if you have a bind mount from the > root filesystem in the container (or the root filesystem itself in the > container is on the out-of-container root filesystem), a process in the > container but with CAP_DAC_READ_SEARCH in the root user namespace could > access *every file on the real root filesystem*. This is how an exploit > for Docker named "shocker" worked > (http://stealth.openwall.net/xSports/shocker.c), caused by Docker > leaving CAP_DAC_READ_SEARCH available by default in privileged > containers. Ouch. I think the conceptual quagmire stemming from the accumulation of features retrofitted on the otherwise simpler 1970 Unix model doesn’t help: mount points, file systems, namespaces, shared/locked/private/slave subtrees, “capabilities”, ACLs, etc. It’s intractable. > I of course hope that the kernel's relaxing of the rules to also allow > open_by_handle_at in some situations in non-root user namespaces has > been carefully thought through to not open any holes like this, but it > would be good to keep an eye on it regardless. Indeed. :-/ Ludo’. PS: Just sent v8.
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc index 1733322316..d0fcc99854 100644 --- a/nix/libstore/build.cc +++ b/nix/libstore/build.cc @@ -2390,6 +2390,9 @@ void DerivationGoal::runChild() within the chroot. */ builderBasename = baseNameOf(drv.builder); drv.builder = canonPath(drv.builder, true); + + if (!isInStore(drv.builder)) + throw Error(format("derivation builder '%1%' is outside the store") % drv.builder); } /* Fill in the arguments. */