diff options
Diffstat (limited to 'bwrap/config.go')
| -rw-r--r-- | bwrap/config.go | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/bwrap/config.go b/bwrap/config.go new file mode 100644 index 00000000..c20a33b0 --- /dev/null +++ b/bwrap/config.go @@ -0,0 +1,265 @@ +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") + } +} |
