Hello community, here is the log from the commit of package singularity for openSUSE:Factory checked in at 2020-09-21 17:26:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/singularity (Old) and /work/SRC/openSUSE:Factory/.singularity.new.4249 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "singularity" Mon Sep 21 17:26:50 2020 rev:20 rq:835372 version:3.6.3 Changes: -------- --- /work/SRC/openSUSE:Factory/singularity/singularity.changes 2020-09-15 16:28:28.322607236 +0200 +++ /work/SRC/openSUSE:Factory/.singularity.new.4249/singularity.changes 2020-09-21 17:31:09.768357685 +0200 @@ -1,0 +2,22 @@ +Fri Sep 18 07:29:10 UTC 2020 - Ana Guerrero Lopez <aguerr...@suse.com> + +- New version 3.6.3, addresses the following security issues: + - CVE-2020-25039, bsc#1176705 + When a Singularity action command (run, shell, exec) is run with + the fakeroot or user namespace option, Singularity will extract + a container image to a temporary sandbox directory. + Due to insecure permissions on the temporary directory it is possible + for any user with access to the system to read the contents of the image. + Additionally, if the image contains a world-writable file or directory, + it is possible for a user to inject arbitrary content into the running + container. + - CVE-2020-25040, bsc#1176707 + When a Singularity command that results in a container + build operation is executed, it is possible for a user with access + to the system to read the contents of the image during the build. + Additionally, if the image contains a world-writable file or directory, + it is possible for a user to inject arbitrary content into the running + build, which in certain circumstances may enable arbitrary code execution + during the build and/or when the built container is run. + +------------------------------------------------------------------- Old: ---- singularity-3.6.2.tar.gz New: ---- singularity-3.6.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ singularity.spec ++++++ --- /var/tmp/diff_new_pack.EhsKrW/_old 2020-09-21 17:31:15.168362782 +0200 +++ /var/tmp/diff_new_pack.EhsKrW/_new 2020-09-21 17:31:15.172362785 +0200 @@ -23,7 +23,7 @@ License: BSD-3-Clause-LBNL Group: Productivity/Clustering/Computing Name: singularity -Version: 3.6.2 +Version: 3.6.3 Release: 0 # https://spdx.org/licenses/BSD-3-Clause-LBNL.html URL: https://github.com/hpcng/singularity ++++++ singularity-3.6.2.tar.gz -> singularity-3.6.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/CHANGELOG.md new/singularity/CHANGELOG.md --- old/singularity/CHANGELOG.md 2020-08-25 20:50:09.000000000 +0200 +++ new/singularity/CHANGELOG.md 2020-09-15 16:05:03.000000000 +0200 @@ -9,6 +9,43 @@ _The old changelog can be found in the `release-2.6` branch_ +# v3.6.3 - [2020-09-15] + +## Security related fixes + +Singularity 3.6.3 addresses the following security issues. + + - [CVE-2020-25039](https://github.com/hpcng/singularity/security/advisories/GHSA-w6v2-qchm-grj7): + When a Singularity action command (run, shell, exec) is run with + the fakeroot or user namespace option, Singularity will extract a + container image to a temporary sandbox directory. Due to insecure + permissions on the temporary directory it is possible for any user + with access to the system to read the contents of the + image. Additionally, if the image contains a world-writable file + or directory, it is possible for a user to inject arbitrary + content into the running container. + + - [CVE-2020-25040](https://github.com/hpcng/singularity/security/advisories/GHSA-jv9c-w74q-6762): + When a Singularity command that results in a container build + operation is executed, it is possible for a user with access to + the system to read the contents of the image during the + build. Additionally, if the image contains a world-writable file + or directory, it is possible for a user to inject arbitrary + content into the running build, which in certain circumstances may + enable arbitrary code execution during the build and/or when the + built container is run. + +## Bug Fixes + + - Add CAP_MKNOD in capability bounding set of RPC to fix issue with + cryptsetup when decrypting image from within a docker container. + - Fix decryption issue when using both IPC and PID namespaces. + - Fix unsupported builtins panic from shell interpreter and add umask + support for definition file scripts. + - Do not load keyring in prepare_linux if ECL not enabled. + - Ensure sandbox option overrides remote build destination. + + # v3.6.2 - [2020-08-25] ## New features / functionalities diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/INSTALL.md new/singularity/INSTALL.md --- old/singularity/INSTALL.md 2020-08-25 20:50:09.000000000 +0200 +++ new/singularity/INSTALL.md 2020-09-15 16:05:03.000000000 +0200 @@ -89,7 +89,7 @@ To build a stable version of Singularity, check out a [release tag](https://github.com/sylabs/singularity/tags) before compiling: ``` -$ git checkout v3.6.2 +$ git checkout v3.6.3 ``` ## Compiling Singularity @@ -132,7 +132,7 @@ and use it to install the RPM like this: ``` -$ export VERSION=3.6.2 # this is the singularity version, change as you need +$ export VERSION=3.6.3 # this is the singularity version, change as you need $ wget https://github.com/sylabs/singularity/releases/download/v${VERSION}/singularity-${VERSION}.tar.gz && \ rpmbuild -tb singularity-${VERSION}.tar.gz && \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/VERSION new/singularity/VERSION --- old/singularity/VERSION 2020-08-26 00:51:09.000000000 +0200 +++ new/singularity/VERSION 2020-09-15 16:11:54.000000000 +0200 @@ -1 +1 @@ -3.6.2 +3.6.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/cmd/internal/cli/actions_linux.go new/singularity/cmd/internal/cli/actions_linux.go --- old/singularity/cmd/internal/cli/actions_linux.go 2020-08-17 19:13:16.000000000 +0200 +++ new/singularity/cmd/internal/cli/actions_linux.go 2020-09-15 16:05:03.000000000 +0200 @@ -45,27 +45,30 @@ "golang.org/x/sys/unix" ) -func convertImage(filename string, unsquashfsPath string) (string, error) { +// convertImage extracts the image found at filename to directory dir within a temporary directory +// tempDir. If the unsquashfs binary is not located, the binary at unsquashfsPath is used. It is +// the caller's responsibility to remove tempDir when no longer needed. +func convertImage(filename string, unsquashfsPath string) (tempDir, imageDir string, err error) { img, err := imgutil.Init(filename, false) if err != nil { - return "", fmt.Errorf("could not open image %s: %s", filename, err) + return "", "", fmt.Errorf("could not open image %s: %s", filename, err) } defer img.File.Close() part, err := img.GetRootFsPartition() if err != nil { - return "", fmt.Errorf("while getting root filesystem in %s: %s", filename, err) + return "", "", fmt.Errorf("while getting root filesystem in %s: %s", filename, err) } // squashfs only if part.Type != imgutil.SQUASHFS { - return "", fmt.Errorf("not a squashfs root filesystem") + return "", "", fmt.Errorf("not a squashfs root filesystem") } // create a reader for rootfs partition reader, err := imgutil.NewPartitionReader(img, "", 0) if err != nil { - return "", fmt.Errorf("could not extract root filesystem: %s", err) + return "", "", fmt.Errorf("could not extract root filesystem: %s", err) } s := unpacker.NewSquashfs() if !s.HasUnsquashfs() && unsquashfsPath != "" { @@ -82,18 +85,28 @@ } // create temporary sandbox - dir, err := ioutil.TempDir(tmpdir, "rootfs-") + tempDir, err = ioutil.TempDir(tmpdir, "rootfs-") if err != nil { - return "", fmt.Errorf("could not create temporary sandbox: %s", err) + return "", "", fmt.Errorf("could not create temporary sandbox: %s", err) + } + defer func() { + if err != nil { + os.RemoveAll(tempDir) + } + }() + + // create an inner dir to extract to, so we don't clobber the secure permissions on the tmpDir. + imageDir = filepath.Join(tempDir, "root") + if err := os.Mkdir(imageDir, 0755); err != nil { + return "", "", fmt.Errorf("could not create root directory: %s", err) } // extract root filesystem - if err := s.ExtractAll(reader, dir); err != nil { - os.RemoveAll(dir) - return "", fmt.Errorf("root filesystem extraction failed: %s", err) + if err := s.ExtractAll(reader, imageDir); err != nil { + return "", "", fmt.Errorf("root filesystem extraction failed: %s", err) } - return dir, err + return tempDir, imageDir, err } // checkHidepid checks if hidepid is set on /proc mount point, when this @@ -650,13 +663,13 @@ } sylog.Verbosef("User namespace requested, convert image %s to sandbox", image) sylog.Infof("Convert SIF file to sandbox...") - dir, err := convertImage(image, unsquashfsPath) + tempDir, imageDir, err := convertImage(image, unsquashfsPath) if err != nil { sylog.Fatalf("while extracting %s: %s", image, err) } - engineConfig.SetImage(dir) - engineConfig.SetDeleteImage(true) - generator.AddProcessEnv("SINGULARITY_CONTAINER", dir) + engineConfig.SetImage(imageDir) + engineConfig.SetDeleteTempDir(tempDir) + generator.AddProcessEnv("SINGULARITY_CONTAINER", imageDir) // if '--disable-cache' flag, then remove original SIF after converting to sandbox if disableCache { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/cmd/internal/cli/build_linux.go new/singularity/cmd/internal/cli/build_linux.go --- old/singularity/cmd/internal/cli/build_linux.go 2020-08-21 20:55:51.000000000 +0200 +++ new/singularity/cmd/internal/cli/build_linux.go 2020-09-15 15:38:45.000000000 +0200 @@ -12,6 +12,7 @@ "os" osExec "os/exec" "runtime" + "strings" "syscall" "github.com/spf13/cobra" @@ -126,17 +127,27 @@ sylog.Fatalf("Unable to build from %s: %v", spec, err) } + // path SIF from remote builder should be placed + rbDst := dst if buildArgs.sandbox { + if strings.HasPrefix(dst, "library://") { + // image destination is the library. + sylog.Fatalf("Library URI detected as destination, sandbox builds are incompatible with library destinations.") + } + // create temporary file to download sif f, err := ioutil.TempFile(tmpDir, "remote-build-") if err != nil { sylog.Fatalf("Could not create temporary directory: %s", err) } - os.Remove(f.Name()) - dest := f.Name() + f.Close() + + // override remote build destation to temporary file for conversion to a sandbox + rbDst = f.Name() + sylog.Debugf("Overriding remote build destination to temporary file: %s", rbDst) // remove downloaded sif - defer os.Remove(f.Name()) + defer os.Remove(rbDst) // build from sif downloaded in tmp location defer func() { @@ -146,7 +157,7 @@ sylog.Fatalf("failed to create an image cache handle") } - d, err := types.NewDefinitionFromURI("localimage" + "://" + dest) + d, err := types.NewDefinitionFromURI("localimage" + "://" + rbDst) if err != nil { sylog.Fatalf("Unable to create definition for sandbox build: %v", err) } @@ -175,7 +186,7 @@ }() } - b, err := remotebuilder.New(dst, buildArgs.libraryURL, def, buildArgs.detached, forceOverwrite, buildArgs.builderURL, authToken, buildArgs.arch) + b, err := remotebuilder.New(rbDst, buildArgs.libraryURL, def, buildArgs.detached, forceOverwrite, buildArgs.builderURL, authToken, buildArgs.arch) if err != nil { sylog.Fatalf("Failed to create builder: %v", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/cmd/starter/c/starter.c new/singularity/cmd/starter/c/starter.c --- old/singularity/cmd/starter/c/starter.c 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/cmd/starter/c/starter.c 2020-09-14 22:26:41.000000000 +0200 @@ -373,6 +373,7 @@ /* required by cryptsetup */ priv->capabilities.bounding = capflag(CAP_SYS_ADMIN); priv->capabilities.bounding |= capflag(CAP_IPC_LOCK); + priv->capabilities.bounding |= capflag(CAP_MKNOD); debugf("Set RPC privileges\n"); apply_privileges(priv, current); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/e2e/run/run.go new/singularity/e2e/run/run.go --- old/singularity/e2e/run/run.go 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/e2e/run/run.go 2020-09-14 22:26:41.000000000 +0200 @@ -166,6 +166,18 @@ e2e.ExpectExit(0), ) + // Ensure decryption works with containall (IPC and PID namespaces) + cmdArgs = []string{"--containall", imgPath} + c.env.RunSingularity( + t, + e2e.AsSubtest("env var passphrase with containall"), + e2e.WithProfile(e2e.UserProfile), + e2e.WithCommand("run"), + e2e.WithArgs(cmdArgs...), + e2e.WithEnv(append(os.Environ(), passphraseEnvVar)), + e2e.ExpectExit(0), + ) + // Specifying the passphrase on the command line should always fail cmdArgs = []string{"--passphrase", e2e.Passphrase, imgPath} c.env.RunSingularity( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/build/build.go new/singularity/internal/pkg/build/build.go --- old/singularity/internal/pkg/build/build.go 2020-08-21 20:55:51.000000000 +0200 +++ new/singularity/internal/pkg/build/build.go 2020-09-15 16:05:03.000000000 +0200 @@ -13,13 +13,13 @@ "os" "os/signal" "path/filepath" + "strings" "syscall" "github.com/sylabs/singularity/internal/pkg/util/fs" "github.com/sylabs/singularity/pkg/util/fs/proc" "github.com/sylabs/singularity/pkg/util/singularityconf" - uuid "github.com/satori/go.uuid" "github.com/sylabs/singularity/internal/pkg/build/apps" "github.com/sylabs/singularity/internal/pkg/build/assemblers" "github.com/sylabs/singularity/internal/pkg/build/sources" @@ -113,14 +113,16 @@ if conf.Format == "sandbox" { rootfsParent = filepath.Dir(conf.Dest) } - rootfs := filepath.Join(rootfsParent, "rootfs-"+uuid.NewV1().String()) + parentPath, err := ioutil.TempDir(rootfsParent, "build-temp-") + if err != nil { + return nil, fmt.Errorf("failed to create build parent dir: %w", err) + } var s stage - var err error if conf.Opts.EncryptionKeyInfo != nil { - s.b, err = types.NewEncryptedBundle(rootfs, conf.Opts.TmpDir, conf.Opts.EncryptionKeyInfo) + s.b, err = types.NewEncryptedBundle(parentPath, conf.Opts.TmpDir, conf.Opts.EncryptionKeyInfo) } else { - s.b, err = types.NewBundle(rootfs, conf.Opts.TmpDir) + s.b, err = types.NewBundle(parentPath, conf.Opts.TmpDir) } if err != nil { return nil, err @@ -134,7 +136,7 @@ // the old behavior which is to create the temporary rootfs inside // $TMPDIR and copy the final root filesystem to the destination // provided - if s.b.RootfsPath != rootfs { + if !strings.HasPrefix(s.b.RootfsPath, parentPath) { sandboxCopy = true sylog.Warningf("The underlying filesystem on which resides %q won't allow to set ownership, "+ "as a consequence the sandbox could not preserve image's files/directories ownerships", conf.Dest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/build/sources/conveyorPacker_oci.go new/singularity/internal/pkg/build/sources/conveyorPacker_oci.go --- old/singularity/internal/pkg/build/sources/conveyorPacker_oci.go 2020-08-17 19:13:16.000000000 +0200 +++ new/singularity/internal/pkg/build/sources/conveyorPacker_oci.go 2020-09-15 16:05:03.000000000 +0200 @@ -96,7 +96,7 @@ cp.srcRef, err = ociarchive.ParseReference(ref) } else { // As non-root we need to do a dumb tar extraction first - tmpDir, err := ioutil.TempDir(cp.b.Opts.TmpDir, "temp-oci-") + tmpDir, err := ioutil.TempDir(b.TmpDir, "temp-oci-") if err != nil { return fmt.Errorf("could not create temporary oci directory: %v", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/runtime/engine/singularity/cleanup_linux.go new/singularity/internal/pkg/runtime/engine/singularity/cleanup_linux.go --- old/singularity/internal/pkg/runtime/engine/singularity/cleanup_linux.go 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/internal/pkg/runtime/engine/singularity/cleanup_linux.go 2020-09-15 16:05:03.000000000 +0200 @@ -50,9 +50,8 @@ } } - if e.EngineConfig.GetDeleteImage() { - image := e.EngineConfig.GetImage() - sylog.Verbosef("Removing image %s", image) + if tempDir := e.EngineConfig.GetDeleteTempDir(); tempDir != "" { + sylog.Verbosef("Removing image tempDir %s", tempDir) sylog.Infof("Cleaning up image...") var err error @@ -63,12 +62,12 @@ // context and can get permission denied error during // image removal, so we execute "rm -rf /tmp/image" via // the fakeroot engine - err = fakerootCleanup(image) + err = fakerootCleanup(tempDir) } else { - err = os.RemoveAll(image) + err = os.RemoveAll(tempDir) } if err != nil { - sylog.Errorf("failed to delete container image %s: %s", image, err) + sylog.Errorf("failed to delete container image tempDir %s: %s", tempDir, err) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/runtime/engine/singularity/prepare_linux.go new/singularity/internal/pkg/runtime/engine/singularity/prepare_linux.go --- old/singularity/internal/pkg/runtime/engine/singularity/prepare_linux.go 2020-08-17 19:13:16.000000000 +0200 +++ new/singularity/internal/pkg/runtime/engine/singularity/prepare_linux.go 2020-09-14 22:26:41.000000000 +0200 @@ -41,6 +41,7 @@ "github.com/sylabs/singularity/pkg/util/fs/proc" "github.com/sylabs/singularity/pkg/util/namespaces" "github.com/sylabs/singularity/pkg/util/singularityconf" + "golang.org/x/crypto/openpgp" "golang.org/x/sys/unix" ) @@ -1166,9 +1167,16 @@ return fmt.Errorf("while validating ECL configuration: %s", err) } - kr, err := sypgp.PublicKeyRing() - if err != nil { - return fmt.Errorf("while obtaining keyring for ECL: %s", err) + // Only try to load the keyring here if the ECL is active. + // Otherwise pass through an empty keyring rather than avoiding calling + // the ECL functions as this keeps the logic for applying / ignoring ECL in a + // single location. + var kr openpgp.KeyRing = openpgp.EntityList{} + if ecl.Activated { + kr, err = sypgp.PublicKeyRing() + if err != nil { + return fmt.Errorf("while obtaining keyring for ECL: %s", err) + } } if ok, err := ecl.ShouldRunFp(img.File, kr); err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/runtime/engine/singularity/process_linux.go new/singularity/internal/pkg/runtime/engine/singularity/process_linux.go --- old/singularity/internal/pkg/runtime/engine/singularity/process_linux.go 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/internal/pkg/runtime/engine/singularity/process_linux.go 2020-09-14 22:26:41.000000000 +0200 @@ -22,6 +22,7 @@ "reflect" "regexp" "runtime" + "strconv" "strings" "sync" "syscall" @@ -757,6 +758,24 @@ return nil } +func umaskBuiltin(ctx context.Context, argv []string) error { + hc := interp.HandlerCtx(ctx) + + if len(argv) == 0 { + old := unix.Umask(0) + unix.Umask(old) + fmt.Fprintf(hc.Stdout, "%#.4o\n", old) + } else { + umask, err := strconv.ParseUint(argv[0], 8, 16) + if err != nil { + return fmt.Errorf("umask: %s: invalid octal number: %s", argv[0], err) + } + unix.Umask(int(umask)) + } + + return nil +} + // runActionScript interprets and executes the action script within // an embedded shell interpreter. func runActionScript(engineConfig *singularityConfig.EngineConfig) ([]string, []string, error) { @@ -794,6 +813,7 @@ shell.RegisterShellBuiltin("fixpath", fixPathBuiltin) shell.RegisterShellBuiltin("hash", hashBuiltin) shell.RegisterShellBuiltin("unescape", unescapeBuiltin) + shell.RegisterShellBuiltin("umask_builtin", umaskBuiltin) // exec builtin won't execute the command but instead // it returns arguments and environment variables and diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/runtime/engine/singularity/rpc/server/server_linux.go new/singularity/internal/pkg/runtime/engine/singularity/rpc/server/server_linux.go --- old/singularity/internal/pkg/runtime/engine/singularity/rpc/server/server_linux.go 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/internal/pkg/runtime/engine/singularity/rpc/server/server_linux.go 2020-09-14 22:26:41.000000000 +0200 @@ -89,7 +89,20 @@ return capErr } + pid := 0 + if hasIPC { + // we can't rely on os.Getpid since this process may + // be in the container PID namespace + self, err := os.Readlink("/proc/self") + if err != nil { + return err + } + pid, err = strconv.Atoi(self) + if err != nil { + return err + } + // cryptsetup requires to run in the host IPC namespace // so we enter temporarily in the host IPC namespace // via the master processus ID if its greater than zero @@ -106,7 +119,7 @@ } if hasIPC { - e := namespaces.Enter(os.Getpid(), "ipc") + e := namespaces.Enter(pid, "ipc") if err == nil && e != nil { err = fmt.Errorf("while joining container IPC namespace: %s", e) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/internal/pkg/util/fs/files/action_scripts.go new/singularity/internal/pkg/util/fs/files/action_scripts.go --- old/singularity/internal/pkg/util/fs/files/action_scripts.go 2020-08-21 20:55:51.000000000 +0200 +++ new/singularity/internal/pkg/util/fs/files/action_scripts.go 2020-09-14 22:26:41.000000000 +0200 @@ -16,6 +16,16 @@ export PWD +unsupported_builtin() { + sylog warning "$1 is not supported by this shell interpreter" +} + +# create alias for unsupported builtin that trigger a panic +alias umask="umask_builtin" +alias trap="unsupported_builtin trap" +alias fg="unsupported_builtin fg" +alias bg="unsupported_builtin bg" + clear_env() { local IFS=$'\n' @@ -69,6 +79,7 @@ } clear_env +shopt -s expand_aliases if test -d "/.singularity.d/env"; then for __script__ in /.singularity.d/env/*.sh; do @@ -116,6 +127,7 @@ source "/.singularity.d/env/99-runtimevars.sh" fi +shopt -u expand_aliases restore_env # See https://github.com/sylabs/singularity/issues/2721, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/pkg/build/types/bundle.go new/singularity/pkg/build/types/bundle.go --- old/singularity/pkg/build/types/bundle.go 2020-08-17 19:13:16.000000000 +0200 +++ new/singularity/pkg/build/types/bundle.go 2020-09-15 16:05:03.000000000 +0200 @@ -30,6 +30,8 @@ RootfsPath string `json:"rootfsPath"` // where actual fs to chroot will appear TmpDir string `json:"tmpPath"` // where temp files required during build will appear + + parentPath string // parent directory for RootfsPath } // Options defines build time behavior to be executed on the bundle. @@ -73,13 +75,13 @@ } // NewEncryptedBundle creates an Encrypted Bundle environment. -func NewEncryptedBundle(rootfs, tempDir string, keyInfo *crypt.KeyInfo) (b *Bundle, err error) { - return newBundle(rootfs, tempDir, keyInfo) +func NewEncryptedBundle(parentPath, tempDir string, keyInfo *crypt.KeyInfo) (b *Bundle, err error) { + return newBundle(parentPath, tempDir, keyInfo) } // NewBundle creates a Bundle environment. -func NewBundle(rootfs, tempDir string) (b *Bundle, err error) { - return newBundle(rootfs, tempDir, nil) +func NewBundle(parentPath, tempDir string) (b *Bundle, err error) { + return newBundle(parentPath, tempDir, nil) } // RunSection iterates through the sections specified in a bundle @@ -100,7 +102,7 @@ // Remove cleans up any bundle files. func (b *Bundle) Remove() error { var errors []string - for _, dir := range []string{b.TmpDir, b.RootfsPath} { + for _, dir := range []string{b.TmpDir, b.parentPath} { if err := fs.ForceRemoveAll(dir); err != nil { errors = append(errors, fmt.Sprintf("could not remove %q: %v", dir, err)) } @@ -141,11 +143,19 @@ } } -// newBundle creates a minimum bundle with root filesystem in rootfs. +// newBundle creates a minimum bundle with root filesystem in parentPath. // Any temporary files created during build process will be in tempDir/bundle-temp-* // directory, that will be cleaned up after successful build. -func newBundle(rootfs, tempDir string, keyInfo *crypt.KeyInfo) (*Bundle, error) { - rootfsPath := rootfs + +// +// TODO: much of the logic in this func should likely be re-factored to func newBuild in the +// internal/pkg/build package, since it is the sole caller and has conditional logic which depends +// on implementation details of this package. In particular, chown() handling should be done at the +// build level, rather than the bundle level, to avoid repetition during multi-stage builds, and +// clarify responsibility for cleanup of the various directories that are created during the build +// process. +func newBundle(parentPath, tempDir string, keyInfo *crypt.KeyInfo) (*Bundle, error) { + rootfsPath := filepath.Join(parentPath, "rootfs") tmpPath, err := ioutil.TempDir(tempDir, "bundle-temp-") if err != nil { @@ -155,6 +165,7 @@ if err := os.MkdirAll(rootfsPath, 0755); err != nil { cleanupDir(tmpPath) + cleanupDir(parentPath) return nil, fmt.Errorf("could not create %q: %v", rootfsPath, err) } @@ -164,14 +175,25 @@ if err != nil { cleanupDir(tmpPath) cleanupDir(rootfsPath) + cleanupDir(parentPath) return nil, err } else if !can { - defer cleanupDir(rootfsPath) + cleanupDir(rootfsPath) + cleanupDir(parentPath) - rootfsNewPath := filepath.Join(tempDir, filepath.Base(rootfsPath)) - if rootfsNewPath != rootfsPath { - if err := os.MkdirAll(rootfsNewPath, 0755); err != nil { + // If the supplied rootfs was not inside tempDir (as is the case during a sandbox build), + // try tempDir as a fallback. + if !strings.HasPrefix(parentPath, tempDir) { + parentPath, err = ioutil.TempDir(tempDir, "build-temp-") + if err != nil { + cleanupDir(tmpPath) + return nil, fmt.Errorf("failed to create rootfs directory: %v", err) + } + // Create an inner dir, so we don't clobber the secure permissions on the surrounding dir. + rootfsNewPath := filepath.Join(parentPath, "rootfs") + if err := os.Mkdir(rootfsNewPath, 0755); err != nil { cleanupDir(tmpPath) + cleanupDir(parentPath) return nil, fmt.Errorf("could not create rootfs dir in %q: %v", rootfsNewPath, err) } // check that chown works with the underlying filesystem pointed @@ -180,10 +202,12 @@ if err != nil { cleanupDir(tmpPath) cleanupDir(rootfsNewPath) + cleanupDir(parentPath) return nil, err } else if !can { cleanupDir(tmpPath) cleanupDir(rootfsNewPath) + cleanupDir(parentPath) sylog.Errorf("Could not set files/directories ownership, if %s is on a network filesystem, "+ "you must set TMPDIR to a local path (eg: TMPDIR=/var/tmp singularity build ...)", rootfsNewPath) return nil, fmt.Errorf("ownership change not allowed in %s, aborting", tempDir) @@ -195,6 +219,7 @@ sylog.Debugf("Created directory %q for the bundle", rootfsPath) return &Bundle{ + parentPath: parentPath, RootfsPath: rootfsPath, TmpDir: tmpPath, JSONObjects: make(map[string][]byte), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/pkg/runtime/engine/singularity/config/config.go new/singularity/pkg/runtime/engine/singularity/config/config.go --- old/singularity/pkg/runtime/engine/singularity/config/config.go 2020-08-24 18:37:07.000000000 +0200 +++ new/singularity/pkg/runtime/engine/singularity/config/config.go 2020-09-15 16:05:03.000000000 +0200 @@ -148,7 +148,7 @@ NoPrivs bool `json:"noPrivs,omitempty"` NoHome bool `json:"noHome,omitempty"` NoInit bool `json:"noInit,omitempty"` - DeleteImage bool `json:"deleteImage,omitempty"` + DeleteTempDir string `json:"deleteTempDir,omitempty"` Fakeroot bool `json:"fakeroot,omitempty"` SignalPropagation bool `json:"signalPropagation,omitempty"` } @@ -711,14 +711,16 @@ return e.JSON.Fakeroot } -// GetDeleteImage returns if container image must be deleted after use. -func (e *EngineConfig) GetDeleteImage() bool { - return e.JSON.DeleteImage +// GetDeleteTempDir returns the path of the temporary directory containing the root filesystem +// which must be deleted after use. If no deletion is required, the empty string is returned. +func (e *EngineConfig) GetDeleteTempDir() string { + return e.JSON.DeleteTempDir } -// SetDeleteImage sets if container image must be deleted after use. -func (e *EngineConfig) SetDeleteImage(delete bool) { - e.JSON.DeleteImage = delete +// SetDeleteTempDir sets dir as the path of the temporary directory containing the root filesystem, +// which must be deleted after use. +func (e *EngineConfig) SetDeleteTempDir(dir string) { + e.JSON.DeleteTempDir = dir } // SetSignalPropagation sets if engine must propagate signals from diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/singularity/singularity.spec new/singularity/singularity.spec --- old/singularity/singularity.spec 2020-08-26 00:50:55.000000000 +0200 +++ new/singularity/singularity.spec 2020-09-15 16:11:43.000000000 +0200 @@ -29,12 +29,12 @@ Summary: Application and environment virtualization Name: singularity -Version: 3.6.2 +Version: 3.6.3 Release: 1%{?dist} # https://spdx.org/licenses/BSD-3-Clause-LBNL.html License: BSD-3-Clause-LBNL URL: https://www.sylabs.io/singularity/ -Source: %{name}-3.6.2.tar.gz +Source: %{name}-3.6.3.tar.gz ExclusiveOS: linux # RPM_BUILD_ROOT wasn't being set ... for some reason %if "%{sles_version}" == "11"