有没有一种方法可以对go的os.exec调用进行chroot/sandbox(防止rm -rf /)?

huangapple go评论87阅读模式
英文:

Is there a way to chroot/sandbox a go os.exec call (prevent rm -rf /)

问题

我想测试/自动化一些代码库,基本流程如下:

repos := []string{"repo 1", "repo 2", ...}
for r := range repos {
    // 克隆代码库
    // 进入代码库目录
    // 运行测试
    // 构建代码
    // ...
}

我正在使用GO来实现这个过程,使用os.exec调用一系列命令,类似于:

exec.Command("sh", "-c", "git clone project")

到目前为止一切顺利,但我想知道是否有一种方法可以防止在Makefile中出现类似rm -rf /的错误写法,从而破坏我的主机。

基本上,我想使用系统库/工具,但限制/限定输出到特定的workdir,这样我就可以避免预先构建chroot。

一个可行的解决方案是使用FreeBSD jail,但我想知道是否有一种替代/安全的方法,在不使用容器、虚拟机等的情况下,在基本的Mac OS X工作站上实现这一点,以便任何人都可以“安全地”运行和测试,而不用担心。

有什么想法吗?

英文:

I want to test/automate some repositories, the basic flow is something like:

repos := []string{"repo 1", "repo 2", ...}
for r := range repos {
	// git clone the repo 
    // cd repo dir
    // make test
    // make build
    // ...
}

I am doing this with GO using os.exec to call the all the series of commands, something like:

 exec.Command("sh", "-c", "git clone project")

So far so good, but I would like to know if there is a way to secure/protect against something miswriting on the Makefile that could be doing something like rm -rf /. and break my host.

Basically I would like to use the system libraries/tools but restrict/chroot only the output to a specific workdir, so that I can avoid pre-build a chroot for this.

A working solution is to use a FreeBSD jail, but I would like to know if there an alternative/secure way of doing this without the need of containers,virtualbox,etc; and using a basic Mac OS X workstation. so that anyone could "safely" run & test without worries.

Any ideas ?

答案1

得分: 1

你可以使用os.Setuid/os.Setgid来实现这个功能(example.go):

package main

import (
	"log"
	"flag"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	var oUid = flag.Int("uid", 0, "Run with User ID")
	var oGid = flag.Int("gid", 0, "Run with Group ID")
	flag.Parse()

	// 从参数中获取UID/GID
	var uid = *oUid
	var gid = *oGid

	// 运行whoami命令
	out, err := exec.Command("whoami").Output()
	if err != nil {
		log.Fatal(err)
		return
	}

	// 输出whoami结果
	log.Println("原始的UID/GID whoami:", string(out))
	log.Println("设置UID/GID")

	// 更改权限
	err = syscall.Setgid(gid)
	if err != nil {
		log.Println("无法设置gid")
		log.Fatal(err)
		return
	}

	err = syscall.Setuid(uid)
	if err != nil {
		log.Println("无法设置uid")
		log.Fatal(err)
		return
	}

	// 再次执行whoami命令
	out, err = exec.Command("whoami").Output()
	if err != nil {
		log.Fatal(err)
		return
	}
	log.Println("更改后的UID/GID whoami:", string(out))

	// 做一些危险的操作
	log.Println("在/bin目录下创建一个可执行文件应该会失败...")
	_, err = os.Create("/bin/should-fail")
	if err == nil {
		log.Println("警告:操作没有失败")
		return
	}

	log.Println("一切正常", err)
}

我还建议你阅读关于正确设置gid/uid的内容(https://unix.stackexchange.com/questions/166817/using-the-setuid-bit-properly,使用C语言)。哦!需要先设置gid再设置uid,因为如果不这样做,示例会失败。

你应该以root权限执行example.go,并使用-gid,-uid标志指定非特权的gid/uid命令。

sudo go run example.go -uid <非特权id> -gid <非特权id>

英文:

You should be fine using os.Setuid/os.Setgid (example.go):

package main
import (
&quot;log&quot;
&quot;flag&quot;
&quot;os&quot;
&quot;os/exec&quot;
&quot;syscall&quot;
)
func main() {
var oUid = flag.Int(&quot;uid&quot;, 0, &quot;Run with User ID&quot;)
var oGid = flag.Int(&quot;gid&quot;, 0, &quot;Run with Group ID&quot;)
flag.Parse()
// Get UID/GUID from args
var uid = *oUid
var gid = *oGid
// Run whoami
out, err := exec.Command(&quot;whoami&quot;).Output()
if err != nil {
log.Fatal(err)
return
}
// Output whoami
log.Println(&quot;Original UID/GID whoami:&quot;, string(out))
log.Println(&quot;Setting UID/GUID&quot;)
// Change privileges
err = syscall.Setgid(gid)
if err != nil {
log.Println(&quot;Cannot setgid&quot;)
log.Fatal(err)
return
}
err = syscall.Setuid(uid)
if err != nil {
log.Println(&quot;Cannot setuid&quot;)
log.Fatal(err)
return
}
// Execute whoami again
out, err = exec.Command(&quot;whoami&quot;).Output()
if err != nil {
log.Fatal(err)
return
}
log.Println(&quot;Changed UID/GID whoami:&quot;, string(out))
// Do some dangerous stuff
log.Println(&quot;Creating a executable file within /bin should fail...&quot;)
_, err = os.Create(&quot;/bin/should-fail&quot;)
if err == nil {
log.Println(&quot;Warning: operation did not fail&quot;)
return
}
log.Println(&quot;We are fine&quot;, err)
}

I would also recommend to read about setting gid/uid properly (https://unix.stackexchange.com/questions/166817/using-the-setuid-bit-properly, in C). Oh! its needed to set gid before uid, because the example fails if you don't do so.

You should execute example.go with root privileges and specify unprivileged gid/uid to the command with flags -gid,-uid respectively.

sudo go run example.go -uid &lt;unprivileged id&gt; -gid &lt;unprivileged id&gt;

huangapple
  • 本文由 发表于 2016年3月13日 03:17:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/35962179.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定