github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/README.md (about)

     1  # 沙箱工具
     2  
     3  这是一个基于Go语言自带的syscall库和os库里搬运出来进程调度工具,未来它的目标是实现一个更加复杂的沙箱。
     4  
     5  本工具基于最新的Go 1.16源代码提取并编译,经测试在1.14版本及以上可以编译通过,其他低版本没有测试。
     6  
     7  本工具**仅在**Linux(amd64)和MacOS(amd64,arm64)操作系统下测试编译并通过,请注意平台兼容性。
     8  
     9  ## 官方文档
    10  - `forkexec` 对应 `syscall` [https://pkg.go.dev/syscall](https://pkg.go.dev/syscall)
    11  - `process` 对应 `os` [https://pkg.go.dev/os](https://pkg.go.dev/os)
    12  
    13  ## 改动
    14  - 原`syscall`包里的 `ProcAttr` 和 `SysProcAttr`抽出到`forkexec`包,原`os`包里的`ProcAttr`抽出到`process`包
    15  - `forkexec`包里的`SysProcAttr`增加了`ExecRLimit`定义。注意,除栈限制外,只要是值为0就表示不做限制。
    16  ```golang
    17  type ExecRLimit struct {
    18      TimeLimit     int           // 时间限制 (ms)
    19      RealTimeLimit int           // 真实时间限制 (ms, 触发SIGALRM)
    20      MemoryLimit   int           // 内存限制 (KB)
    21      FileSizeLimit int           // 文件读写限制 (B)
    22      StackLimit    int           // 栈大小限制 (KB,0表示用内存限制的值,-1表示不限制)
    23  }
    24  ```
    25  
    26  - `process`包的`ProcAttr`修改了`Files`的类型为`interface{}`
    27  ```golang
    28  type ProcAttr struct {
    29      Dir string
    30      Env []string
    31      Files []interface{}         // 支持传入*os.File或者是uintptr(即使用syscall.Pipe()获取的管道文件描述符号)
    32      Sys *forkexec.SysProcAttr   // 换成修改过的SysProcAttr,支持setrlimit操作
    33  }
    34  ```
    35  
    36  ## 使用
    37  - 简单的运行一个程序并把子程序的内容输出到stdout
    38  ```golang
    39  func run() {
    40      p, err := process.StartProcess("./test", nil, &process.ProcAttr{
    41          Env:   os.Environ(),
    42          Files: []interface{}{os.Stdin,  os.Stdout,  os.Stderr},
    43          Sys: &forkexec.SysProcAttr {
    44              Rlimit: forkexec.ExecRLimit {
    45                  TimeLimit: 1000,
    46                  RealTimeLimit: 2000,
    47                  MemoryLimit: 128000,
    48                  StackLimit: 128000,
    49                  FileSizeLimit: 1024 * 1024 * 50,
    50              },
    51          },
    52      })
    53      if err != nil {
    54          panic(err)
    55      }
    56      ps, err := p.Wait()
    57      if err != nil {
    58          panic(err)
    59      }
    60      ws := ps.Sys().(syscall.WaitStatus)
    61      fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
    62  }
    63  ```
    64  - 运行一个交互程序,此时它们的输入输出使用管道进行连接。
    65  ```golang
    66  func program(stdin, stdout uintptr) {
    67      p, err := process.StartProcess("./target", nil, &process.ProcAttr{
    68          Env:   os.Environ(),
    69          Files: []interface{}{stdin,  stdout,  os.Stderr},
    70          Sys: &forkexec.SysProcAttr {
    71              Rlimit: forkexec.ExecRLimit {
    72                  TimeLimit: 1000,
    73                  RealTimeLimit: 2000,
    74                  MemoryLimit: 128000,
    75                  StackLimit: 65500,
    76                  FileSizeLimit: 1024 * 1024 * 50,
    77              },
    78          },
    79      })
    80      if err != nil {
    81          panic(err)
    82      }
    83      ps, err := p.Wait()
    84      if err != nil {
    85          panic(err)
    86      }
    87      ws := ps.Sys().(syscall.WaitStatus)
    88      fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
    89  }
    90  
    91  func judgement(stdin, stdout uintptr) {
    92      p, err := process.StartProcess("./judgement", []string {
    93          "./judgement",
    94          "./0.in",
    95          "./0.out",
    96          "./0.err",
    97          "./0.log",
    98      }, &process.ProcAttr{
    99          Env:   os.Environ(),
   100          Files: []interface{}{stdin,  stdout,  os.Stderr},
   101          Sys: &forkexec.SysProcAttr {
   102              Rlimit: forkexec.ExecRLimit {
   103                  TimeLimit: 1000,
   104                  RealTimeLimit: 2000,
   105                  MemoryLimit: 128000,
   106                  StackLimit: 65500,
   107                  FileSizeLimit: 1024 * 1024 * 50,
   108              },
   109          },
   110      })
   111      if err != nil {
   112          panic(err)
   113      }
   114      ps, err := p.Wait()
   115      if err != nil {
   116          panic(err)
   117      }
   118      ws := ps.Sys().(syscall.WaitStatus)
   119      fmt.Printf("\n\nExitCode: %d, Signal: %d\n", ps.ExitCode(), ws.Signal())
   120  }
   121  
   122  func run() {
   123      fdjudge, err := forkexec.GetPipe()
   124      if err != nil {
   125          panic(err)
   126      }
   127      
   128      fdtarget, err := forkexec.GetPipe()
   129      if err != nil {
   130          panic(err)
   131      }
   132  
   133      go program(fdtarget[0], fdjudge[1])
   134      go judgement(fdjudge[0], fdtarget[1])
   135  
   136      // 这里演示,只是简单的睡一下,一般会用管道去监听协程,并注意进行超时处理。
   137      time.Sleep(10 * time.Second)
   138  }
   139  ```
   140  
   141  ## 未来计划
   142  
   143  - ptrace + seccomp
   144  - windows支持