aboutsummaryrefslogtreecommitdiffhomepage
path: root/bwrap/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'bwrap/config.go')
-rw-r--r--bwrap/config.go265
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")
+ }
+}