From da76363da8c62d28c7e9953cedf9a6070792731a Mon Sep 17 00:00:00 2001 From: David Trudgian Date: Wed, 20 Nov 2024 13:55:38 +0000 Subject: [PATCH] feat: accommodate missing /run/user dir in OCI mode On systems where `pam-systemd` is disabled, or there is other configuration preventing the creation of a /run/user/ directory, we need to use a different location for the OCI runtime state. Change the runtimeStateDir handling to: * Use XDG_RUNTIME_DIR if set. * Otherwise, try to use /run/user/. * Otherwise, use a location under $TMPDIR. Fixes #3393 --- CHANGELOG.md | 10 ++++ .../pkg/runtime/launcher/oci/oci_linux.go | 48 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76874f4d41..170989f3b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ - Fix regression from 4.1.5 that overwrites source image runscript, environment etc. in build from local image. +### New Features & Functionality + +- In OCI-Mode, accommodate systems configured so that they do not create a + `/run/user` session directory. OCI-Mode will now attempt to use + `$TMPDIR/singularity-oci-` for runtime state on systems where + `$XDG_RUNTIME_DIR` is not set and the default user session path of + `/run/user/` does not exist. Note that the `$TMPDIR/singularity-oci-` + directory is shared between concurrent `--oci` mode invocations, and will not + be removed on exit - an empty directory will remain. + ## 4.2.1 \[2024-09-13\] ### Bug Fixes diff --git a/internal/pkg/runtime/launcher/oci/oci_linux.go b/internal/pkg/runtime/launcher/oci/oci_linux.go index 0d280af7cc..f9843ac4fd 100644 --- a/internal/pkg/runtime/launcher/oci/oci_linux.go +++ b/internal/pkg/runtime/launcher/oci/oci_linux.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved. +// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved. // This software is licensed under a 3-clause BSD license. Please consult the // LICENSE.md file distributed with the sources of this project regarding your // rights to use or distribute this software. @@ -13,9 +13,11 @@ import ( "os" "path" "path/filepath" + "syscall" "time" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/sylabs/singularity/v4/internal/pkg/cgroups" "github.com/sylabs/singularity/v4/internal/pkg/util/bin" "github.com/sylabs/singularity/v4/internal/pkg/util/fs" "github.com/sylabs/singularity/v4/internal/pkg/util/rootless" @@ -58,10 +60,52 @@ func runtimeStateDir() (path string, err error) { if err != nil { return "", err } + + // Root - use our own /run directory if u.Uid == "0" { return "/run/singularity-oci", nil } - return fmt.Sprintf("/run/user/%s/singularity-oci", u.Uid), nil + + // Prefer XDG_RUNTIME_DIR for non-root, if set and usable. + if ok, _ := cgroups.HasXDGRuntimeDir(); ok { + d := filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "singularity-oci") + sylog.Debugf("Using XDG_RUNTIME_DIR for runtime state (%s)", d) + return d, nil + } + + // If no XDG_RUNTIME_DIR, then try standard user session directory location. + runDir := fmt.Sprintf("/run/user/%s/", u.Uid) + if fs.IsDir(runDir) { + d := filepath.Join(runDir, "singularity-oci") + sylog.Debugf("Using /run/user default for runtime state (%s)", d) + return d, nil + } + + // If standard user session directory not available, use TMPDIR as a last resort. + runDir = filepath.Join(os.TempDir(), "singularity-oci-"+u.Uid) + sylog.Infof("No /run/user session directory for user. Using %q for runtime state.", runDir) + + // Create if not present + st, err := os.Stat(runDir) + if os.IsNotExist(err) { + return runDir, os.Mkdir(runDir, 0o700) + } + if err != nil { + return "", err + } + + // If it exists, verify it's a directory with correct ownership, perms. + if !st.IsDir() { + return "", fmt.Errorf("%s exists, but is not a directory", runDir) + } + if st.Sys().(*syscall.Stat_t).Uid != uint32(os.Geteuid()) { //nolint:forcetypeassert + return "", fmt.Errorf("%s exists, but is not owned by correct user", runDir) + } + if st.Mode().Perm() != 0o700 { + return "", fmt.Errorf("%s exists, but does not have correct permissions (700)", runDir) + } + + return runDir, nil } // stateDir returns the path to container state handled by conmon/singularity