diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2025-10-13 10:39:21 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2025-10-13 14:48:26 +0200 |
| commit | e0e64be8de3d220a12612b3a2e4aee428277d865 (patch) | |
| tree | d55858b5e6ebd050907c1f51188d7575b9e0048e /gnu | |
| parent | 73cbebfaede2d31e22a6e75d395c37768d6382d1 (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.scm | 111 | ||||
| -rw-r--r-- | gnu/system/linux-container.scm | 4 |
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 |
| 198 | host user identifiers to map into the user namespace. GUEST-UID and GUEST-GID | 195 | host user identifiers to map into the user namespace. GUEST-UID and GUEST-GID |
| 199 | specify the first UID (respectively GID) that host UIDs (respectively GIDs) | 196 | specify 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 |
| 245 | the root directory for the container. MOUNTS is a list of <file-system> | 244 | the root directory for the container. MOUNTS is a list of <file-system> |
| 246 | objects that specify file systems to mount inside the container. NAMESPACES | 245 | objects that specify file systems to mount inside the container. NAMESPACES |
| 247 | is a list of symbols that correspond to the possible Linux namespaces: mnt, | 246 | is a list of symbols that correspond to the possible Linux namespaces: mnt, |
| 248 | ipc, uts, user, and net. When LOCK-MOUNTS? is true, arrange so that none of | 247 | ipc, uts, user, and net. |
| 249 | MOUNTS can be unmounted or remounted individually from within THUNK. | ||
| 250 | 248 | ||
| 251 | When LOOPBACK-NETWORK? is true and 'net is amount NAMESPACES, set up the | 249 | When LOOPBACK-NETWORK? is true and 'net is amount NAMESPACES, set up the |
| 252 | loopback device (\"lo\") and a minimal /etc/hosts. | 250 | loopback 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 | ||
