mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
make pivot root readonly & add sync socketpair
This commit is contained in:
parent
67c0621b7f
commit
f7f041e1ef
@ -53,6 +53,7 @@ func (r *Runner) Start() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// pipe p used to notify child the uid / gid mapping have been setup
|
||||
if unshareUser {
|
||||
err = unix.Pipe2(p[:], unix.O_CLOEXEC)
|
||||
if err != nil {
|
||||
@ -60,8 +61,19 @@ func (r *Runner) Start() (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// socketpair p2 used to sync with parent before final execve
|
||||
p2, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
if p[0] > 0 { // avoid fd leak
|
||||
unix.Close(p[0])
|
||||
unix.Close(p[1])
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// similar to exec_linux, avoid side effect by shuffling around
|
||||
fd, nextfd := prepareFds(r.Files)
|
||||
pipe := p2[1]
|
||||
|
||||
// Acquire the fork lock so that no other threads
|
||||
// create new fds that are not yet close-on-exec
|
||||
@ -90,6 +102,24 @@ func (r *Runner) Start() (int, error) {
|
||||
unix.Close(p[1])
|
||||
}
|
||||
|
||||
// sync with child
|
||||
unix.Close(p2[1])
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(p2[0]), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
if err1 != 0 {
|
||||
unix.Close(p2[0])
|
||||
return int(pid), syscall.Errno(err1)
|
||||
}
|
||||
|
||||
if r.SyncFunc != nil {
|
||||
err = r.SyncFunc(int(pid))
|
||||
}
|
||||
if err == nil {
|
||||
err1 = 0
|
||||
syscall.RawSyscall(syscall.SYS_WRITE, uintptr(p2[0]), uintptr(unsafe.Pointer(&err1)), uintptr(unsafe.Sizeof((err1))))
|
||||
}
|
||||
|
||||
unix.Close(p2[0])
|
||||
|
||||
if err1 != 0 {
|
||||
return int(pid), syscall.Errno(err1)
|
||||
}
|
||||
@ -134,14 +164,21 @@ func (r *Runner) Start() (int, error) {
|
||||
|
||||
// Pass 1 & pass 2 assigns fds for child process
|
||||
// Pass 1: fd[i] < i => nextfd
|
||||
if pipe < nextfd {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(pipe), uintptr(nextfd), syscall.FD_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
for i := 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < int(i) {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), 0)
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), syscall.FD_CLOEXEC)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
// Set up close on exec
|
||||
syscall.RawSyscall(syscall.SYS_FCNTL, uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC)
|
||||
fd[i] = nextfd
|
||||
nextfd++
|
||||
}
|
||||
@ -229,29 +266,38 @@ func (r *Runner) Start() (int, error) {
|
||||
|
||||
// pivit_root
|
||||
if pivotRoot != nil {
|
||||
// mkdir(root/old_root)
|
||||
// mkdir("old_root")
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_MKDIRAT, uintptr(_AT_FDCWD), uintptr(unsafe.Pointer(oldRoot)), 0755)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// pivot_root(root, root/old_root)
|
||||
// pivot_root(root, "old_root")
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(pivotRoot)), uintptr(unsafe.Pointer(oldRoot)), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// umount(root/old, MNT_DETACH)
|
||||
// umount("old_root", MNT_DETACH)
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_UMOUNT2, uintptr(unsafe.Pointer(oldRoot)), syscall.MNT_DETACH, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// rmdir(root/old_root)
|
||||
// rmdir("old_root")
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_UNLINKAT, uintptr(_AT_FDCWD), uintptr(unsafe.Pointer(oldRoot)), uintptr(unix.AT_REMOVEDIR))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// mount("tmpfs", "/", "tmpfs", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOATIME | MS_NOSUID, nil)
|
||||
_, _, err1 = syscall.RawSyscall6(syscall.SYS_MOUNT, uintptr(unsafe.Pointer(&tmpfs[0])),
|
||||
uintptr(unsafe.Pointer(&slash[0])), uintptr(unsafe.Pointer(&tmpfs[0])),
|
||||
uintptr(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_NOATIME|syscall.MS_NOSUID),
|
||||
uintptr(unsafe.Pointer(&empty[0])), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// chdir for child
|
||||
@ -288,7 +334,7 @@ func (r *Runner) Start() (int, error) {
|
||||
}
|
||||
|
||||
// Enable Ptrace
|
||||
if r.Ptrace {
|
||||
if r.Ptrace && r.Seccomp != nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
@ -320,6 +366,26 @@ func (r *Runner) Start() (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Before exec, sync with parent through pipe (configured as close_on_exec)
|
||||
err2 = 0
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
if r1 == 0 || err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
r1, _, err1 = syscall.RawSyscall(syscall.SYS_READ, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), uintptr(unsafe.Sizeof(err2)))
|
||||
if r1 == 0 || err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// Enable ptrace if no seccomp is needed
|
||||
if r.Ptrace && r.Seccomp == nil {
|
||||
_, _, err1 = syscall.RawSyscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, runner is successfully attached for seccomp trap filter
|
||||
// or execve traped without seccomp filter
|
||||
// time to exec
|
||||
|
||||
@ -51,22 +51,31 @@ type Runner struct {
|
||||
|
||||
// mounts defines the mount syscalls after unshare mount namespace
|
||||
// need CAP_ADMIN inside the namespace (e.g. unshare user namespace)
|
||||
// if pivot root is provided, relative target will based on PivotRoot directory
|
||||
// if pivot root is provided, relative target is better for chdir-mount meta
|
||||
// and pivot root will mount as tmpfs before any mount
|
||||
Mounts []*mount.Mount
|
||||
|
||||
// pivot_root defines the new root after unshare mount namespace
|
||||
// root need to be a mount point (e.g. defined in Mounts) and it should
|
||||
// be a absolute path
|
||||
// It will call:
|
||||
// mkdir(root/old_root)
|
||||
// pivot_root(root, root/old_root)
|
||||
// umount(root/old, MNT_DETACH)
|
||||
// rmdir(root/old_root)
|
||||
// pivot_root defines a readonly new root after unshare mount namespace
|
||||
// it should be a directory in absolute path
|
||||
// Before mounts, tt will call:
|
||||
// mount("tmpfs", root, "tmpfs", 0, nil)
|
||||
// chdir(root)
|
||||
// After mounts, it will call:
|
||||
// mkdir("old_root")
|
||||
// pivot_root(root, "old_root")
|
||||
// umount("old_root", MNT_DETACH)
|
||||
// rmdir("old_root")
|
||||
// mount("tmpfs", "/", "tmpfs", MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOATIME | MS_NOSUID, nil)
|
||||
PivotRoot string
|
||||
|
||||
// drop_caps calls cap_set(self, 0) to drop all capabilities
|
||||
// from effective, permitted, inheritable capability sets before execve
|
||||
// it should avoid calls to set
|
||||
// it should avoid calls to set ambient capabilities
|
||||
DropCaps bool
|
||||
|
||||
// Parent and child process with sync sataus through a socket pair.
|
||||
// SyncFunc will invoke with the child pid. If SyncFunc return some error,
|
||||
// parent will signal child to stop and report the error
|
||||
// SyncFunc is called right before execve, thus it could track cpu more percisely
|
||||
SyncFunc func(int) error
|
||||
}
|
||||
|
||||
@ -14,9 +14,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// UnshareFlags is flags used to create namespaces
|
||||
UnshareFlags = unix.CLONE_NEWIPC | unix.CLONE_NEWNET | unix.CLONE_NEWNS |
|
||||
unix.CLONE_NEWPID | unix.CLONE_NEWUSER | unix.CLONE_NEWUTS | unix.CLONE_NEWCGROUP
|
||||
// UnshareFlags is flags used to create namespaces except NET and IPC
|
||||
UnshareFlags = unix.CLONE_NEWNS | unix.CLONE_NEWPID | unix.CLONE_NEWUSER | unix.CLONE_NEWUTS | unix.CLONE_NEWCGROUP
|
||||
)
|
||||
|
||||
// Start starts the unshared process
|
||||
|
||||
Loading…
Reference in New Issue
Block a user