118 lines
3.7 KiB
Go
118 lines
3.7 KiB
Go
//go:build linux
|
|
|
|
package sandbox
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
type helperPolicy struct {
|
|
Config Config `json:"config"`
|
|
CWD string `json:"cwd"`
|
|
Env map[string]string `json:"env"`
|
|
Command []string `json:"command"`
|
|
}
|
|
|
|
func ExecHelper(args []string) error {
|
|
if len(args) != 1 {
|
|
return fmt.Errorf("sandbox-exec: expected encoded policy")
|
|
}
|
|
data, err := base64.RawStdEncoding.DecodeString(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var p helperPolicy
|
|
if err := json.Unmarshal(data, &p); err != nil {
|
|
return err
|
|
}
|
|
if err := restrict(&p.Config, p.CWD, p.Env); err != nil {
|
|
return err
|
|
}
|
|
if len(p.Command) == 0 {
|
|
return fmt.Errorf("sandbox-exec: empty command")
|
|
}
|
|
if err := syscall.Chdir(p.CWD); err != nil {
|
|
return err
|
|
}
|
|
// syscall.Exec requires an absolute path; resolve via PATH if needed
|
|
cmd0 := p.Command[0]
|
|
if !strings.HasPrefix(cmd0, "/") {
|
|
if resolved, err := exec.LookPath(cmd0); err == nil {
|
|
cmd0 = resolved
|
|
}
|
|
}
|
|
return syscall.Exec(cmd0, p.Command, envList(p.Env))
|
|
}
|
|
|
|
func envList(env map[string]string) []string {
|
|
out := make([]string, 0, len(env))
|
|
for k, v := range env {
|
|
out = append(out, k+"="+v)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func restrict(cfg *Config, cwd string, env map[string]string) error {
|
|
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
|
return fmt.Errorf("set no_new_privs: %w", err)
|
|
}
|
|
fsAccess := uint64(unix.LANDLOCK_ACCESS_FS_EXECUTE | unix.LANDLOCK_ACCESS_FS_WRITE_FILE | unix.LANDLOCK_ACCESS_FS_READ_FILE | unix.LANDLOCK_ACCESS_FS_READ_DIR | unix.LANDLOCK_ACCESS_FS_REMOVE_DIR | unix.LANDLOCK_ACCESS_FS_REMOVE_FILE | unix.LANDLOCK_ACCESS_FS_MAKE_CHAR | unix.LANDLOCK_ACCESS_FS_MAKE_DIR | unix.LANDLOCK_ACCESS_FS_MAKE_REG | unix.LANDLOCK_ACCESS_FS_MAKE_SOCK | unix.LANDLOCK_ACCESS_FS_MAKE_FIFO | unix.LANDLOCK_ACCESS_FS_MAKE_BLOCK | unix.LANDLOCK_ACCESS_FS_MAKE_SYM)
|
|
fd, _, err := unix.Syscall(unix.SYS_LANDLOCK_CREATE_RULESET, uintptr(unsafe.Pointer(&fsAccess)), unsafe.Sizeof(fsAccess), 0)
|
|
if err != 0 {
|
|
return fmt.Errorf("landlock unavailable: %w", err)
|
|
}
|
|
defer unix.Close(int(fd))
|
|
add := func(paths []string, access uint64) error {
|
|
for _, pattern := range paths {
|
|
p := expandPath(pattern, cwd, func(k string) string { return env[k] })
|
|
if !pathExists(p) {
|
|
continue
|
|
}
|
|
f, e := unix.Open(p, unix.O_PATH|unix.O_CLOEXEC, 0)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
rule := struct {
|
|
Allowed uint64
|
|
ParentFD int
|
|
}{access, int(f)}
|
|
_, _, errno := unix.Syscall6(unix.SYS_LANDLOCK_ADD_RULE, fd, uintptr(1), uintptr(unsafe.Pointer(&rule)), 0, 0, 0)
|
|
unix.Close(f)
|
|
if errno != 0 {
|
|
return fmt.Errorf("add landlock path %s: %w", p, errno)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
read := uint64(unix.LANDLOCK_ACCESS_FS_READ_FILE | unix.LANDLOCK_ACCESS_FS_READ_DIR)
|
|
write := uint64(unix.LANDLOCK_ACCESS_FS_WRITE_FILE | unix.LANDLOCK_ACCESS_FS_REMOVE_DIR | unix.LANDLOCK_ACCESS_FS_REMOVE_FILE | unix.LANDLOCK_ACCESS_FS_MAKE_DIR | unix.LANDLOCK_ACCESS_FS_MAKE_REG | unix.LANDLOCK_ACCESS_FS_MAKE_SOCK | unix.LANDLOCK_ACCESS_FS_MAKE_FIFO | unix.LANDLOCK_ACCESS_FS_MAKE_CHAR | unix.LANDLOCK_ACCESS_FS_MAKE_BLOCK | unix.LANDLOCK_ACCESS_FS_MAKE_SYM)
|
|
if err := add(cfg.Filesystem.RO, read); err != nil {
|
|
return err
|
|
}
|
|
if err := add(cfg.Filesystem.ROX, read|unix.LANDLOCK_ACCESS_FS_EXECUTE); err != nil {
|
|
return err
|
|
}
|
|
if err := add(cfg.Filesystem.RW, read|write); err != nil {
|
|
return err
|
|
}
|
|
if err := add(cfg.Filesystem.RWX, read|write|unix.LANDLOCK_ACCESS_FS_EXECUTE); err != nil {
|
|
return err
|
|
}
|
|
_, _, err = unix.Syscall(unix.SYS_LANDLOCK_RESTRICT_SELF, fd, 0, 0)
|
|
if err != 0 {
|
|
return fmt.Errorf("restrict landlock: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ = os.Args
|