1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
// Copyright (c) 2023, 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.
package rootless
import (
"os"
"os/exec"
"os/signal"
"os/user"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/sylabs/singularity/v4/internal/pkg/buildcfg"
fakerootConfig "github.com/sylabs/singularity/v4/internal/pkg/runtime/engine/fakeroot/config"
"github.com/sylabs/singularity/v4/internal/pkg/util/starter"
"github.com/sylabs/singularity/v4/pkg/runtime/engine/config"
"github.com/sylabs/singularity/v4/pkg/sylog"
)
const (
NSEnv = "_SINGULARITY_NAMESPACE"
UIDEnv = "_CONTAINERS_ROOTLESS_UID"
GIDEnv = "_CONTAINERS_ROOTLESS_GID"
)
// Getuid retrieves the uid stored in the env var _CONTAINERS_ROOTLESS_UID, or
// the current euid if the env var is not set.
func Getuid() (uid int, err error) {
u := os.Getenv(UIDEnv)
if u != "" {
return strconv.Atoi(u)
}
return os.Geteuid(), nil
}
// Getgid retrieves the uid stored in the env var _CONTAINERS_ROOTLESS_GID, or
// the current egid if the env var is not set.
func Getgid() (uid int, err error) {
g := os.Getenv(GIDEnv)
if g != "" {
return strconv.Atoi(g)
}
return os.Getegid(), nil
}
// GetUser retrieves the User struct for the uid stored in the env var
// _CONTAINERS_ROOTLESS_UID, or the current euid if the env var is not set.
func GetUser() (*user.User, error) {
u := os.Getenv(UIDEnv)
if u != "" {
return user.LookupId(u)
}
return user.Current()
}
// InNS returns true if we are in a namespace created using this package.
func InNS() bool {
_, envSet := os.LookupEnv(NSEnv)
return envSet
}
// ExecWithFakeroot will exec singularity with provided args, in a
// subuid/gid-mapped fakeroot user namespace. This uses the fakeroot engine.
func ExecWithFakeroot(args []string) error {
singularityBin := []string{
filepath.Join(buildcfg.BINDIR, "singularity"),
}
args = append(singularityBin, args...)
env := os.Environ()
env = append(env, NSEnv+"=TRUE")
// Use _CONTAINERS_ROOTLESS_xID naming for these vars as they are required
// by our use of containers/image for OCI image handling.
env = append(env, UIDEnv+"="+strconv.Itoa(os.Geteuid()))
env = append(env, GIDEnv+"="+strconv.Itoa(os.Getegid()))
sylog.Debugf("Calling fakeroot engine to execute %q", strings.Join(args, " "))
cfg := &config.Common{
EngineName: fakerootConfig.Name,
ContainerID: "fakeroot",
EngineConfig: &fakerootConfig.EngineConfig{
Envs: env,
Args: args,
NoPIDNS: true,
NoSetgroups: true,
},
}
return starter.Exec(
"Singularity oci fakeroot",
cfg,
)
}
// RunInMountNS will run singularity with provided args, in a mount
// namespace only.
func RunInMountNS(args []string) error {
singularityBin := filepath.Join(buildcfg.BINDIR, "singularity")
env := os.Environ()
env = append(env, NSEnv+"=TRUE")
cmd := exec.Command(singularityBin, args...)
cmd.Env = env
cmd.SysProcAttr = &syscall.SysProcAttr{}
// Unshare mount namespace
cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
signals := make(chan os.Signal, 2)
signal.Notify(signals)
errChan := make(chan error, 1)
err := cmd.Start()
if err != nil {
return err
}
go func() {
errChan <- cmd.Wait()
}()
for {
select {
case s := <-signals:
sylog.Debugf("Received signal %s", s.String())
switch s {
case syscall.SIGCHLD:
break
case syscall.SIGURG:
// Ignore SIGURG, which is used for non-cooperative goroutine
// preemption starting with Go 1.14. For more information, see
// https://github.com/golang/go/issues/24543.
break
default:
//nolint:forcetypeassert
signal := s.(syscall.Signal)
if err := syscall.Kill(cmd.Process.Pid, signal); err != nil {
return err
}
}
case err := <-errChan:
if e, ok := err.(*exec.ExitError); ok {
status, ok := e.Sys().(syscall.WaitStatus)
if ok && status.Signaled() {
os.Exit(128 + int(status.Signal()))
}
os.Exit(e.ExitCode())
}
if err == nil {
os.Exit(0)
}
return err
}
}
}
|