在新用户名称空间中具有凭据的exec.Command会出错:“不允许进行操作”

问题描述 投票:0回答:1

我想使用Linux名称空间并执行命令来实现一个简单的沙箱。为了防止命令写入磁盘,使用Credential: &syscall.Credential{Uid: uint32(1), Gid: uint32(1)}以另一个用户的身份执行命令。

但是,我收到此错误:“ fork / exec / Main:不允许操作”。

即使我将代码更改为Credential: &syscall.Credential{Uid: uint32(0), Gid: uint32(0)},也会发生相同的错误。

container.go如下:

// +build linux
// +build go1.12

package main

import (
    "flag"
    "fmt"
    uuid "github.com/satori/go.uuid"
    "io/ioutil"
    "os"
    "os/exec"
    "os/user"
    "path/filepath"
    "strconv"
    "strings"
    "syscall"
    "time"

    "github.com/ZiheLiu/sandbox/sandbox"
    "github.com/docker/docker/pkg/reexec"
)

func init() {
    // register "justiceInit" => justiceInit() every time
    reexec.Register("justiceInit", justiceInit)

    /**
    * 0. `init()` adds key "justiceInit" in `map`;
    * 1. reexec.Init() seeks if key `os.Args[0]` exists in `registeredInitializers`;
    * 2. for the first time this binary is invoked, the key is os.Args[0], AKA "/path/to/clike_container",
         which `registeredInitializers` will return `false`;
    * 3. `main()` calls binary itself by reexec.Command("justiceInit", args...);
    * 4. for the second time this binary is invoked, the key is os.Args[0], AKA "justiceInit",
    *    which exists in `registeredInitializers`;
    * 5. the value `justiceInit()` is invoked, any hooks(like set hostname) before fork() can be placed here.
    */
    if reexec.Init() {
        os.Exit(0)
    }
}

func justiceInit() {
    command := os.Args[1]
    timeout, _ := strconv.ParseInt(os.Args[2], 10, 32)

    cmd := exec.Command(command)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    // set uid and gid as another user
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Setpgid: true,
        Credential: &syscall.Credential{Uid: uint32(1), Gid: uint32(1)},
    }
    cmd.Env = []string{"PS1=[justice] # "}

    // got the error "fork/exec /Main: operation not permitted" here
    if err := cmd.Run(); err != nil {
        _, _ = os.Stderr.WriteString(fmt.Sprintf("%s\n", err.Error()))
    }
}

// logs will be printed to os.Stderr
func main() {
    command := flag.String("command", "./Main", "the command needed to be execute in sandbox")
    username := flag.String("username", "root", "the user to execute command")
    flag.Parse()

    u, err := user.Lookup(*username)
    if err != nil {
        _, _ = os.Stderr.WriteString(fmt.Sprintf("%s\n", err.Error()))
        os.Exit(0)
    }
    uid, _ := strconv.Atoi(u.Uid)
    gid, _ := strconv.Atoi(u.Gid)

    cmd := reexec.Command("justiceInit", *basedir, *command, *timeout)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWNS |
            syscall.CLONE_NEWUTS |
            syscall.CLONE_NEWIPC |
            syscall.CLONE_NEWPID |
            syscall.CLONE_NEWNET |
            syscall.CLONE_NEWUSER,
        UidMappings: []syscall.SysProcIDMap{
            {
                ContainerID: 0,
                HostID:      os.Getuid(),
                Size:        1,
            },
            {
                ContainerID: 1,
                HostID:      uid,
                Size:        1,
            },
        },
        GidMappings: []syscall.SysProcIDMap{
            {
                ContainerID: 0,
                HostID:      os.Getgid(),
                Size:        1,
            },
            {
                ContainerID: 1,
                HostID:      gid,
                Size:        1,
            },
        },
    }

    if err := cmd.Run(); err != nil {
        _, _ = os.Stderr.WriteString(fmt.Sprintf("%s\n", err.Error()))
    }

    os.Exit(0)
}

当我运行sudo ./container -command='/Main' -username='nobody'时,发生错误“ fork / exec / Main:不允许操作”。

justiceInit的用户名称空间中的用户应为root,但不能使用Credential设置uid和gid。

我是linux和名称空间的新手。也许我误会了一些东西。我该如何解决该错误?非常感谢!

linux docker go sandbox linux-namespaces
1个回答
0
投票

根据@Charles Duffy的建议,我跟踪了cmd.Run()的源代码,发现:


type SysProcAttr struct {
    UidMappings  []SysProcIDMap // User ID mappings for user namespaces.
    GidMappings  []SysProcIDMap // Group ID mappings for user namespaces.
    // GidMappingsEnableSetgroups enabling setgroups syscall.
    // If false, then setgroups syscall will be disabled for the child process.
    // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
    // users this should be set to false for mappings work.
    GidMappingsEnableSetgroups bool
}

因此,如果GidMappingsEnableSetgroups的默认值是false,则子进程justiceInit将无权使用setgroups syscall,无论它是否具有root特权。

结果,当我在功能cmd.SysProcAttr.GidMappingsEnableSetgroups中将true设置为main时,如下所示!

cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{
    // ...
    GidMappingsEnableSetgroups: true,
}
© www.soinside.com 2019 - 2024. All rights reserved.