summaryrefslogtreecommitdiff
path: root/gnu
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2025-10-13 10:39:21 +0200
committerLudovic Courtès <ludo@gnu.org>2025-10-13 14:48:26 +0200
commite0e64be8de3d220a12612b3a2e4aee428277d865 (patch)
treed55858b5e6ebd050907c1f51188d7575b9e0048e /gnu
parent73cbebfaede2d31e22a6e75d395c37768d6382d1 (diff)
linux-container: Remove #:lock-mounts? and related code.
This reverts commits 437bb9ece55f37d4b5a62cafc98c0c3b848a53ce and a57ed987ffd1452ba5a4d70feb54893e99b8e076, which were reported in guix/guix#1169 to occasionally cause errors like: guix shell: error: unshare : 268566528: Invalid argument
Diffstat (limited to 'gnu')
-rw-r--r--gnu/build/linux-container.scm111
-rw-r--r--gnu/system/linux-container.scm4
2 files changed, 37 insertions, 78 deletions
diff --git a/gnu/build/linux-container.scm b/gnu/build/linux-container.scm
index b6f8563f7d0..0df51c390b5 100644
--- a/gnu/build/linux-container.scm
+++ b/gnu/build/linux-container.scm
@@ -190,10 +190,7 @@ for the process."
190 (remount-read-only "/")))) 190 (remount-read-only "/"))))
191 191
192(define* (initialize-user-namespace pid host-uids 192(define* (initialize-user-namespace pid host-uids
193 #:key 193 #:key (guest-uid 0) (guest-gid 0))
194 (host-uid (getuid))
195 (host-gid (getgid))
196 (guest-uid 0) (guest-gid 0))
197 "Configure the user namespace for PID. HOST-UIDS specifies the number of 194 "Configure the user namespace for PID. HOST-UIDS specifies the number of
198host user identifiers to map into the user namespace. GUEST-UID and GUEST-GID 195host user identifiers to map into the user namespace. GUEST-UID and GUEST-GID
199specify the first UID (respectively GID) that host UIDs (respectively GIDs) 196specify the first UID (respectively GID) that host UIDs (respectively GIDs)
@@ -204,21 +201,24 @@ map to in the namespace."
204 (define (scope file) 201 (define (scope file)
205 (string-append proc-dir file)) 202 (string-append proc-dir file))
206 203
207 ;; Only root can write to the gid map without first disabling the 204 (let ((uid (getuid))
208 ;; setgroups syscall. 205 (gid (getgid)))
209 (unless (and (zero? host-uid) (zero? host-gid)) 206
210 (call-with-output-file (scope "/setgroups") 207 ;; Only root can write to the gid map without first disabling the
211 (lambda (port) 208 ;; setgroups syscall.
212 (display "deny" port)))) 209 (unless (and (zero? uid) (zero? gid))
210 (call-with-output-file (scope "/setgroups")
211 (lambda (port)
212 (display "deny" port))))
213 213
214 ;; Map the user/group that created the container to the root user 214 ;; Map the user/group that created the container to the root user
215 ;; within the container. 215 ;; within the container.
216 (call-with-output-file (scope "/uid_map") 216 (call-with-output-file (scope "/uid_map")
217 (lambda (port) 217 (lambda (port)
218 (format port "~d ~d ~d" guest-uid host-uid host-uids))) 218 (format port "~d ~d ~d" guest-uid uid host-uids)))
219 (call-with-output-file (scope "/gid_map") 219 (call-with-output-file (scope "/gid_map")
220 (lambda (port) 220 (lambda (port)
221 (format port "~d ~d ~d" guest-gid host-gid host-uids)))) 221 (format port "~d ~d ~d" guest-gid gid host-uids)))))
222 222
223(define (namespaces->bit-mask namespaces) 223(define (namespaces->bit-mask namespaces)
224 "Return the number suitable for the 'flags' argument of 'clone' that 224 "Return the number suitable for the 'flags' argument of 'clone' that
@@ -239,14 +239,12 @@ corresponds to the symbols in NAMESPACES."
239 #:key (guest-uid 0) (guest-gid 0) 239 #:key (guest-uid 0) (guest-gid 0)
240 (populate-file-system (const #t)) 240 (populate-file-system (const #t))
241 (loopback-network? #t) 241 (loopback-network? #t)
242 (lock-mounts? #t)
243 writable-root?) 242 writable-root?)
244 "Run THUNK in a new container process and return its PID. ROOT specifies 243 "Run THUNK in a new container process and return its PID. ROOT specifies
245the root directory for the container. MOUNTS is a list of <file-system> 244the root directory for the container. MOUNTS is a list of <file-system>
246objects that specify file systems to mount inside the container. NAMESPACES 245objects that specify file systems to mount inside the container. NAMESPACES
247is a list of symbols that correspond to the possible Linux namespaces: mnt, 246is a list of symbols that correspond to the possible Linux namespaces: mnt,
248ipc, uts, user, and net. When LOCK-MOUNTS? is true, arrange so that none of 247ipc, uts, user, and net.
249MOUNTS can be unmounted or remounted individually from within THUNK.
250 248
251When LOOPBACK-NETWORK? is true and 'net is amount NAMESPACES, set up the 249When LOOPBACK-NETWORK? is true and 'net is amount NAMESPACES, set up the
252loopback device (\"lo\") and a minimal /etc/hosts. 250loopback device (\"lo\") and a minimal /etc/hosts.
@@ -306,28 +304,6 @@ that host UIDs (respectively GIDs) map to in the namespace."
306 ;; cannot be 'read' so they shouldn't be written as is. 304 ;; cannot be 'read' so they shouldn't be written as is.
307 (write args child) 305 (write args child)
308 (primitive-exit 3)))) 306 (primitive-exit 3))))
309
310 (when (and lock-mounts?
311 (memq 'mnt namespaces)
312 (memq 'user namespaces))
313 ;; Create a new mount namespace owned by a new user
314 ;; namespace to "lock" together previous mounts, such that
315 ;; they cannot be unmounted or remounted separately--see
316 ;; mount_namespaces(7).
317 ;;
318 ;; Note: at this point, the process is single-threaded (no
319 ;; GC mark threads, no finalization thread, etc.) which is
320 ;; why unshare(CLONE_NEWUSER) can be used.
321 (let ((uid (getuid)) (gid (getgid)))
322 (unshare (logior CLONE_NEWUSER CLONE_NEWNS))
323 (when (file-exists? "/proc/self")
324 (initialize-user-namespace (getpid)
325 host-uids
326 #:host-uid uid
327 #:host-gid gid
328 #:guest-uid guest-uid
329 #:guest-gid guest-gid))))
330
331 ;; TODO: Manage capabilities. 307 ;; TODO: Manage capabilities.
332 (write 'ready child) 308 (write 'ready child)
333 (close-port child) 309 (close-port child)
@@ -400,7 +376,6 @@ if there are no child processes left."
400 376
401(define* (call-with-container mounts thunk #:key (namespaces %namespaces) 377(define* (call-with-container mounts thunk #:key (namespaces %namespaces)
402 (host-uids 1) (guest-uid 0) (guest-gid 0) 378 (host-uids 1) (guest-uid 0) (guest-gid 0)
403 (lock-mounts? #t)
404 (relayed-signals (list SIGINT SIGTERM)) 379 (relayed-signals (list SIGINT SIGTERM))
405 (child-is-pid1? #t) 380 (child-is-pid1? #t)
406 (populate-file-system (const #t)) 381 (populate-file-system (const #t))
@@ -485,7 +460,6 @@ load path must be adjusted as needed."
485 (call-with-temporary-directory 460 (call-with-temporary-directory
486 (lambda (root) 461 (lambda (root)
487 (let ((pid (run-container root mounts namespaces host-uids thunk* 462 (let ((pid (run-container root mounts namespaces host-uids thunk*
488 #:lock-mounts? lock-mounts?
489 #:guest-uid guest-uid 463 #:guest-uid guest-uid
490 #:guest-gid guest-gid 464 #:guest-gid guest-gid
491 #:populate-file-system populate-file-system 465 #:populate-file-system populate-file-system
@@ -506,35 +480,24 @@ return the exit status, an integer as returned by 'waitpid'."
506 (0 480 (0
507 (call-with-clean-exit 481 (call-with-clean-exit
508 (lambda () 482 (lambda ()
509 ;; First, determine the user namespace that owns the pid namespace and 483 (for-each (lambda (ns)
510 ;; join that user namespace (the assumption is that it also owns all 484 (let ((source (namespace-file (getpid) ns))
511 ;; the other namespaces). It's important that the user namespace is 485 (target (namespace-file pid ns)))
512 ;; joined first, so that the user will have the privileges to join the 486 ;; Joining the namespace that the process already
513 ;; other namespaces. 487 ;; belongs to would throw an error so avoid that.
514 (let* ((pid-ns (open-fdes (namespace-file pid "pid") 488 ;; XXX: This /proc interface leads to TOCTTOU.
515 (logior O_CLOEXEC O_RDONLY))) 489 (unless (string=? (readlink source) (readlink target))
516 (user-ns (get-user-ns pid-ns))) 490 (call-with-input-file source
517 (close-fdes pid-ns) 491 (lambda (current-ns-port)
518 (unless (equal? (stat user-ns) 492 (call-with-input-file target
519 (stat (namespace-file (getpid) "user"))) 493 (lambda (new-ns-port)
520 (setns user-ns 0)) 494 (setns (fileno new-ns-port) 0))))))))
521 (close-fdes user-ns) 495 ;; It's important that the user namespace is joined first,
522 496 ;; so that the user will have the privileges to join the
523 ;; Then join all the remaining namespaces. 497 ;; other namespaces. Furthermore, it's important that the
524 (for-each (lambda (ns) 498 ;; mount namespace is joined last, otherwise the /proc mount
525 (let ((source (namespace-file (getpid) ns)) 499 ;; point would no longer be accessible.
526 (target (namespace-file pid ns))) 500 '("user" "ipc" "uts" "net" "pid" "mnt"))
527 ;; Joining the namespace that the process already
528 ;; belongs to would throw an error so avoid that.
529 ;; XXX: This /proc interface leads to TOCTTOU.
530 (unless (string=? (readlink source) (readlink target))
531 (call-with-input-file target
532 (lambda (new-ns-port)
533 (setns (fileno new-ns-port) 0))))))
534 ;; It's important that the mount namespace is joined last,
535 ;; otherwise the /proc mount point would no longer be
536 ;; accessible.
537 '("ipc" "uts" "net" "pid" "mnt")))
538 (purify-environment) 501 (purify-environment)
539 (chdir "/") 502 (chdir "/")
540 503
diff --git a/gnu/system/linux-container.scm b/gnu/system/linux-container.scm
index d16d1e78b56..9bcdf24a7e0 100644
--- a/gnu/system/linux-container.scm
+++ b/gnu/system/linux-container.scm
@@ -317,10 +317,6 @@ Run the container with the given options."))
317 #:namespaces (if #$shared-network? 317 #:namespaces (if #$shared-network?
318 (delq 'net %namespaces) 318 (delq 'net %namespaces)
319 %namespaces) 319 %namespaces)
320
321 ;; XXX: Work around <https://issues.guix.gnu.org/78356>.
322 #:lock-mounts? #f
323
324 #:writable-root? #t 320 #:writable-root? #t
325 #:process-spawned-hook explain))))) 321 #:process-spawned-hook explain)))))
326 322