From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id yNldGU1kpmbbJQEAqHPOHw:P1 (envelope-from ) for ; Sun, 28 Jul 2024 15:31:25 +0000 Received: from aspmx1.migadu.com ([2001:41d0:403:4876::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id yNldGU1kpmbbJQEAqHPOHw (envelope-from ) for ; Sun, 28 Jul 2024 17:31:25 +0200 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=uWwVp0PF; dkim=fail ("body hash did not verify") header.d=autistici.org header.s=stigmate header.b=VuxE4p9i; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gnu.org ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1722180681; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=pRExfj/QbBRUMmyqXxH5DWCtYJKWyhIRUEKk/qE3SMA=; b=K5MdikvdkKEL3rZDLazCjOXfyz6edOT4TTE4IFPY7r8bkCTWwB6VXkHoTvLmNuXr+6S4/v kfLhOwvhnQeMQj8mho33L77nC7yu5J7W01kLSaj2D3DoHBeWrGH1QQBVO6Z5q3nasvrfhN 8pb42DjLW2YXG1FcwNfX5G5QuxBW6EBxINm5BXvQE1mYzJEpO7MrO00q1DgAInmIpB2r97 K1wjGA7Kw7/XnyHdANbBWXTnPBBBXytoBMN7uvvmht65E7p43NFGyWbI2rebIjZXkHb8wa dpQ6T/eWVMVvnvHOSct7Y/f+6O547KXjS33taWPXhtc11Fw8NTkl4cLvVtwUoA== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=uWwVp0PF; dkim=fail ("body hash did not verify") header.d=autistici.org header.s=stigmate header.b=VuxE4p9i; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gnu.org ARC-Seal: i=1; s=key1; d=yhetil.org; t=1722180681; a=rsa-sha256; cv=none; b=LjzvwA0a1YXiJ4yrTY1EDF0YOh0Cf5kgfHwSVveiLEVrG+XGOqzXIxuNgefM2F/q/aAey6 tK6hWKdz96r6qObkuKYfaHLRa4uoCjHbp+mrU9aX2MlzJpgXNZ6IBtZX068Xrk+zdDMnLz m8WY5xbookvEqTVSCijrdkC7HhfmFnPN9H2AfjbPTocbZNQnpsssm6DC46iGw8LOd0cbXO iPjgpuBLf+V+wNESfAoQGX5HrRN9sSWiZaPMweTS4X5mcZ9V3Fl2qeKfNckfsvPIS/FqMO iiYrMTDrWPdRPIfre0p9WsLGjAUrQ2AnYkDJKWqbWidWYaKD7eVUqw6uozPiMA== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 6C1E1212A4 for ; Sun, 28 Jul 2024 17:31:21 +0200 (CEST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sY5rN-0002aK-LK; Sun, 28 Jul 2024 11:30:57 -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 ) id 1sY5rL-0002Zu-Mi for guix-patches@gnu.org; Sun, 28 Jul 2024 11:30:55 -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 ) id 1sY5rL-0006Kv-Cp; Sun, 28 Jul 2024 11:30:55 -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:References:In-Reply-To:Date:From:To:Subject; bh=oLTegUcVJfuhvYpKCRVt4khbkZ45x/yMeGCr8B/bx3A=; b=uWwVp0PFXrNe3udyilxdDo1AGNcqr6ifbRDHcKbPqBrLFRAz6neN+deCsmMN5Z5T0eEJSEC+7BPOZ6P9VjP9o6Y6ALGrrP8bbh9bEIHe13Q7USL1gQQy2nJFFBkBBya/SvWC0XIIANjoEurN/eE8UEdAlzkac1Cw4cy8h9NhJTnKAvOm6R/ZNL8D1jPRxSS33wHzo13gkQkA9ChXHI4+1Y3tNSKEIsXrX61XjyAWIMyTKPwSTm8q+uYAKuM5aPe3b1jsmKy7QLd+OqQ9hqBZVfo1S9PLdVdXOaQbdgKaXfzlCnTh09rBr1mnGjeTEppyMyvcZSWcAF+eeqaE6bqQUQ==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1sY5rT-00042r-93; Sun, 28 Jul 2024 11:31:03 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#72337] [PATCH 3/3] system: Add /etc/subuid and /etc/subgid support. Resent-From: Giacomo Leidi Original-Sender: "Debbugs-submit" Resent-CC: pelzflorian@pelzflorian.de, ludo@gnu.org, matt@excalamus.com, maxim.cournoyer@gmail.com, guix-patches@gnu.org Resent-Date: Sun, 28 Jul 2024 15:31:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 72337 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: To: 72337@debbugs.gnu.org Cc: Giacomo Leidi , Florian Pelz , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Matthew Trzcinski , Maxim Cournoyer X-Debbugs-Original-Xcc: Florian Pelz , Ludovic =?UTF-8?Q?Court=C3=A8s?= , Matthew Trzcinski , Maxim Cournoyer Received: via spool by 72337-submit@debbugs.gnu.org id=B72337.172218062815480 (code B ref 72337); Sun, 28 Jul 2024 15:31:03 +0000 Received: (at 72337) by debbugs.gnu.org; 28 Jul 2024 15:30:28 +0000 Received: from localhost ([127.0.0.1]:44240 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sY5qs-00041a-W0 for submit@debbugs.gnu.org; Sun, 28 Jul 2024 11:30:28 -0400 Received: from confino.investici.org ([93.190.126.19]:56797) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1sY5qp-00041R-Q5 for 72337@debbugs.gnu.org; Sun, 28 Jul 2024 11:30:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=autistici.org; s=stigmate; t=1722180604; bh=oLTegUcVJfuhvYpKCRVt4khbkZ45x/yMeGCr8B/bx3A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VuxE4p9iV45vsTYnZWBM/5msezNOs15+UUep3psLz/WA/JKSOd5Kqt3VUIneFnNjJ NP0+9BaybKBMH223BNYuvGLsPylGo7cRpA3iFZoNNZhHrkqiWzf1XiuQP/7raZ94ho 7DLE9MIAnDRN7BAWgvBCjJl1XimQlhfgV+2acDSg= Received: from mx1.investici.org (unknown [127.0.0.1]) by confino.investici.org (Postfix) with ESMTP id 4WX55m3Pzwz113C; Sun, 28 Jul 2024 15:30:04 +0000 (UTC) Received: from [93.190.126.19] (mx1.investici.org [93.190.126.19]) (Authenticated sender: goodoldpaul@autistici.org) by localhost (Postfix) with ESMTPSA id 4WX55m2VWDz112f; Sun, 28 Jul 2024 15:30:04 +0000 (UTC) Date: Sun, 28 Jul 2024 17:29:26 +0200 Message-ID: <6b97096800ebf51a666ab2ee93fd2fdec3c2c65c.1722180566.git.goodoldpaul@autistici.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <1901209e4998ad29192b6f73b1e2828bc5d6f90e.1722180566.git.goodoldpaul@autistici.org> References: <1901209e4998ad29192b6f73b1e2828bc5d6f90e.1722180566.git.goodoldpaul@autistici.org> 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: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Giacomo Leidi X-ACL-Warn: , Giacomo Leidi via Guix-patches From: Giacomo Leidi via Guix-patches via Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Country: US X-Migadu-Flow: FLOW_IN X-Spam-Score: -5.52 X-Migadu-Queue-Id: 6C1E1212A4 X-Migadu-Scanner: mx10.migadu.com X-Migadu-Spam-Score: -5.52 X-TUID: oxdagn9QX7Gx This commit adds a Guix System service to handle allocation of subuid and subgid requests. Users that don't care can just add themselves as a subid-range and don't need to specify anything but their user name. Users that care about specific ranges, such as possibly LXD, can specify a start and a count. * doc/guix.texi: Document the new service. * gnu/build/activation.scm (activate-subuids+subgids): New variable. * gnu/local.mk: Add gnu/tests/shadow.scm. * gnu/system/accounts.scm (sexp->subid-range): New variable. * gnu/system/shadow.scm (%root-subid): New variable; (subids-configuration): new record; (subid-range->gexp): new variable; (assert-valid-subids): new variable; (delete-duplicate-ranges): new variable; (subids-activation): new variable; (subids-extension): new record; (append-subid-ranges): new variable; (subids-extension-merge): new variable; (subids-service-type): new variable. * gnu/tests/shadow.scm (subids): New system test. Change-Id: I3755e1c75771220c74fe8ae5de1a7d90f2376635 --- doc/guix.texi | 171 ++++++++++++++++++++++++++++++++ gnu/build/activation.scm | 19 ++++ gnu/local.mk | 1 + gnu/system/accounts.scm | 10 ++ gnu/system/shadow.scm | 207 ++++++++++++++++++++++++++++++++++++++- gnu/tests/shadow.scm | 128 ++++++++++++++++++++++++ 6 files changed, 534 insertions(+), 2 deletions(-) create mode 100644 gnu/tests/shadow.scm diff --git a/doc/guix.texi b/doc/guix.texi index 9ba96af459..d0b2a5284c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -41582,6 +41582,177 @@ Miscellaneous Services @end deftp +@c %end of fragment + +@cindex Subids +@subsubheading Subid Service + +The @code{(gnu system shadow)} module exposes the +@code{subids-service-type}, its configuration record +@code{subids-configuration} and its extension record +@code{subids-extension}. + +With @code{subids-service-type}, subuids and subgids ranges can be reserved for +users that desire so: + +@lisp +(use-modules (gnu system shadow) ;for 'subids-service-type' + (gnu system accounts) ;for 'subid-range' + @dots{}) + +(operating-system + ;; @dots{} + (services + (list + (simple-service 'alice-bob-subids + subids-service-type + (subids-extension + (subgids + (list + (subid-range (name "alice")))) + (subuids + (list + (subid-range (name "alice")) + (subid-range (name "bob") + (start 100700))))))))) +@end lisp + +Users (definitely other services), usually, are supposed to extend the service +instead of adding subids directly to @code{subids-configuration}, unless the +want to change the default behavior for root. With default settings the +@code{subids-service-type} adds, if it's not already there, a configuration +for the root account to both @code{/etc/subuid} and @code{/etc/subgid}, possibly +starting at the minimum possible subid. Otherwise the root subuids and subgids +ranges are fitted wherever possible. + +The above configuration will yield the following: + +@example +# cat /etc/subgid +root:100000:65536 +alice:165536:65536 +# cat /etc/subuid +root:100000:700 +bob:100700:65536 +alice:166236:65536 +@end example + +@c %start of fragment + +@deftp {Data Type} subids-configuration + +With default settings the +@code{subids-service-type} adds, if it's not already there, a configuration +for the root account to both @code{/etc/subuid} and @code{/etc/subgid}, possibly +starting at the minimum possible subid. To disable the default behavior and +provide your own definition for the root subid ranges you can set to @code{#f} +the @code{add-root?} field: + +@lisp +(use-modules (gnu system shadow) ;for 'subids-service-type' + (gnu system accounts) ;for 'subid-range' + @dots{}) + +(operating-system + ;; @dots{} + (services + (list + (service subids-service-type + (subids-configuration + (add-root? #f) + (subgids + (subid-range (name "root") + (start 120000) + (count 100))) + (subuids + (subid-range (name "root") + (start 120000) + (count 100))))) + (simple-service 'alice-bob-subids + subids-service-type + (subids-extension + (subgids + (list + (subid-range (name "alice")))) + (subuids + (list + (subid-range (name "alice")) + (subid-range (name "bob") + (start 100700))))))))) +@end lisp + +Available @code{subids-configuration} fields are: + +@table @asis +@item @code{add-root?} (default: @code{#t}) (type: boolean) +Whether to automatically configure subuids and subgids for root. + +@item @code{subgids} (default: @code{'()}) (type: list-of-subid-ranges) +The list of @code{subid-range}s that will be serialized to @code{/etc/subgid}. +If a range doesn't specify a start it will be fitted based on its number of +requrested subids. If a range doesn't specify a count the default size +of 65536 will be assumed. + +@item @code{subuids} (default: @code{'()}) (type: list-of-subid-ranges) +The list of @code{subid-range}s that will be serialized to @code{/etc/subuid}. +If a range doesn't specify a start it will be fitted based on its number of +requrested subids. If a range doesn't specify a count the default size +of 65536 will be assumed. + +@end table + +@end deftp + +@c %end of fragment + +@c %start of fragment + +@deftp {Data Type} subids-extension + +Available @code{subids-extension} fields are: + +@table @asis + +@item @code{subgids} (default: @code{'()}) (type: list-of-subid-ranges) +The list of @code{subid-range}s that will be appended to +@code{subids-configuration-subgids}. Entries with the same name are deduplicated +upon merging. + +@item @code{subuids} (default: @code{'()}) (type: list-of-subid-ranges) +The list of @code{subid-range}s that will be appended to +@code{subids-configuration-subuids}. Entries with the same name are deduplicated +upon merging. + +@end table + +@end deftp + +@c %end of fragment + +@c %start of fragment + +@deftp {Data Type} subid-range + +The @code{subid-range} record is defined at @code{(gnu system accounts)}. +Available fields are: + +@table @asis + +@item @code{name} (type: string) +The name of the user or group that will own this range. + +@item @code{start} (default: @code{#f}) (type: integer) +The first requested subid. When false the first available subid with enough +contiguous subids will be assigned. + +@item @code{count} (default: @code{#f}) (type: integer) +The number of total allocated subids. When #f the default of 65536 will be +assumed . + +@end table + +@end deftp + @c %end of fragment @node Setuid Programs diff --git a/gnu/build/activation.scm b/gnu/build/activation.scm index d8c0cd22a3..943d72694f 100644 --- a/gnu/build/activation.scm +++ b/gnu/build/activation.scm @@ -9,6 +9,7 @@ ;;; Copyright © 2020 Christine Lemmer-Webber ;;; Copyright © 2021 Brice Waegeneire ;;; Copyright © 2024 Nicolas Graves +;;; Copyright © 2024 Giacomo Leidi ;;; ;;; This file is part of GNU Guix. ;;; @@ -39,6 +40,7 @@ (define-module (gnu build activation) #:use-module (srfi srfi-11) #:use-module (srfi srfi-26) #:export (activate-users+groups + activate-subuids+subgids activate-user-home activate-etc activate-setuid-programs @@ -202,6 +204,23 @@ (define (activate-users+groups users groups) (chmod directory #o555)) (duplicates (map user-account-home-directory system-accounts)))) +(define (activate-subuids+subgids subuids subgids) + "Make sure SUBUIDS (a list of subid range records) and SUBGIDS (a list of +subid range records) are all available." + + ;; Take same lock as Shadow while we read + ;; and write the databases. This ensures there's no race condition with + ;; other tools that might be accessing it at the same time. + (with-file-lock "/etc/subgid.lock" + (let-values (((subuid subgid) + (subuid+subgid-databases subuids subgids))) + (write-subgid subgid))) + + (with-file-lock "/etc/subuid.lock" + (let-values (((subuid subgid) + (subuid+subgid-databases subuids subgids))) + (write-subuid subuid)))) + (define (activate-user-home users) "Create and populate the home directory of USERS, a list of tuples, unless they already exist." diff --git a/gnu/local.mk b/gnu/local.mk index ef1e82eb04..3019747328 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -835,6 +835,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/samba.scm \ %D%/tests/security.scm \ %D%/tests/security-token.scm \ + %D%/tests/shadow.scm \ %D%/tests/singularity.scm \ %D%/tests/ssh.scm \ %D%/tests/telephony.scm \ diff --git a/gnu/system/accounts.scm b/gnu/system/accounts.scm index 1b88ca301f..f63d7f96bd 100644 --- a/gnu/system/accounts.scm +++ b/gnu/system/accounts.scm @@ -51,6 +51,7 @@ (define-module (gnu system accounts) sexp->user-account sexp->user-group + sexp->subid-range default-shell)) @@ -159,3 +160,12 @@ (define (sexp->user-account sexp) (create-home-directory? create-home-directory?) (shell shell) (password password) (system? system?))))) + +(define (sexp->subid-range sexp) + "Take SEXP, a tuple as returned by 'subid-range->gexp', and turn it into a +subid-range record." + (match sexp + ((name start count) + (subid-range (name name) + (start start) + (count count))))) diff --git a/gnu/system/shadow.scm b/gnu/system/shadow.scm index d9f13271d8..84b5de660b 100644 --- a/gnu/system/shadow.scm +++ b/gnu/system/shadow.scm @@ -4,6 +4,7 @@ ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen ;;; Copyright © 2020, 2023 Efraim Flashner ;;; Copyright © 2020 Maxim Cournoyer +;;; Copyright © 2024 Giacomo Leidi ;;; ;;; This file is part of GNU Guix. ;;; @@ -77,7 +78,20 @@ (define-module (gnu system shadow) %base-user-accounts account-service-type - account-service)) + account-service + + subids-configuration + subids-configuration? + subids-configuration-add-root? + subids-configuration-subgids + subids-configuration-subuids + + subids-extension + subids-extension? + subids-extension-subgids + subids-extension-subuids + + subids-service-type)) ;;; Commentary: ;;; @@ -380,7 +394,7 @@ (define (assert-valid-users/groups users groups) ;;; -;;; Service. +;;; Accounts Service. ;;; (define (user-group->gexp group) @@ -521,4 +535,193 @@ (define (account-service accounts+groups skeletons) (service account-service-type (append skeletons accounts+groups))) + +;;; +;;; Subids Service. +;;; + +(define %sub-id-min + (@@ (gnu build accounts) %sub-id-min)) +(define %sub-id-max + (@@ (gnu build accounts) %sub-id-max)) +(define %sub-id-count + (@@ (gnu build accounts) %sub-id-count)) + +(define* (%root-subid #:optional (start %sub-id-min) (count %sub-id-count)) + (subid-range + (name "root") + (start start) + (count count))) + +(define-record-type* + subids-configuration make-subids-configuration + subids-configuration? + this-subids-configuration + + (add-root? subids-configuration-add-root? ; boolean + (default #t)) + (subgids subids-configuration-subgids ; list of + (default '())) + (subuids subids-configuration-subuids ; list of + (default '()))) + +(define (subid-range->gexp range) + "Turn RANGE, a object, into a list-valued gexp suitable for +'activate-subuids+subgids'." + (define count (subid-range-count range)) + #~`(#$(subid-range-name range) + #$(subid-range-start range) + #$(if (and (number? count) + (> count 0)) + count + %sub-id-count))) + +(define (assert-valid-subids ranges) + (cond ((>= (fold + 0 (map subid-range-count ranges)) + (- %sub-id-max %sub-id-min -1)) + (raise + (string-append + "The configured ranges are more than the " + (- %sub-id-max %sub-id-min -1) " max allowed."))) + ((any (lambda (r) + (define start (subid-range-start r)) + (and start + (< start %sub-id-min))) + ranges) + (raise + (string-append + "One subid-range starts before the minimum allowed sub id " + %sub-id-min "."))) + ((any (lambda (r) + (define end (subid-range-end r)) + (and end + (> end %sub-id-max))) + ranges) + (raise + (string-append + "One subid-range ends after the maximum allowed sub id " + %sub-id-max "."))) + ((any (compose null? subid-range-name) + ranges) + (raise + "One subid-range has a null name.")) + ((any (compose string-null? subid-range-name) + ranges) + (raise + "One subid-range has a name equal to the empty string.")) + (else #t))) + +(define (delete-duplicate-ranges ranges) + (delete-duplicates ranges + (lambda args + (apply string=? (map subid-range-name ranges))))) + +(define (subids-activation config) + "Return a gexp that activates SUBUIDS+SUBGIDS, a list of +objects." + (define (add-root-when-missing ranges) + (define sorted-ranges + (sort-list ranges subid-range-less)) + (define root-missing? + (not + (find (lambda (r) + (string=? "root" + (subid-range-name r))) + sorted-ranges))) + (define first-start + (and (> (length sorted-ranges) 0) + (subid-range-start (first sorted-ranges)))) + (define first-has-start? + (number? first-start)) + (define root-start + (if first-has-start? + (and + (> first-start %sub-id-min) + %sub-id-min) + %sub-id-min)) + (define root-count + (if first-has-start? + (- first-start %sub-id-min) + %sub-id-count)) + (if (and root-missing? + (subids-configuration-add-root? config)) + (append (list (%root-subid root-start root-count)) + sorted-ranges) + sorted-ranges)) + + (define subuids + (delete-duplicate-ranges (subids-configuration-subuids config))) + + (define subuids-specs + (map subid-range->gexp (add-root-when-missing subuids))) + + (define subgids + (delete-duplicate-ranges (subids-configuration-subgids config))) + + (define subgids-specs + (map subid-range->gexp (add-root-when-missing subgids))) + + (assert-valid-subids subgids) + (assert-valid-subids subuids) + + ;; Add subuids and subgids. + (with-imported-modules (source-module-closure '((gnu system accounts))) + #~(begin + (use-modules (gnu system accounts)) + + (activate-subuids+subgids (map sexp->subid-range (list #$@subuids-specs)) + (map sexp->subid-range (list #$@subgids-specs)))))) + +(define-record-type* + subids-extension make-subids-extension + subids-extension? + this-subids-extension + + (subgids subids-extension-subgids ; list of + (default '())) + (subuids subids-extension-subuids ; list of + (default '()))) + +(define append-subid-ranges + (lambda args + (delete-duplicate-ranges + (apply append args)))) + +(define (subids-extension-merge a b) + (subids-extension + (subgids (append-subid-ranges + (subids-extension-subgids a) + (subids-extension-subgids b))) + (subuids (append-subid-ranges + (subids-extension-subuids a) + (subids-extension-subuids b))))) + +(define subids-service-type + (service-type (name 'subids) + ;; Concatenate lists. + (compose (lambda (args) + (fold subids-extension-merge + (subids-extension) + args))) + (extend + (lambda (config extension) + (subids-configuration + (inherit config) + (subgids + (append-subid-ranges + (subids-configuration-subgids config) + (subids-extension-subgids extension))) + (subuids + (append-subid-ranges + (subids-configuration-subuids config) + (subids-extension-subuids extension)))))) + (extensions + (list (service-extension activation-service-type + subids-activation))) + (default-value + (subids-configuration)) + (description + "Ensure the specified sub UIDs and sub GIDs exist in +/etc/subuid and /etc/subgid."))) + ;;; shadow.scm ends here diff --git a/gnu/tests/shadow.scm b/gnu/tests/shadow.scm new file mode 100644 index 0000000000..1e755b5438 --- /dev/null +++ b/gnu/tests/shadow.scm @@ -0,0 +1,128 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2024 Giacomo Leidi +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu tests shadow) + #:use-module (gnu packages base) + #:use-module (gnu tests) + #:use-module (gnu services) + #:use-module (gnu system) + #:use-module (gnu system accounts) + #:use-module (gnu system shadow) + #:use-module (gnu system vm) + #:use-module (guix gexp) + #:export (%test-subids)) + + +(define %subids-os + (simple-operating-system + (simple-service + 'simple-subids + subids-service-type + (subids-extension + (subgids + (list + (subid-range + (name "alice")) + (subid-range + (name "bob") + (start 100700)))) + (subuids + (list + (subid-range + (name "alice")))))))) + +(define (run-subids-test) + "Run IMAGE as an OCI backed Shepherd service, inside OS." + + (define os + (marionette-operating-system + (operating-system-with-gc-roots + %subids-os + (list)) + #:imported-modules '((gnu services herd) + (guix combinators)))) + + (define vm + (virtual-machine + (operating-system os) + (volatile? #f) + (memory-size 1024) + (disk-image-size (* 3000 (expt 2 20))) + (port-forwardings '()))) + + (define test + (with-imported-modules '((gnu build marionette)) + #~(begin + (use-modules (srfi srfi-11) (srfi srfi-64) + (gnu build marionette)) + + (define marionette + ;; Relax timeout to accommodate older systems and + ;; allow for pulling the image. + (make-marionette (list #$vm) #:timeout 60)) + + (test-runner-current (system-test-runner #$output)) + (test-begin "subids") + + (test-equal "/etc/subid and /etc/subgid are created and their content is sound" + '("root:100000:700\nbob:100700:65536\nalice:166236:65536" + "root:100000:65536\nalice:165536:65536") + (marionette-eval + `(begin + (use-modules (ice-9 popen) + (ice-9 match) + (ice-9 rdelim)) + + (define (read-lines file-or-port) + (define (loop-lines port) + (let loop ((lines '())) + (match (read-line port) + ((? eof-object?) + (reverse lines)) + (line + (loop (cons line lines)))))) + + (if (port? file-or-port) + (loop-lines file-or-port) + (call-with-input-file file-or-port + loop-lines))) + + (define slurp + (lambda args + (let* ((port (apply open-pipe* OPEN_READ args)) + (output (read-lines port)) + (status (close-pipe port))) + output))) + (let* ((response1 (slurp + ,(string-append #$coreutils "/bin/cat") + "/etc/subgid")) + (response2 (slurp + ,(string-append #$coreutils "/bin/cat") + "/etc/subuid"))) + (list (string-join response1 "\n") (string-join response2 "\n")))) + marionette)) + + (test-end)))) + + (gexp->derivation "subids-test" test)) + +(define %test-subids + (system-test + (name "subids") + (description "Test sub UIDs and sub GIDs provisioning service.") + (value (run-subids-test)))) -- 2.45.2