fortify/helper/bwrap/config.go
Ophestra Umiker 6a2802cf30
helper: move bwrap into helper
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
2024-10-07 14:40:35 +09:00

266 lines
7.6 KiB
Go

package bwrap
import (
"os"
"strconv"
)
func (c *Config) Args() (args []string) {
b := c.boolArgs()
n := c.intArgs()
s := c.stringArgs()
p := c.pairArgs()
g := c.interfaceArgs()
argc := 0
for i, arg := range b {
if arg {
argc += len(boolArgs[i])
}
}
for _, arg := range n {
if arg != nil {
argc += 2
}
}
for _, arg := range s {
argc += len(arg) * 2
}
for _, arg := range p {
argc += len(arg) * 3
}
args = make([]string, 0, argc)
for i, arg := range b {
if arg {
args = append(args, boolArgs[i]...)
}
}
for i, arg := range n {
if arg != nil {
args = append(args, intArgs[i], strconv.Itoa(*arg))
}
}
for i, arg := range s {
for _, v := range arg {
args = append(args, stringArgs[i], v)
}
}
for i, arg := range p {
for _, v := range arg {
args = append(args, pairArgs[i], v[0], v[1])
}
}
for i, arg := range g {
for _, v := range arg {
args = append(args, v.Value(interfaceArgs[i])...)
}
}
return
}
type Config struct {
// unshare every namespace we support by default if nil
// (--unshare-all)
Unshare *UnshareConfig `json:"unshare,omitempty"`
// retain the network namespace (can only combine with nil Unshare)
// (--share-net)
Net bool `json:"net"`
// disable further use of user namespaces inside sandbox and fail unless
// further use of user namespace inside sandbox is disabled if false
// (--disable-userns) (--assert-userns-disabled)
UserNS bool `json:"userns"`
// custom uid in the sandbox, requires new user namespace
// (--uid UID)
UID *int `json:"uid,omitempty"`
// custom gid in the sandbox, requires new user namespace
// (--gid GID)
GID *int `json:"gid,omitempty"`
// custom hostname in the sandbox, requires new uts namespace
// (--hostname NAME)
Hostname string `json:"hostname,omitempty"`
// change directory
// (--chdir DIR)
Chdir string `json:"chdir,omitempty"`
// unset all environment variables
// (--clearenv)
Clearenv bool `json:"clearenv"`
// set environment variable
// (--setenv VAR VALUE)
SetEnv map[string]string `json:"setenv,omitempty"`
// unset environment variables
// (--unsetenv VAR)
UnsetEnv []string `json:"unsetenv,omitempty"`
// take a lock on file while sandbox is running
// (--lock-file DEST)
LockFile []string `json:"lock_file,omitempty"`
// bind mount host path on sandbox
// (--bind SRC DEST)
Bind [][2]string `json:"bind,omitempty"`
// equal to Bind but ignores non-existent host path
// (--bind-try SRC DEST)
BindTry [][2]string `json:"bind_try,omitempty"`
// bind mount host path on sandbox, allowing device access
// (--dev-bind SRC DEST)
DevBind [][2]string `json:"dev_bind,omitempty"`
// equal to DevBind but ignores non-existent host path
// (--dev-bind-try SRC DEST)
DevBindTry [][2]string `json:"dev_bind_try,omitempty"`
// bind mount host path readonly on sandbox
// (--ro-bind SRC DEST)
ROBind [][2]string `json:"ro_bind,omitempty"`
// equal to ROBind but ignores non-existent host path
// (--ro-bind-try SRC DEST)
ROBindTry [][2]string `json:"ro_bind_try,omitempty"`
// remount path as readonly; does not recursively remount
// (--remount-ro DEST)
RemountRO []string `json:"remount_ro,omitempty"`
// mount new procfs in sandbox
// (--proc DEST)
Procfs []PermConfig[string] `json:"proc,omitempty"`
// mount new dev in sandbox
// (--dev DEST)
DevTmpfs []PermConfig[string] `json:"dev,omitempty"`
// mount new tmpfs in sandbox
// (--tmpfs DEST)
Tmpfs []PermConfig[TmpfsConfig] `json:"tmpfs,omitempty"`
// mount new mqueue in sandbox
// (--mqueue DEST)
Mqueue []PermConfig[string] `json:"mqueue,omitempty"`
// create dir in sandbox
// (--dir DEST)
Dir []PermConfig[string] `json:"dir,omitempty"`
// create symlink within sandbox
// (--symlink SRC DEST)
Symlink []PermConfig[[2]string] `json:"symlink,omitempty"`
// change permissions (must already exist)
// (--chmod OCTAL PATH)
Chmod map[string]os.FileMode `json:"chmod,omitempty"`
// create a new terminal session
// (--new-session)
NewSession bool `json:"new_session"`
// kills with SIGKILL child process (COMMAND) when bwrap or bwrap's parent dies.
// (--die-with-parent)
DieWithParent bool `json:"die_with_parent"`
// do not install a reaper process with PID=1
// (--as-pid-1)
AsInit bool `json:"as_init"`
/* unmapped options include:
--unshare-user-try Create new user namespace if possible else continue by skipping it
--unshare-cgroup-try Create new cgroup namespace if possible else continue by skipping it
--userns FD Use this user namespace (cannot combine with --unshare-user)
--userns2 FD After setup switch to this user namespace, only useful with --userns
--pidns FD Use this pid namespace (as parent namespace if using --unshare-pid)
--sync-fd FD Keep this fd open while sandbox is running
--exec-label LABEL Exec label for the sandbox
--file-label LABEL File label for temporary sandbox content
--file FD DEST Copy from FD to destination DEST
--bind-data FD DEST Copy from FD to file which is bind-mounted on DEST
--ro-bind-data FD DEST Copy from FD to file which is readonly bind-mounted on DEST
--seccomp FD Load and use seccomp rules from FD (not repeatable)
--add-seccomp-fd FD Load and use seccomp rules from FD (repeatable)
--block-fd FD Block on FD until some data to read is available
--userns-block-fd FD Block on FD until the user namespace is ready
--info-fd FD Write information about the running container to FD
--json-status-fd FD Write container status to FD as multiple JSON documents
--cap-add CAP Add cap CAP when running as privileged user
--cap-drop CAP Drop cap CAP when running as privileged user
among which --args is used internally for passing arguments */
}
type UnshareConfig struct {
// (--unshare-user)
// create new user namespace
User bool `json:"user"`
// (--unshare-ipc)
// create new ipc namespace
IPC bool `json:"ipc"`
// (--unshare-pid)
// create new pid namespace
PID bool `json:"pid"`
// (--unshare-net)
// create new network namespace
Net bool `json:"net"`
// (--unshare-uts)
// create new uts namespace
UTS bool `json:"uts"`
// (--unshare-cgroup)
// create new cgroup namespace
CGroup bool `json:"cgroup"`
}
type TmpfsConfig struct {
// set size of tmpfs
// (--size BYTES)
Size int `json:"size,omitempty"`
// mount point of new tmpfs
// (--tmpfs DEST)
Dir string `json:"dir"`
}
type argOf interface {
Value(arg string) (args []string)
}
func copyToArgOfSlice[T [2]string | string | TmpfsConfig](src []PermConfig[T]) (dst []argOf) {
dst = make([]argOf, len(src))
for i, arg := range src {
dst[i] = arg
}
return
}
type PermConfig[T [2]string | string | TmpfsConfig] struct {
// set permissions of next argument
// (--perms OCTAL)
Mode *os.FileMode `json:"mode,omitempty"`
// path to get the new permission
// (--bind-data, --file, etc.)
Path T
}
func (p PermConfig[T]) Value(arg string) (args []string) {
// max possible size
if p.Mode != nil {
args = make([]string, 0, 6)
args = append(args, "--perms", strconv.Itoa(int(*p.Mode)))
} else {
args = make([]string, 0, 4)
}
switch v := any(p.Path).(type) {
case string:
args = append(args, arg, v)
return
case [2]string:
args = append(args, arg, v[0], v[1])
return
case TmpfsConfig:
if arg != "--tmpfs" {
panic("unreachable")
}
if v.Size > 0 {
args = append(args, "--size", strconv.Itoa(v.Size))
}
args = append(args, arg, v.Dir)
return
default:
panic("unreachable")
}
}