summaryrefslogtreecommitdiff
path: root/gnu/system
diff options
context:
space:
mode:
authorGiacomo Leidi <goodoldpaul@autistici.org>2024-10-08 00:40:28 +0200
committerLudovic Courtès <ludo@gnu.org>2024-12-18 18:32:40 +0100
commita1ecd7f56c4ffadc49d5501a0df7f4c4556120c2 (patch)
tree1584a2a34c4194b93fd3344ec4063c5de6079179 /gnu/system
parent337037d22cfcc7764c1ce87127166c351a91369d (diff)
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>
Diffstat (limited to 'gnu/system')
-rw-r--r--gnu/system/accounts.scm10
-rw-r--r--gnu/system/shadow.scm211
2 files changed, 219 insertions, 2 deletions
diff --git a/gnu/system/accounts.scm b/gnu/system/accounts.scm
index 1b88ca301fc..f63d7f96bdb 100644
--- a/gnu/system/accounts.scm
+++ b/gnu/system/accounts.scm
@@ -51,6 +51,7 @@
51 51
52 sexp->user-account 52 sexp->user-account
53 sexp->user-group 53 sexp->user-group
54 sexp->subid-range
54 55
55 default-shell)) 56 default-shell))
56 57
@@ -159,3 +160,12 @@ user-account record."
159 (create-home-directory? create-home-directory?) 160 (create-home-directory? create-home-directory?)
160 (shell shell) (password password) 161 (shell shell) (password password)
161 (system? system?))))) 162 (system? system?)))))
163
164(define (sexp->subid-range sexp)
165 "Take SEXP, a tuple as returned by 'subid-range->gexp', and turn it into a
166subid-range record."
167 (match sexp
168 ((name start count)
169 (subid-range (name name)
170 (start start)
171 (count count)))))
diff --git a/gnu/system/shadow.scm b/gnu/system/shadow.scm
index d9f13271d8f..48eca2564f1 100644
--- a/gnu/system/shadow.scm
+++ b/gnu/system/shadow.scm
@@ -4,6 +4,7 @@
4;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> 4;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
5;;; Copyright © 2020, 2023 Efraim Flashner <efraim@flashner.co.il> 5;;; Copyright © 2020, 2023 Efraim Flashner <efraim@flashner.co.il>
6;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com> 6;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
7;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
7;;; 8;;;
8;;; This file is part of GNU Guix. 9;;; This file is part of GNU Guix.
9;;; 10;;;
@@ -28,6 +29,10 @@
28 #:use-module (guix modules) 29 #:use-module (guix modules)
29 #:use-module (guix sets) 30 #:use-module (guix sets)
30 #:use-module (guix ui) 31 #:use-module (guix ui)
32 #:use-module ((gnu build accounts)
33 #:select (%subordinate-id-count
34 %subordinate-id-max
35 %subordinate-id-min))
31 #:use-module (gnu system accounts) 36 #:use-module (gnu system accounts)
32 #:use-module (gnu services) 37 #:use-module (gnu services)
33 #:use-module (gnu services shepherd) 38 #:use-module (gnu services shepherd)
@@ -77,7 +82,20 @@
77 %base-user-accounts 82 %base-user-accounts
78 83
79 account-service-type 84 account-service-type
80 account-service)) 85 account-service
86
87 subids-configuration
88 subids-configuration?
89 subids-configuration-add-root?
90 subids-configuration-subgids
91 subids-configuration-subuids
92
93 subids-extension
94 subids-extension?
95 subids-extension-subgids
96 subids-extension-subuids
97
98 subids-service-type))
81 99
82;;; Commentary: 100;;; Commentary:
83;;; 101;;;
@@ -380,7 +398,7 @@ of user '~a' is undeclared")
380 398
381 399
382;;; 400;;;
383;;; Service. 401;;; Accounts Service.
384;;; 402;;;
385 403
386(define (user-group->gexp group) 404(define (user-group->gexp group)
@@ -521,4 +539,193 @@ ACCOUNTS+GROUPS as its initial list of accounts and groups."
521 (service account-service-type 539 (service account-service-type
522 (append skeletons accounts+groups))) 540 (append skeletons accounts+groups)))
523 541
542
543;;;
544;;; Subids Service.
545;;;
546
547(define* (%root-subid #:optional (start %subordinate-id-min) (count %subordinate-id-count))
548 (subid-range
549 (name "root")
550 (start start)
551 (count count)))
552
553(define-record-type* <subids-configuration>
554 subids-configuration make-subids-configuration
555 subids-configuration?
556 this-subids-configuration
557
558 (add-root? subids-configuration-add-root? ; boolean
559 (default #t))
560 (subgids subids-configuration-subgids ; list of <subid-range>
561 (default '()))
562 (subuids subids-configuration-subuids ; list of <subid-range>
563 (default '())))
564
565(define (subid-range->gexp range)
566 "Turn RANGE, a <subid-range> object, into a list-valued gexp suitable for
567'activate-subuids+subgids'."
568 (define count (subid-range-count range))
569 #~`(#$(subid-range-name range)
570 #$(subid-range-start range)
571 #$(if (and (number? count)
572 (> count 0))
573 count
574 %subordinate-id-count)))
575
576(define (assert-valid-subids ranges)
577 (cond ((>= (fold + 0 (map subid-range-count ranges))
578 (- %subordinate-id-max %subordinate-id-min -1))
579 (raise
580 (formatted-message
581 (G_
582 "The configured ranges are more than the ~a max allowed.")
583 (- %subordinate-id-max %subordinate-id-min -1))))
584 ((any (lambda (r)
585 (define start (subid-range-start r))
586 (and start
587 (< start %subordinate-id-min)))
588 ranges)
589 (raise
590 (formatted-message
591 (G_
592 "One subid-range starts before the minimum allowed sub id ~a.")
593 %subordinate-id-min)))
594 ((any (lambda (r)
595 (define end (subid-range-end r))
596 (and end
597 (> end %subordinate-id-max)))
598 ranges)
599 (raise
600 (formatted-message
601 (G_
602 "One subid-range ends after the maximum allowed sub id ~a.")
603 %subordinate-id-max)))
604 ((any (compose null? subid-range-name)
605 ranges)
606 (raise
607 (formatted-message
608 (G_
609 "One subid-range has a null name."))))
610 ((any (compose string-null? subid-range-name)
611 ranges)
612 (raise
613 (formatted-message
614 (G_
615 "One subid-range has a name equal to the empty string."))))
616 (else #t)))
617
618(define (delete-duplicate-ranges ranges)
619 (delete-duplicates ranges
620 (lambda args
621 (apply string=? (map subid-range-name ranges)))))
622
623(define (subids-activation config)
624 "Return a gexp that activates SUBUIDS+SUBGIDS, a list of <subid-range>
625objects."
626 (define (add-root-when-missing ranges)
627 (define sorted-ranges
628 (sort-list ranges subid-range-less))
629 (define root-missing?
630 (not
631 (find (lambda (r)
632 (string=? "root"
633 (subid-range-name r)))
634 sorted-ranges)))
635 (define first-start
636 (and (> (length sorted-ranges) 0)
637 (subid-range-start (first sorted-ranges))))
638 (define first-has-start?
639 (number? first-start))
640 (define root-start
641 (if first-has-start?
642 (and
643 (> first-start %subordinate-id-min)
644 %subordinate-id-min)
645 %subordinate-id-min))
646 (define root-count
647 (if first-has-start?
648 (- first-start %subordinate-id-min)
649 %subordinate-id-count))
650 (if (and root-missing?
651 (subids-configuration-add-root? config))
652 (append (list (%root-subid root-start root-count))
653 sorted-ranges)
654 sorted-ranges))
655
656 (define subuids
657 (delete-duplicate-ranges (subids-configuration-subuids config)))
658
659 (define subuids-specs
660 (map subid-range->gexp (add-root-when-missing subuids)))
661
662 (define subgids
663 (delete-duplicate-ranges (subids-configuration-subgids config)))
664
665 (define subgids-specs
666 (map subid-range->gexp (add-root-when-missing subgids)))
667
668 (assert-valid-subids subgids)
669 (assert-valid-subids subuids)
670
671 ;; Add subuids and subgids.
672 (with-imported-modules (source-module-closure '((gnu system accounts)))
673 #~(begin
674 (use-modules (gnu system accounts))
675
676 (activate-subuids+subgids (map sexp->subid-range (list #$@subuids-specs))
677 (map sexp->subid-range (list #$@subgids-specs))))))
678
679(define-record-type* <subids-extension>
680 subids-extension make-subids-extension
681 subids-extension?
682 this-subids-extension
683
684 (subgids subids-extension-subgids ; list of <subid-range>
685 (default '()))
686 (subuids subids-extension-subuids ; list of <subid-range>
687 (default '())))
688
689(define append-subid-ranges
690 (lambda args
691 (delete-duplicate-ranges
692 (apply append args))))
693
694(define (subids-extension-merge a b)
695 (subids-extension
696 (subgids (append-subid-ranges
697 (subids-extension-subgids a)
698 (subids-extension-subgids b)))
699 (subuids (append-subid-ranges
700 (subids-extension-subuids a)
701 (subids-extension-subuids b)))))
702
703(define subids-service-type
704 (service-type (name 'subids)
705 ;; Concatenate <subid-range> lists.
706 (compose (lambda (args)
707 (fold subids-extension-merge
708 (subids-extension)
709 args)))
710 (extend
711 (lambda (config extension)
712 (subids-configuration
713 (inherit config)
714 (subgids
715 (append-subid-ranges
716 (subids-configuration-subgids config)
717 (subids-extension-subgids extension)))
718 (subuids
719 (append-subid-ranges
720 (subids-configuration-subuids config)
721 (subids-extension-subuids extension))))))
722 (extensions
723 (list (service-extension activation-service-type
724 subids-activation)))
725 (default-value
726 (subids-configuration))
727 (description
728 "Ensure the specified sub UIDs and sub GIDs exist in
729/etc/subuid and /etc/subgid.")))
730
524;;; shadow.scm ends here 731;;; shadow.scm ends here