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

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

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

问题

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

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

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

  1. 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:

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

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

  1. 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):

  1. package main
  2. import (
  3. "log"
  4. "flag"
  5. "os"
  6. "os/exec"
  7. "syscall"
  8. )
  9. func main() {
  10. var oUid = flag.Int("uid", 0, "Run with User ID")
  11. var oGid = flag.Int("gid", 0, "Run with Group ID")
  12. flag.Parse()
  13. // 从参数中获取UID/GID
  14. var uid = *oUid
  15. var gid = *oGid
  16. // 运行whoami命令
  17. out, err := exec.Command("whoami").Output()
  18. if err != nil {
  19. log.Fatal(err)
  20. return
  21. }
  22. // 输出whoami结果
  23. log.Println("原始的UID/GID whoami:", string(out))
  24. log.Println("设置UID/GID")
  25. // 更改权限
  26. err = syscall.Setgid(gid)
  27. if err != nil {
  28. log.Println("无法设置gid")
  29. log.Fatal(err)
  30. return
  31. }
  32. err = syscall.Setuid(uid)
  33. if err != nil {
  34. log.Println("无法设置uid")
  35. log.Fatal(err)
  36. return
  37. }
  38. // 再次执行whoami命令
  39. out, err = exec.Command("whoami").Output()
  40. if err != nil {
  41. log.Fatal(err)
  42. return
  43. }
  44. log.Println("更改后的UID/GID whoami:", string(out))
  45. // 做一些危险的操作
  46. log.Println("在/bin目录下创建一个可执行文件应该会失败...")
  47. _, err = os.Create("/bin/should-fail")
  48. if err == nil {
  49. log.Println("警告:操作没有失败")
  50. return
  51. }
  52. log.Println("一切正常", err)
  53. }

我还建议你阅读关于正确设置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):

  1. package main
  2. import (
  3. &quot;log&quot;
  4. &quot;flag&quot;
  5. &quot;os&quot;
  6. &quot;os/exec&quot;
  7. &quot;syscall&quot;
  8. )
  9. func main() {
  10. var oUid = flag.Int(&quot;uid&quot;, 0, &quot;Run with User ID&quot;)
  11. var oGid = flag.Int(&quot;gid&quot;, 0, &quot;Run with Group ID&quot;)
  12. flag.Parse()
  13. // Get UID/GUID from args
  14. var uid = *oUid
  15. var gid = *oGid
  16. // Run whoami
  17. out, err := exec.Command(&quot;whoami&quot;).Output()
  18. if err != nil {
  19. log.Fatal(err)
  20. return
  21. }
  22. // Output whoami
  23. log.Println(&quot;Original UID/GID whoami:&quot;, string(out))
  24. log.Println(&quot;Setting UID/GUID&quot;)
  25. // Change privileges
  26. err = syscall.Setgid(gid)
  27. if err != nil {
  28. log.Println(&quot;Cannot setgid&quot;)
  29. log.Fatal(err)
  30. return
  31. }
  32. err = syscall.Setuid(uid)
  33. if err != nil {
  34. log.Println(&quot;Cannot setuid&quot;)
  35. log.Fatal(err)
  36. return
  37. }
  38. // Execute whoami again
  39. out, err = exec.Command(&quot;whoami&quot;).Output()
  40. if err != nil {
  41. log.Fatal(err)
  42. return
  43. }
  44. log.Println(&quot;Changed UID/GID whoami:&quot;, string(out))
  45. // Do some dangerous stuff
  46. log.Println(&quot;Creating a executable file within /bin should fail...&quot;)
  47. _, err = os.Create(&quot;/bin/should-fail&quot;)
  48. if err == nil {
  49. log.Println(&quot;Warning: operation did not fail&quot;)
  50. return
  51. }
  52. log.Println(&quot;We are fine&quot;, err)
  53. }

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.

  1. 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:

确定