Yeuoly 1 年間 前
コミット
c945072b72
共有7 個のファイルを変更した156 個の追加2 個の削除を含む
  1. 6 0
      README.md
  2. 8 1
      cmd/test/sandbox/main.go
  3. 1 0
      go.mod
  4. 2 0
      go.sum
  5. 8 0
      internal/core/runner/python/python.go
  6. 126 1
      internal/core/runner/seccomp.go
  7. 5 0
      internal/static/syscalls.go

+ 6 - 0
README.md

@@ -0,0 +1,6 @@
+# Dify-Sandbox
+## Requirements
+
+```bash
+sudo apt-get install pkg-config libseccomp-dev
+```

+ 8 - 1
cmd/test/sandbox/main.go

@@ -1,5 +1,12 @@
 package main
 
-func main() {
+import (
+	"time"
+
+	"github.com/langgenius/dify-sandbox/internal/core/runner/python"
+)
 
+func main() {
+	runner := python.PythonRunner{}
+	runner.Run("aaa", time.Minute, nil)
 }

+ 1 - 0
go.mod

@@ -20,6 +20,7 @@ require (
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pelletier/go-toml/v2 v2.1.1 // indirect
+	github.com/seccomp/libseccomp-golang v0.10.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
 	golang.org/x/arch v0.7.0 // indirect

+ 2 - 0
go.sum

@@ -46,6 +46,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
 github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
 github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY=
+github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

+ 8 - 0
internal/core/runner/python/python.go

@@ -1,6 +1,7 @@
 package python
 
 import (
+	"fmt"
 	"time"
 
 	"github.com/langgenius/dify-sandbox/internal/core/runner"
@@ -12,5 +13,12 @@ type PythonRunner struct {
 }
 
 func (p *PythonRunner) Run(code string, timeout time.Duration, stdin chan []byte) (<-chan []byte, <-chan []byte, error) {
+	err := p.WithSeccomp(func() error {
+		fmt.Println("Hello World")
+		return nil
+	})
+	if err != nil {
+		fmt.Println(err)
+	}
 	return nil, nil, nil
 }

+ 126 - 1
internal/core/runner/seccomp.go

@@ -1,8 +1,133 @@
 package runner
 
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"os"
+	"syscall"
+	"unsafe"
+
+	"github.com/langgenius/dify-sandbox/internal/static"
+	sg "github.com/seccomp/libseccomp-golang"
+)
+
 type SeccompRunner struct {
 }
 
-func (s *SeccompRunner) SetSeccomp() error {
+func (s *SeccompRunner) WithSeccomp(closures func() error) error {
+	ctx, err := sg.NewFilter(sg.ActKillProcess)
+	if err != nil {
+		return err
+	}
+	defer ctx.Release()
+
+	for call := range static.ALLOW_SYSCALLS {
+		err = ctx.AddRule(sg.ScmpSyscall(call), sg.ActAllow)
+		if err != nil {
+			return err
+		}
+	}
+
+	reader, writer, err := os.Pipe()
+	if err != nil {
+		return err
+	}
+	defer reader.Close()
+	defer writer.Close()
+
+	file := os.NewFile(uintptr(writer.Fd()), "pipe")
+	ctx.ExportBPF(file)
+
+	// read from pipe
+	data := make([]byte, 4096)
+	n, err := reader.Read(data)
+	if err != nil {
+		return err
+	}
+
+	// load bpf
+	sock_filters := make([]syscall.SockFilter, n/8)
+	bytesBuffer := bytes.NewBuffer(data)
+	err = binary.Read(bytesBuffer, binary.LittleEndian, &sock_filters)
+	if err != nil {
+		return err
+	}
+
+	var pipe_fds [2]int
+	// create stdout pipe
+	err = syscall.Pipe2(pipe_fds[0:], syscall.O_CLOEXEC)
+	if err != nil {
+		return err
+	}
+	stdout_reader, stdout_writer := pipe_fds[0], pipe_fds[1]
+	// create stderr pipe
+	err = syscall.Pipe2(pipe_fds[0:], syscall.O_CLOEXEC)
+	if err != nil {
+		return err
+	}
+	stderr_reader, stderr_writer := pipe_fds[0], pipe_fds[1]
+
+	// fork subprocess
+	pid, _, errno := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
+	if errno != 0 {
+		return fmt.Errorf("fork failed: %d", errno)
+	}
+
+	defer func() {
+		syscall.Close(int(stdout_reader))
+		syscall.Close(int(stderr_reader))
+		syscall.Close(int(stdout_writer))
+		syscall.Close(int(stderr_writer))
+	}()
+
+	// child process
+	if pid == 0 {
+		// close read end of stdout pipe
+		syscall.Close(int(stdout_reader))
+		// close read end of stderr pipe
+		syscall.Close(int(stderr_reader))
+
+		defer syscall.Close(int(stdout_writer))
+		defer syscall.Close(int(stderr_writer))
+
+		bpf := syscall.SockFprog{
+			Len:    uint16(len(sock_filters)),
+			Filter: &sock_filters[0],
+		}
+		_, _, err2 := syscall.RawSyscall6(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, 2, uintptr(unsafe.Pointer(&bpf)), 0, 0, 0)
+		if err2 != 0 {
+			response := fmt.Sprintf("prctl failed: %d\n", err2)
+			_, _ = syscall.Write(int(stderr_writer), []byte(response))
+			return nil
+		}
+
+		err := closures()
+		if err != nil {
+			response := fmt.Sprintf("%v\n", err)
+			_, _ = syscall.Write(int(stderr_writer), []byte(response))
+			return nil
+		}
+	} else {
+		// close write end of stdout pipe
+		syscall.Close(int(stdout_writer))
+		// close write end of stderr pipe
+		syscall.Close(int(stderr_writer))
+		// wait for child process to finish
+		_, _, err2 := syscall.RawSyscall(syscall.SYS_WAIT4, pid, 0, 0)
+		if err2 != 0 {
+			return fmt.Errorf("wait4 failed: %d", err2)
+		}
+
+		// read from stderr pipe
+		data := make([]byte, 4096)
+		n, err := syscall.Read(int(stderr_reader), data)
+		if err != nil {
+			return err
+		}
+
+		fmt.Println(string(data[:n]))
+	}
+
 	return nil
 }

+ 5 - 0
internal/static/syscalls.go

@@ -0,0 +1,5 @@
+package static
+
+var ALLOW_SYSCALLS = []int{
+	0, 1,
+}