mirror of
https://git.savannah.gnu.org/git/guix.git
synced 2025-01-18 13:36:36 +01:00
system: Add /etc/subuid and /etc/subgid support.
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 (Miscellaneous Services): Document it. * 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 Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org> Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
parent
337037d22c
commit
a1ecd7f56c
5 changed files with 428 additions and 2 deletions
189
doc/guix.texi
189
doc/guix.texi
|
@ -18848,6 +18848,13 @@ Note that the ``root'' account is not included here. It is a
|
|||
special-case and is automatically added whether or not it is specified.
|
||||
@end defvar
|
||||
|
||||
@cindex containers, subordinate IDs
|
||||
The Linux kernel also implements @dfn{subordinate user and group IDs},
|
||||
or ``subids'', which are used to map the ID of a user and group to
|
||||
several IDs inside separate name spaces---inside ``containers''.
|
||||
@xref{subordinate-user-group-ids, the subordinate user and group ID
|
||||
service}, for information on how to configure it.
|
||||
|
||||
@node Keyboard Layout
|
||||
@section Keyboard Layout
|
||||
|
||||
|
@ -41522,6 +41529,188 @@ Whether to allow grafting or not in the pack build.
|
|||
@end deftp
|
||||
|
||||
|
||||
@c %end of fragment
|
||||
|
||||
@anchor{subordinate-user-group-ids}
|
||||
@cindex subordinate user and group IDs
|
||||
@cindex subid, subordinate user and group IDs
|
||||
@subsubheading Subordinate User and Group ID Service
|
||||
|
||||
Among the virtualization facilities implemented by the Linux kernel is the
|
||||
concept of @dfn{subordinate IDs}. Subordinate IDs allow for mapping user and group
|
||||
IDs inside process namespaces to user and group IDs of the host system.
|
||||
Subordinate user ID ranges (subuids) allow users to map virtual user IDs inside
|
||||
containers to the user ID of an unprivileged user of the host system.
|
||||
Subordinate group ID ranges (subgids), instead map virtual group IDs to the
|
||||
group ID of an unprivileged user on the host system. You can access
|
||||
@code{subuid(5)} and @code{subgid(5)} Linux man pages for more details.
|
||||
|
||||
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 @file{/etc/subuid} and @file{/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 @file{/etc/subuid} and @file{/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
|
||||
|
||||
@cindex Audit
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
|
||||
;;; Copyright © 2022 Tobias Geerinckx-Rice <me@tobias.gr>
|
||||
;;; Copyright © 2024 Nicolas Graves <ngraves@ngraves.fr>
|
||||
;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
|
@ -40,6 +41,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-privileged-programs
|
||||
|
@ -229,6 +231,23 @@ (define system-accounts
|
|||
(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."
|
||||
|
|
|
@ -851,6 +851,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 \
|
||||
|
|
|
@ -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)))))
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
|
||||
;;; Copyright © 2020, 2023 Efraim Flashner <efraim@flashner.co.il>
|
||||
;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
|
||||
;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
|
@ -28,6 +29,10 @@ (define-module (gnu system shadow)
|
|||
#:use-module (guix modules)
|
||||
#:use-module (guix sets)
|
||||
#:use-module (guix ui)
|
||||
#:use-module ((gnu build accounts)
|
||||
#:select (%subordinate-id-count
|
||||
%subordinate-id-max
|
||||
%subordinate-id-min))
|
||||
#:use-module (gnu system accounts)
|
||||
#:use-module (gnu services)
|
||||
#:use-module (gnu services shepherd)
|
||||
|
@ -77,7 +82,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 +398,7 @@ (define (validate-supplementary-group user group)
|
|||
|
||||
|
||||
;;;
|
||||
;;; Service.
|
||||
;;; Accounts Service.
|
||||
;;;
|
||||
|
||||
(define (user-group->gexp group)
|
||||
|
@ -521,4 +539,193 @@ (define (account-service accounts+groups skeletons)
|
|||
(service account-service-type
|
||||
(append skeletons accounts+groups)))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Subids Service.
|
||||
;;;
|
||||
|
||||
(define* (%root-subid #:optional (start %subordinate-id-min) (count %subordinate-id-count))
|
||||
(subid-range
|
||||
(name "root")
|
||||
(start start)
|
||||
(count count)))
|
||||
|
||||
(define-record-type* <subids-configuration>
|
||||
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 <subid-range>
|
||||
(default '()))
|
||||
(subuids subids-configuration-subuids ; list of <subid-range>
|
||||
(default '())))
|
||||
|
||||
(define (subid-range->gexp range)
|
||||
"Turn RANGE, a <subid-range> 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
|
||||
%subordinate-id-count)))
|
||||
|
||||
(define (assert-valid-subids ranges)
|
||||
(cond ((>= (fold + 0 (map subid-range-count ranges))
|
||||
(- %subordinate-id-max %subordinate-id-min -1))
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_
|
||||
"The configured ranges are more than the ~a max allowed.")
|
||||
(- %subordinate-id-max %subordinate-id-min -1))))
|
||||
((any (lambda (r)
|
||||
(define start (subid-range-start r))
|
||||
(and start
|
||||
(< start %subordinate-id-min)))
|
||||
ranges)
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_
|
||||
"One subid-range starts before the minimum allowed sub id ~a.")
|
||||
%subordinate-id-min)))
|
||||
((any (lambda (r)
|
||||
(define end (subid-range-end r))
|
||||
(and end
|
||||
(> end %subordinate-id-max)))
|
||||
ranges)
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_
|
||||
"One subid-range ends after the maximum allowed sub id ~a.")
|
||||
%subordinate-id-max)))
|
||||
((any (compose null? subid-range-name)
|
||||
ranges)
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_
|
||||
"One subid-range has a null name."))))
|
||||
((any (compose string-null? subid-range-name)
|
||||
ranges)
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_
|
||||
"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 <subid-range>
|
||||
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 %subordinate-id-min)
|
||||
%subordinate-id-min)
|
||||
%subordinate-id-min))
|
||||
(define root-count
|
||||
(if first-has-start?
|
||||
(- first-start %subordinate-id-min)
|
||||
%subordinate-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>
|
||||
subids-extension make-subids-extension
|
||||
subids-extension?
|
||||
this-subids-extension
|
||||
|
||||
(subgids subids-extension-subgids ; list of <subid-range>
|
||||
(default '()))
|
||||
(subuids subids-extension-subuids ; list of <subid-range>
|
||||
(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 <subid-range> 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
|
||||
|
|
Loading…
Reference in a new issue