error about User Namespace
lvxingzhe opened this issue · comments
使用root权限运行go run main.go
,"main.go"如下所示:
//main.go
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
}
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: uint32(1), Gid: uint32(1)}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
os.Exit(-1)
}
会出现下列报错
2017/07/30 01:27:36 fork/exec /bin/sh: operation not permitted
exit status 1
谁能解答一下?
请提供下你运行的操作系统的版本和内核的版本,我们复现下
Linux kernel在3.19以上的版本中对user namespace
做了些修改,我怀疑跟这个有关,链接是:https://go-review.googlesource.com/c/10670/
我们当时开发验证的操作系统版本和内核版本是ubuntu 14.04
和kernel-3.13
,可以切换成同样的环境验证,或者你如果有在高版本内核上兼容的实现方案,欢迎提交PR。
刚才在4.4的内核上测试了下那个代码,这样修改就可以在4.4内核正确运行了,修改后的代码是:
//main.go
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 1234,
HostID: 0,
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 1234,
HostID: 0,
Size: 1,
},
},
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
os.Exit(-1)
}
请确认是否解决,我先关闭这个issue了,有问题可以随时在Open这个issue
我的环境:
# uname -a
Linux drjr-ThinkPad-T520 4.4.0-92-generic #115-Ubuntu SMP Thu Aug 10 09:04:33 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
我通过删除该代码成功运行了书中的示例。
被删除的代码
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uiint32(1), Gid: uint32(1)}
运行展示
# go run main.go
$ id
uid=65534(nobody) gid=65534(nogroup) 组=65534(nogroup)
代码如下:
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
syscall.CLONE_NEWUSER,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
os.Exit(-1)
}
@luhuisicnu 是的,4.4的内核不在支持cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uiint32(1), Gid: uint32(1)}
的方式
@riclava 尴尬,kernel在更新时有些兼容性可能不能保证,你如果发现了在4.13上或者兼容所有的解决办法欢迎贡献代码给我们
@BSWANG 尴尬尴尬😅 我的问题,不知什么时候退了sudo -s了;不过 golang 的 issue#10626 上面的代码可以借鉴(非root也能跑,当然部分Namespace没有权限):
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 1234,
HostID: syscall.Getuid(),
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 1234,
HostID: syscall.Getgid(),
Size: 1,
},
},
效果:
ricl@ricl:~/code/playground$ make
go run main.go
$ id
uid=1234 gid=1234 groups=1234,65534(nogroup)
$
尴尬 ;-(
上面两种方法都试了,不行呢?
[root@15-pxe Docker]# uname -a
Linux 15-pxe 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
我的也是上面两种都试了 不行 Linux iZj6c5dly2y6k9y0thzjoqZ 3.10.0-693.2.2.el7.x86_64 #1 SMP Tue Sep 12 22:26:13 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Linux VM_86_181_centos 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
fork/exec /usr/bin/sh: invalid argument
目前还未解决
@v1xingyue @Arbusz
centos默认的没有开启user namespace,参考链接
https://zhuanlan.zhihu.com/p/31871814
golang/go#16283
grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
执行后要reboot
centos7
[root@mydocker chapter2]# uname -a
Linux mydocker 3.10.0-957.10.1.el7.x86_64 #1 SMP Mon Mar 18 15:06:45 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
如果已经内核开启user_namespace
依旧invalid argument
的话
使用下面的命令fixed
echo 640 > /proc/sys/user/max_user_namespaces
https://unix.stackexchange.com/questions/479635/unable-to-create-user-namespace-in-rhel?rq=1
内核:4.15.0-47-generic
系统:Ubuntu 18.04.2 LTS
func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
// Cloneflags: syscall.CLONE_NEWUTS,
// Cloneflags: syscall.CLONE_NEWIPC,
// Cloneflags: syscall.CLONE_NEWPID,
// Cloneflags: syscall.CLONE_NEWNS,
Cloneflags: syscall.CLONE_NEWUSER,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
os.Exit(0)
}
除了CLONE_NEWUSER外,其他均无法用普通用户权限执行,但CLONE_NEWUSER | CLONE_XXXXX可以。
此外,关于UID和GID的设置,在我的测试系统中有如下发现:
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUSER,
/*
以下两种情况,会导致UidMappings/GidMappings中设置了非当前进程所属UID和GID的相关数值:
1. HostID非本进程所有(与Getuid()和Getgid()不等)
2. Size大于1 (则肯定包含非当前进程的UID和GID)
则需要Host机使用Root权限才能正常执行此段代码。
*/
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 10086,
HostID: syscall.Getuid(),
Size: 1,
},
{
ContainerID: 10010,
HostID: syscall.Getgid() + 1,
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 10086,
HostID: syscall.Getgid(),
Size: 1,
},
{
ContainerID: 10010,
HostID: syscall.Getgid() + 1,
Size: 1,
},
},
}
以上规则应该是处于安全的考虑
uname -a
Linux ubuntu 3.19.0-80-generic #88~14.04.1-Ubuntu SMP Fri Jan 13 14:54:07 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
把原书中cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid: uint32(1)}这行去掉跑出了结果,其他方案都不行
我的是centos7.3 ,Linux localhost.localdomain 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux,执行usernamespace的报错
@wangpu123 centos 7是不支持user namespace的
echo 640 > /proc/sys/user/max_user_namespaces
靠谱,centos 7上验证可行
系统版本: Linux VM-0-10-centos 3.10.0-1127.13.1.el7.x86_64 #1 SMP Tue Jun 23 15:46:38 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
按照此修改后可用
sh-4.2$ id
uid=1234 gid=1234 组=1234
系统:linux 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
go: 1.12
两个版本都可以用。。。
grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
执行后要reboot
阿里云 ECS 上 cent os 7.x 的 userns 打开过程很奇怪,显示执行一下指令:
grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
后报错:
grubby: failed to open /etc/mtab: No such file or directory
提交工单后,重启了一下,居然就好了......
之后的执行过程就很顺利了。
这个问题在CentoOS 7上的解决方案
go version: go1.15.2 linux/amd64
CentOS 7 kernelLinux work 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
默认没有开启UserNamespace.
下述指令以及代码皆使用root用户运行
-
开启UserNamespace
# grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)" # reboot # echo 640 > /proc/sys/user/max_user_namespaces
-
关闭UserNamespace
# grubby --remove-args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)" # reboot
-
代码
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("sh")
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: 0,
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: 0,
Size: 1,
},
},
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
os.Exit(-1)
}
这个问题在CentoOS 7上的解决方案
go version:
go1.15.2 linux/amd64
CentOS 7 kernel
Linux work 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
默认没有开启UserNamespace.下述指令以及代码皆使用root用户运行
- 开启UserNamespace
# grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)" # reboot # echo 640 > /proc/sys/user/max_user_namespaces
- 关闭UserNamespace
# grubby --remove-args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)" # reboot
- 代码
package main import ( "log" "os" "os/exec" "syscall" ) func main() { cmd := exec.Command("sh") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER, UidMappings: []syscall.SysProcIDMap{ { ContainerID: 0, HostID: 0, Size: 1, }, }, GidMappings: []syscall.SysProcIDMap{ { ContainerID: 0, HostID: 0, Size: 1, }, }, } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatal(err) } os.Exit(-1) }
Linux izwz957vusqd7tgu3cd2eyz 3.10.0-514.26.2.el7.x86_64 #1 SMP Tue Jul 4 15:04:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
CentOS Linux release 7.3.1611 (Core)
go version : go version go1.13.1 linux/amd64
如果要执行以上代码需要加上:
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: uint32(0),
Gid: uint32(0),
}
不然会报错operation not permitted
@v1xingyue @Arbusz
centos默认的没有开启user namespace,参考链接
https://zhuanlan.zhihu.com/p/31871814
golang/go#16283
你好,我当前项目中也碰到这个问题。请问 "fork/exec /bin/bash: invalid argument" 错误和开启 user_namespace 有什么文章依据吗?目前由于文献依据,团队leader不是很认可这个解决方案。
Exec Error: fork/exec /usr/bin/sh: invalid argument
Code:
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
syscall.CLONE_NEWUSER,
}
System: Linux 3.10.0-1160.71.1.el7.x86_64 #1 SMP Tue Jun 28 15:37:28 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
The command "echo 640 > /proc/sys/user/max_user_namespaces" can resolve the exec error.