From 938448bf40fc77092859352d2243e2d0c236375f Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Wed, 30 Mar 2022 16:10:18 +0200 Subject: shepherd: Adjust 'fork+exec-command/container' for the Shepherd 0.9. * gnu/build/shepherd.scm (exec-command*): New procedure, with code formerly... (make-forkexec-constructor/container): ... here. Use it. (fork+exec-command/container): Use 'fork+exec-command' only when CONTAINER-SUPPORT? is false or PID is the current process. --- gnu/build/shepherd.scm | 83 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 29 deletions(-) (limited to 'gnu/build/shepherd.scm') diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index 778e3fc627..0627bac5b9 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2017, 2018, 2019, 2020 Ludovic Courtès +;;; Copyright © 2017, 2018, 2019, 2020, 2022 Ludovic Courtès ;;; Copyright © 2020 Mathieu Othacehe ;;; ;;; This file is part of GNU Guix. @@ -103,8 +103,13 @@ separate mount and PID name space. Return the \"outer\" PID. " (match (container-excursion* pid (lambda () - (read-pid-file pid-file - #:max-delay max-delay))) + ;; XXX: Trick for Shepherd 0.9: prevent 'read-pid-file' from + ;; using (@ (fibers) sleep), which would try to suspend the + ;; current task, which doesn't work in this extra process. + (with-continuation-barrier + (lambda () + (read-pid-file pid-file + #:max-delay max-delay))))) (#f ;; Send SIGTERM to the whole process group. (catch-system-error (kill (- pid) SIGTERM)) @@ -114,6 +119,26 @@ separate mount and PID name space. Return the \"outer\" PID. " ;; PID is always 1, but that's not what Shepherd needs to know. pid))) +(define* (exec-command* command #:key user group log-file pid-file + directory (environment-variables (environ))) + "Like 'exec-command', but first restore signal handles modified by +shepherd (PID 1)." + ;; First restore the default handlers. + (for-each (cut sigaction <> SIG_DFL) %precious-signals) + + ;; Unblock any signals that have been blocked by the parent process. + (unblock-signals %precious-signals) + + (mkdir-p "/var/run") + (clean-up pid-file) + + (exec-command command + #:user user + #:group group + #:log-file log-file + #:directory directory + #:environment-variables environment-variables)) + (define* (make-forkexec-constructor/container command #:key (namespaces @@ -164,24 +189,14 @@ namespace, in addition to essential bind-mounts such /proc." (let ((pid (run-container container-directory mounts namespaces 1 (lambda () - ;; First restore the default handlers. - (for-each (cut sigaction <> SIG_DFL) - %precious-signals) - - ;; Unblock any signals that have been blocked - ;; by the parent process. - (unblock-signals %precious-signals) - - (mkdir-p "/var/run") - (clean-up pid-file) - - (exec-command command - #:user user - #:group group - #:log-file log-file - #:directory directory - #:environment-variables - environment-variables))))) + (exec-command* command + #:user user + #:group group + #:pid-file pid-file + #:log-file log-file + #:directory directory + #:environment-variables + environment-variables))))) (if pid-file (if (or (memq 'mnt namespaces) (memq 'pid namespaces)) (read-pid-file/container pid pid-file @@ -209,14 +224,24 @@ on Hurd systems for instance, fallback to direct forking." ((head . rest) (loop rest (cons head result)))))) - (let ((container-support? - (file-exists? "/proc/self/ns")) - (fork-proc (lambda () - (apply fork+exec-command command - (strip-pid args))))) - (if container-support? - (container-excursion* pid fork-proc) - (fork-proc)))) + (let ((container-support? (file-exists? "/proc/self/ns"))) + (if (and container-support? + (not (and pid (= pid (getpid))))) + (container-excursion* pid + (lambda () + ;; Note: In the Shepherd 0.9, 'fork+exec-command' expects to be + ;; called from the shepherd process (because it creates a pipe to + ;; capture stdout/stderr and spawns a logging fiber) so we cannot + ;; use it here. + (match (primitive-fork) + (0 (dynamic-wind + (const #t) + (lambda () + (apply exec-command* command (strip-pid args))) + (lambda () + (primitive-_exit 127)))) + (pid #t)))) + (apply fork+exec-command command (strip-pid args))))) ;; Local Variables: ;; eval: (put 'container-excursion* 'scheme-indent-function 1) -- cgit v1.2.3 From ada530acb1e848daaac027872e6bad067e82207b Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 10 Apr 2022 23:41:15 +0200 Subject: shepherd: 'exec-command*' has a valid default #:directory. Fixes a regression introduced in 938448bf40fc77092859352d2243e2d0c236375f where 'exec-command*' could get #:directory #f, in particular when called by 'fork+exec-command/container'. * gnu/build/shepherd.scm (exec-command*): Add default value for #:directory. --- gnu/build/shepherd.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gnu/build/shepherd.scm') diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index 0627bac5b9..474054f645 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm @@ -120,7 +120,7 @@ separate mount and PID name space. Return the \"outer\" PID. " pid))) (define* (exec-command* command #:key user group log-file pid-file - directory (environment-variables (environ))) + (directory "/") (environment-variables (environ))) "Like 'exec-command', but first restore signal handles modified by shepherd (PID 1)." ;; First restore the default handlers. -- cgit v1.2.3 From f42959662765d1cd47137ff5da09086fc6a62e99 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sun, 10 Apr 2022 23:48:03 +0200 Subject: shepherd: 'fork+exec-command/container' always returns a PID. Fixes a regression introduced in 938448bf40fc77092859352d2243e2d0c236375f whereby 'fork+exec-command/container' would return #t, then used as the running value of the 'guix-daemon' service in the installer. Upon installation completion, stopping the 'guix-daemon' service would fail with wrong-type-arg because that #t would be passed to the 'stop' method in lieu of a PID. * gnu/build/shepherd.scm (fork+exec-command/container): Return a PID rather than #t. --- gnu/build/shepherd.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gnu/build/shepherd.scm') diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index 474054f645..595db40f2e 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm @@ -240,7 +240,7 @@ on Hurd systems for instance, fallback to direct forking." (apply exec-command* command (strip-pid args))) (lambda () (primitive-_exit 127)))) - (pid #t)))) + (pid pid)))) ;XXX: assuming the same PID namespace (apply fork+exec-command command (strip-pid args))))) ;; Local Variables: -- cgit v1.2.3 From e1f0c88ea221d846b5a533c4dc88e99e953af63e Mon Sep 17 00:00:00 2001 From: Leo Nikkilä Date: Mon, 11 Apr 2022 01:00:08 +0300 Subject: shepherd: Add #:supplementary-groups. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support the argument introduced in Shepherd 0.9.0 when defining container-bound services. * gnu/build/shepherd.scm (exec-command*) (make-forkexec-constructor/container): Add '#:supplementary-groups'. Signed-off-by: Ludovic Courtès --- gnu/build/shepherd.scm | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'gnu/build/shepherd.scm') diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index 595db40f2e..d52e53eb78 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm @@ -1,6 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017, 2018, 2019, 2020, 2022 Ludovic Courtès ;;; Copyright © 2020 Mathieu Othacehe +;;; Copyright © 2022 Leo Nikkilä ;;; ;;; This file is part of GNU Guix. ;;; @@ -120,6 +121,7 @@ separate mount and PID name space. Return the \"outer\" PID. " pid))) (define* (exec-command* command #:key user group log-file pid-file + (supplementary-groups '()) (directory "/") (environment-variables (environ))) "Like 'exec-command', but first restore signal handles modified by shepherd (PID 1)." @@ -135,6 +137,7 @@ shepherd (PID 1)." (exec-command command #:user user #:group group + #:supplementary-groups supplementary-groups #:log-file log-file #:directory directory #:environment-variables environment-variables)) @@ -146,6 +149,7 @@ shepherd (PID 1)." (mappings '()) (user #f) (group #f) + (supplementary-groups '()) (log-file #f) pid-file (pid-file-timeout 5) @@ -192,6 +196,8 @@ namespace, in addition to essential bind-mounts such /proc." (exec-command* command #:user user #:group group + #:supplementary-groups + supplementary-groups #:pid-file pid-file #:log-file log-file #:directory directory -- cgit v1.2.3 From 3682bd4003f4c4e313498931c471eeae2efe09b8 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sat, 16 Apr 2022 16:11:23 +0200 Subject: Add (guix least-authority). * guix/least-authority.scm: New file. * Makefile.am (MODULES): Add it. * gnu/build/shepherd.scm (default-mounts): Make public. --- Makefile.am | 1 + gnu/build/shepherd.scm | 3 +- guix/least-authority.scm | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 guix/least-authority.scm (limited to 'gnu/build/shepherd.scm') diff --git a/Makefile.am b/Makefile.am index 8b92a4a9bb..2c07fe724b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,6 +130,7 @@ MODULES = \ guix/cache.scm \ guix/cve.scm \ guix/workers.scm \ + guix/least-authority.scm \ guix/ipfs.scm \ guix/build-system.scm \ guix/build-system/android-ndk.scm \ diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index d52e53eb78..f4caefce3c 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm @@ -31,7 +31,8 @@ exec-command %precious-signals) #:autoload (shepherd system) (unblock-signals) - #:export (make-forkexec-constructor/container + #:export (default-mounts + make-forkexec-constructor/container fork+exec-command/container)) ;;; Commentary: diff --git a/guix/least-authority.scm b/guix/least-authority.scm new file mode 100644 index 0000000000..d871816fca --- /dev/null +++ b/guix/least-authority.scm @@ -0,0 +1,135 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 Ludovic Courtès +;;; +;;; 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 (guix least-authority) + #:use-module (guix gexp) + #:use-module (guix modules) + #:use-module ((guix store) #:select (%store-prefix)) + #:autoload (gnu build linux-container) (%namespaces) + #:autoload (gnu system file-systems) (file-system-mapping + file-system-mapping-source + spec->file-system + file-system->spec + file-system-mapping->bind-mount) + #:export (least-authority-wrapper)) + +;;; Commentary: +;;; +;;; This module provides tools to execute programs with the least authority +;;; necessary, using Linux namespaces. +;;; +;;; Code: + +(define %precious-variables + ;; Environment variables preserved by the wrapper by default. + '("HOME" "USER" "LOGNAME" "DISPLAY" "XAUTHORITY" "TERM" "TZ" "PAGER")) + +(define* (least-authority-wrapper program + #:key (name "pola-wrapper") + (guest-uid 1000) + (guest-gid 1000) + (mappings '()) + (namespaces %namespaces) + (directory "/") + (preserved-environment-variables + %precious-variables)) + "Return a wrapper of PROGRAM that executes it with the least authority. + +PROGRAM is executed in separate namespaces according to NAMESPACES, a list of +symbols; it turns with GUEST-UID and GUEST-GID. MAPPINGS is a list of + records indicating directories mirrored inside the +execution environment of PROGRAM. DIRECTORY is the working directory of the +wrapped process. Each environment listed in PRESERVED-ENVIRONMENT-VARIABLES +is preserved; other environment variables are erased." + (define code + (with-imported-modules (source-module-closure + '((gnu system file-systems) + (gnu build shepherd) + (gnu build linux-container))) + #~(begin + (use-modules (gnu system file-systems) + (gnu build linux-container) + ((gnu build shepherd) #:select (default-mounts)) + (srfi srfi-1)) + + (define variables + (filter-map (lambda (variable) + (let ((value (getenv variable))) + (and value + (string-append variable "=" value)))) + '#$preserved-environment-variables)) + + (define (read-file file) + (call-with-input-file file read)) + + (define references + (delete-duplicates + (append-map read-file + '#$(map references-file + (cons program + (map file-system-mapping-source + mappings)))))) + + (define (store? file-system) + (string=? (file-system-mount-point file-system) + #$(%store-prefix))) + + (define mounts + (append (map (lambda (item) + (file-system-mapping->bind-mount + (file-system-mapping (source item) + (target item)))) + references) + (remove store? + (default-mounts + #:namespaces '#$namespaces)) + (map spec->file-system + '#$(map (compose file-system->spec + file-system-mapping->bind-mount) + mappings)))) + + (define (reify-exit-status status) + (cond ((status:exit-val status) => exit) + ((or (status:term-sig status) + (status:stop-sig status)) + => (lambda (signal) + (format (current-error-port) + "~a terminated with signal ~a~%" + #$program signal) + (exit (+ 128 signal)))))) + + ;; Note: 'call-with-container' creates a sub-process that this one + ;; waits for. This might seem suboptimal but unshare(2) isn't + ;; really applicable: the process would still run in the same PID + ;; namespace. + + (reify-exit-status + (call-with-container mounts + (lambda () + (chdir #$directory) + (environ variables) + (apply execl #$program #$program (cdr (command-line)))) + + ;; Don't assume PROGRAM can behave as an init process. + #:child-is-pid1? #f + + #:guest-uid #$guest-uid + #:guest-gid #$guest-gid + #:namespaces '#$namespaces))))) + + (program-file name code)) -- cgit v1.2.3