linux-container: Make the guest UID and GID a parameter.

* gnu/build/linux-container.scm (initialize-user-namespace): Add
 #:guest-uid and #:guest-gid parameters and honor them.
(run-container): Likewise.
(call-with-container): Likewise.
* tests/containers.scm ("call-with-container, user namespace, guest UID/GID"):
New test.
This commit is contained in:
Ludovic Courtès 2019-04-02 10:34:48 +02:00
parent 36d69ac993
commit af76c020bf
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
2 changed files with 43 additions and 16 deletions

View File

@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015 David Thompson <davet@gnu.org> ;;; Copyright © 2015 David Thompson <davet@gnu.org>
;;; Copyright © 2017, 2018 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -168,9 +168,12 @@ for the process."
(umount "real-root" MNT_DETACH) (umount "real-root" MNT_DETACH)
(rmdir "real-root"))) (rmdir "real-root")))
(define (initialize-user-namespace pid host-uids) (define* (initialize-user-namespace pid host-uids
#:key (guest-uid 0) (guest-gid 0))
"Configure the user namespace for PID. HOST-UIDS specifies the number of "Configure the user namespace for PID. HOST-UIDS specifies the number of
host user identifiers to map into the user namespace." host user identifiers to map into the user namespace. GUEST-UID and GUEST-GID
specify the first UID (respectively GID) that host UIDs (respectively GIDs)
map to in the namespace."
(define proc-dir (define proc-dir
(string-append "/proc/" (number->string pid))) (string-append "/proc/" (number->string pid)))
@ -191,10 +194,10 @@ host user identifiers to map into the user namespace."
;; within the container. ;; within the container.
(call-with-output-file (scope "/uid_map") (call-with-output-file (scope "/uid_map")
(lambda (port) (lambda (port)
(format port "0 ~d ~d" uid host-uids))) (format port "~d ~d ~d" guest-uid uid host-uids)))
(call-with-output-file (scope "/gid_map") (call-with-output-file (scope "/gid_map")
(lambda (port) (lambda (port)
(format port "0 ~d ~d" gid host-uids))))) (format port "~d ~d ~d" guest-gid gid host-uids)))))
(define (namespaces->bit-mask namespaces) (define (namespaces->bit-mask namespaces)
"Return the number suitable for the 'flags' argument of 'clone' that "Return the number suitable for the 'flags' argument of 'clone' that
@ -210,13 +213,17 @@ corresponds to the symbols in NAMESPACES."
('net CLONE_NEWNET)) ('net CLONE_NEWNET))
namespaces))) namespaces)))
(define (run-container root mounts namespaces host-uids thunk) (define* (run-container root mounts namespaces host-uids thunk
#:key (guest-uid 0) (guest-gid 0))
"Run THUNK in a new container process and return its PID. ROOT specifies "Run THUNK in a new container process and return its PID. ROOT specifies
the root directory for the container. MOUNTS is a list of <file-system> the root directory for the container. MOUNTS is a list of <file-system>
objects that specify file systems to mount inside the container. NAMESPACES objects that specify file systems to mount inside the container. NAMESPACES
is a list of symbols that correspond to the possible Linux namespaces: mnt, is a list of symbols that correspond to the possible Linux namespaces: mnt,
ipc, uts, user, and net. HOST-UIDS specifies the number of ipc, uts, user, and net.
host user identifiers to map into the user namespace."
HOST-UIDS specifies the number of host user identifiers to map into the user
namespace. GUEST-UID and GUEST-GID specify the first UID (respectively GID)
that host UIDs (respectively GIDs) map to in the namespace."
;; The parent process must initialize the user namespace for the child ;; The parent process must initialize the user namespace for the child
;; before it can boot. To negotiate this, a pipe is used such that the ;; before it can boot. To negotiate this, a pipe is used such that the
;; child process blocks until the parent writes to it. ;; child process blocks until the parent writes to it.
@ -254,7 +261,9 @@ host user identifiers to map into the user namespace."
(pid (pid
(close-port child) (close-port child)
(when (memq 'user namespaces) (when (memq 'user namespaces)
(initialize-user-namespace pid host-uids)) (initialize-user-namespace pid host-uids
#:guest-uid guest-uid
#:guest-gid guest-gid))
;; TODO: Initialize cgroups. ;; TODO: Initialize cgroups.
(write 'ready parent) (write 'ready parent)
(newline parent) (newline parent)
@ -271,23 +280,30 @@ host user identifiers to map into the user namespace."
#f))))))))) #f)))))))))
(define* (call-with-container mounts thunk #:key (namespaces %namespaces) (define* (call-with-container mounts thunk #:key (namespaces %namespaces)
(host-uids 1)) (host-uids 1) (guest-uid 0) (guest-gid 0))
"Run THUNK in a new container process and return its exit status. "Run THUNK in a new container process and return its exit status.
MOUNTS is a list of <file-system> objects that specify file systems to mount MOUNTS is a list of <file-system> objects that specify file systems to mount
inside the container. NAMESPACES is a list of symbols corresponding to inside the container. NAMESPACES is a list of symbols corresponding to
the identifiers for Linux namespaces: mnt, ipc, uts, pid, user, and net. By the identifiers for Linux namespaces: mnt, ipc, uts, pid, user, and net. By
default, all namespaces are used. HOST-UIDS is the number of host user default, all namespaces are used.
identifiers to map into the container's user namespace, if there is one. By
default, only a single uid/gid, that of the current user, is mapped into the HOST-UIDS is the number of host user identifiers to map into the container's
container. The host user that creates the container is the root user (uid/gid user namespace, if there is one. By default, only a single uid/gid, that of
0) within the container. Only root can map more than a single uid/gid. the current user, is mapped into the container. The host user that creates
the container is the root user (uid/gid 0) within the container. Only root
can map more than a single uid/gid.
GUEST-UID and GUEST-GID specify the first UID (respectively GID) that host
UIDs (respectively GIDs) map to in the namespace.
Note that if THUNK needs to load any additional Guile modules, the relevant Note that if THUNK needs to load any additional Guile modules, the relevant
module files must be present in one of the mappings in MOUNTS and the Guile module files must be present in one of the mappings in MOUNTS and the Guile
load path must be adjusted as needed." load path must be adjusted as needed."
(call-with-temporary-directory (call-with-temporary-directory
(lambda (root) (lambda (root)
(let ((pid (run-container root mounts namespaces host-uids thunk))) (let ((pid (run-container root mounts namespaces host-uids thunk
#:guest-uid guest-uid
#:guest-gid guest-gid)))
;; Catch SIGINT and kill the container process. ;; Catch SIGINT and kill the container process.
(sigaction SIGINT (sigaction SIGINT
(lambda (signum) (lambda (signum)

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2015 David Thompson <davet@gnu.org> ;;; Copyright © 2015 David Thompson <davet@gnu.org>
;;; Copyright © 2016, 2017, 2019 Ludovic Courtès <ludo@gnu.org>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -51,6 +52,16 @@
(assert-exit (and (zero? (getuid)) (zero? (getgid))))) (assert-exit (and (zero? (getuid)) (zero? (getgid)))))
#:namespaces '(user)))) #:namespaces '(user))))
(skip-if-unsupported)
(test-assert "call-with-container, user namespace, guest UID/GID"
(zero?
(call-with-container '()
(lambda ()
(assert-exit (and (= 42 (getuid)) (= 77 (getgid)))))
#:guest-uid 42
#:guest-gid 77
#:namespaces '(user))))
(skip-if-unsupported) (skip-if-unsupported)
(test-assert "call-with-container, uts namespace" (test-assert "call-with-container, uts namespace"
(zero? (zero?