Skip to content

Commit

Permalink
entrypoint: remove jexec dependency
Browse files Browse the repository at this point in the history
Co-authored-by: Gijs Peskens <[email protected]>
  • Loading branch information
samuelkarp and gizahNL committed Oct 30, 2021
1 parent a37cfff commit 398df9a
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 10 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ Hello from the container!

## Implementation details

runj uses FreeBSD's userland utilities for managing jails; it does not directly
invoke the jail-related syscalls. You must have working versions of `jail(8)`,
`jls(8)`, `jexec(8)`, and `ps(1)` installed on your system. `runj kill` makes
use of the `kill(1)` command inside the jail's rootfs; if this command does not
exist (or is not functional), `runj kill` will not work.
runj uses both FreeBSD's userland utilities for managing jails and jail-related
syscalls. You must have working versions of `jail(8)`, `jls(8)`, `jexec(8)`,
and `ps(1)` installed on your system. `runj kill` makes use of the `kill(1)`
command inside the jail's rootfs; if this command does not exist (or is not
functional), `runj kill` will not work.

## Contributing

Expand Down
35 changes: 30 additions & 5 deletions cmd/runj-entrypoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"syscall"

"go.sbk.wtf/runj/jail"

"github.com/containerd/console"
"golang.org/x/sys/unix"
)
Expand All @@ -48,7 +51,6 @@ func main() {
var errUsage = errors.New("usage: runj-entrypoint JAIL-ID FIFO-PATH PROGRAM [ARGS...]")

const (
jexecPath = "/usr/sbin/jexec"
consoleSocketEnv = "__RUNJ_CONSOLE_SOCKET"

// skipExecFifo signals that the exec fifo sync procedure should be skipped
Expand All @@ -61,7 +63,8 @@ func _main() (int, error) {
}
jid := os.Args[1]
fifoPath := os.Args[2]
argv := os.Args[3:]
command := os.Args[3]
argv := os.Args[4:]

if err := setupConsole(); err != nil {
return 2, err
Expand All @@ -78,9 +81,31 @@ func _main() (int, error) {
}
}

// call unix.Exec (which is execve(2)) to replace this process with the jexec
if err := unix.Exec(jexecPath, append([]string{"jexec", jid}, argv...), unix.Environ()); err != nil {
return 6, fmt.Errorf("failed to exec: %w", err)
j, err := jail.FromName(jid)
if err != nil {
return 5, err
}

// attach places us inside the jail and implicitly does a chroot
err = j.Attach()
if err != nil {
return 6, err
}

// change to the jail's root
err = os.Chdir("/")
if err != nil {
return 7, err
}

// unix.Exec requires the full path to the supplied command
cmdpath, err := exec.LookPath(command)
if err != nil {
return 8, err
}
// call unix.Exec (which is execve(2)) to replace this process with the command
if err := unix.Exec(cmdpath, append([]string{command}, argv...), unix.Environ()); err != nil {
return 9, fmt.Errorf("failed to exec: %w", err)
}
return 0, nil
}
Expand Down
24 changes: 24 additions & 0 deletions jail/jail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jail

type Jail interface {
// Attach attaches the current running process to the jail
Attach() error
}

type jail struct {
id ID
}

// FromName queries the OS for a jail with the specified name
func FromName(name string) (Jail, error) {
id, err := find(name)
if err != nil {
return nil, err
}
return &jail{id: id}, nil
}

// Attach attaches the current running process to the jail
func (j *jail) Attach() error {
return attach(j.id)
}
100 changes: 100 additions & 0 deletions jail/syscall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package jail

import (
"errors"
"math"
"strconv"
"syscall"
"unsafe"
)

// ID identifies jails
type ID int32

// attach attaches the current process to the jail
func attach(jid ID) error {
return jidSyscall(syscall.SYS_JAIL_ATTACH, jid)
}

// find queries the OS for a jail with the specified name or JID
func find(identifier string) (ID, error) {
params := &findIovec{}
jid, err := strconv.Atoi(identifier)
if err == nil {
if jid == 0 {
return 0, nil
}
if jid > math.MaxInt32 {
return 0, errors.New("invalid jid")
}
params.jid = int32(jid)
} else {
params.name = identifier
}
iovec, err := params.serialize()
if err != nil {
return 0, err
}
return get(iovec, 0)
}

type findIovec struct {
jid int32
name string
}

func (f *findIovec) serialize() ([]syscall.Iovec, error) {
iovec := make([]syscall.Iovec, 0)
if f.jid != 0 {
jidKey, err := syscall.ByteSliceFromString("jid")
if err != nil {
return nil, err
}
jidVal := (*byte)(unsafe.Pointer(&f.jid))
iovec = append(iovec, makeIovec(jidKey, jidVal, 4)...)
}
if f.name != "" {
nameKey, err := syscall.ByteSliceFromString("name")
if err != nil {
return nil, err
}
nameVal, err := syscall.BytePtrFromString(f.name)
if err != nil {
return nil, err
}
iovec = append(iovec, makeIovec(nameKey, nameVal, len(f.name)+1)...)
}
return iovec, nil
}

func makeIovec(name []byte, value *byte, valuesize int) []syscall.Iovec {
iovecs := make([]syscall.Iovec, 2)

iovecs[0].Base = &name[0]
iovecs[0].SetLen(len(name))

iovecs[1].Base = value
iovecs[1].SetLen(valuesize)
return iovecs
}

// get calls SYS_JAIL_GET
func get(iovecs []syscall.Iovec, flags int) (ID, error) {
return iovSyscall(syscall.SYS_JAIL_GET, iovecs, flags)
}

func jidSyscall(callnum uintptr, jid ID) error {
_, _, errno := syscall.Syscall(callnum, uintptr(jid), 0, 0)
if errno != 0 {
return errno
}
return nil
}

func iovSyscall(callnum uintptr, iovecs []syscall.Iovec, flags int) (ID, error) {
jid, _, errno := syscall.Syscall(callnum, uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)), uintptr(flags))
if int32(jid) == -1 || errno != 0 {
return ID(jid), errno
}
return ID(jid), nil
}

0 comments on commit 398df9a

Please sign in to comment.