aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/bwrap
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-01-15 10:57:10 +0900
committerOphestra <cat@gensokyo.uk>2025-01-15 13:07:06 +0900
commit3e11ce6868182f1a3f74d5bf6c1db761097666fa (patch)
tree85c611acc2aadb5b5b7818ad5a6a0769a6d71fa4 /helper/bwrap
parent562f5ed7971603a4e4e3315a7fe4188153bfc205 (diff)
helper/bwrap: separate sequential/static args
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper/bwrap')
-rw-r--r--helper/bwrap/arg.static.awkward.go23
-rw-r--r--helper/bwrap/arg.static.bool.go81
-rw-r--r--helper/bwrap/arg.static.int.go47
-rw-r--r--helper/bwrap/arg.static.pair.go73
-rw-r--r--helper/bwrap/arg.static.string.go65
-rw-r--r--helper/bwrap/builder.go (renamed from helper/bwrap/config.set.go)46
-rw-r--r--helper/bwrap/config.go158
-rw-r--r--helper/bwrap/config_test.go295
-rw-r--r--helper/bwrap/sequential.go223
-rw-r--r--helper/bwrap/static.go249
10 files changed, 648 insertions, 612 deletions
diff --git a/helper/bwrap/arg.static.awkward.go b/helper/bwrap/arg.static.awkward.go
deleted file mode 100644
index 91fde87a..00000000
--- a/helper/bwrap/arg.static.awkward.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package bwrap
-
-const (
- Tmpfs = iota
- Dir
- Symlink
-
- OverlaySrc
- Overlay
- TmpOverlay
- ROOverlay
-)
-
-var awkwardArgs = [...]string{
- Tmpfs: "--tmpfs",
- Dir: "--dir",
- Symlink: "--symlink",
-
- OverlaySrc: "--overlay-src",
- Overlay: "--overlay",
- TmpOverlay: "--tmp-overlay",
- ROOverlay: "--ro-overlay",
-}
diff --git a/helper/bwrap/arg.static.bool.go b/helper/bwrap/arg.static.bool.go
deleted file mode 100644
index 6c3ca0c4..00000000
--- a/helper/bwrap/arg.static.bool.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package bwrap
-
-const (
- UnshareAll = iota
- UnshareUser
- UnshareIPC
- UnsharePID
- UnshareNet
- UnshareUTS
- UnshareCGroup
- ShareNet
-
- UserNS
- Clearenv
-
- NewSession
- DieWithParent
- AsInit
-)
-
-var boolArgs = [...][]string{
- UnshareAll: {"--unshare-all", "--unshare-user"},
- UnshareUser: {"--unshare-user"},
- UnshareIPC: {"--unshare-ipc"},
- UnsharePID: {"--unshare-pid"},
- UnshareNet: {"--unshare-net"},
- UnshareUTS: {"--unshare-uts"},
- UnshareCGroup: {"--unshare-cgroup"},
- ShareNet: {"--share-net"},
-
- UserNS: {"--disable-userns", "--assert-userns-disabled"},
- Clearenv: {"--clearenv"},
-
- NewSession: {"--new-session"},
- DieWithParent: {"--die-with-parent"},
- AsInit: {"--as-pid-1"},
-}
-
-func (c *Config) boolArgs() Builder {
- b := boolArg{
- UserNS: !c.UserNS,
- Clearenv: c.Clearenv,
-
- NewSession: c.NewSession,
- DieWithParent: c.DieWithParent,
- AsInit: c.AsInit,
- }
-
- if c.Unshare == nil {
- b[UnshareAll] = true
- b[ShareNet] = c.Net
- } else {
- b[UnshareUser] = c.Unshare.User
- b[UnshareIPC] = c.Unshare.IPC
- b[UnsharePID] = c.Unshare.PID
- b[UnshareNet] = c.Unshare.Net
- b[UnshareUTS] = c.Unshare.UTS
- b[UnshareCGroup] = c.Unshare.CGroup
- }
-
- return &b
-}
-
-type boolArg [len(boolArgs)]bool
-
-func (b *boolArg) Len() (l int) {
- for i, v := range b {
- if v {
- l += len(boolArgs[i])
- }
- }
- return
-}
-
-func (b *boolArg) Append(args *[]string) {
- for i, v := range b {
- if v {
- *args = append(*args, boolArgs[i]...)
- }
- }
-}
diff --git a/helper/bwrap/arg.static.int.go b/helper/bwrap/arg.static.int.go
deleted file mode 100644
index cba3b1f7..00000000
--- a/helper/bwrap/arg.static.int.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package bwrap
-
-import "strconv"
-
-const (
- UID = iota
- GID
- Perms
- Size
-)
-
-var intArgs = [...]string{
- UID: "--uid",
- GID: "--gid",
- Perms: "--perms",
- Size: "--size",
-}
-
-func (c *Config) intArgs() Builder {
- // Arg types:
- // Perms
- // are handled by the sequential builder
-
- return &intArg{
- UID: c.UID,
- GID: c.GID,
- }
-}
-
-type intArg [len(intArgs)]*int
-
-func (n *intArg) Len() (l int) {
- for _, v := range n {
- if v != nil {
- l += 2
- }
- }
- return
-}
-
-func (n *intArg) Append(args *[]string) {
- for i, v := range n {
- if v != nil {
- *args = append(*args, intArgs[i], strconv.Itoa(*v))
- }
- }
-}
diff --git a/helper/bwrap/arg.static.pair.go b/helper/bwrap/arg.static.pair.go
deleted file mode 100644
index 18d3415d..00000000
--- a/helper/bwrap/arg.static.pair.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package bwrap
-
-import (
- "slices"
-)
-
-const (
- SetEnv = iota
-
- Bind
- BindTry
- DevBind
- DevBindTry
- ROBind
- ROBindTry
-
- Chmod
-)
-
-var pairArgs = [...]string{
- SetEnv: "--setenv",
-
- Bind: "--bind",
- BindTry: "--bind-try",
- DevBind: "--dev-bind",
- DevBindTry: "--dev-bind-try",
- ROBind: "--ro-bind",
- ROBindTry: "--ro-bind-try",
-
- Chmod: "--chmod",
-}
-
-func (c *Config) pairArgs() Builder {
- var n pairArg
- n[SetEnv] = make([][2]string, len(c.SetEnv))
- keys := make([]string, 0, len(c.SetEnv))
- for k := range c.SetEnv {
- keys = append(keys, k)
- }
- slices.Sort(keys)
- for i, k := range keys {
- n[SetEnv][i] = [2]string{k, c.SetEnv[k]}
- }
-
- // Arg types:
- // Bind
- // BindTry
- // DevBind
- // DevBindTry
- // ROBind
- // ROBindTry
- // Chmod
- // are handled by the sequential builder
-
- return &n
-}
-
-type pairArg [len(pairArgs)][][2]string
-
-func (p *pairArg) Len() (l int) {
- for _, v := range p {
- l += len(v) * 3
- }
- return
-}
-
-func (p *pairArg) Append(args *[]string) {
- for i, arg := range p {
- for _, v := range arg {
- *args = append(*args, pairArgs[i], v[0], v[1])
- }
- }
-}
diff --git a/helper/bwrap/arg.static.string.go b/helper/bwrap/arg.static.string.go
deleted file mode 100644
index e7f5c332..00000000
--- a/helper/bwrap/arg.static.string.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package bwrap
-
-const (
- Hostname = iota
- Chdir
- UnsetEnv
- LockFile
-
- RemountRO
- Procfs
- DevTmpfs
- Mqueue
-)
-
-var stringArgs = [...]string{
- Hostname: "--hostname",
- Chdir: "--chdir",
- UnsetEnv: "--unsetenv",
- LockFile: "--lock-file",
-
- RemountRO: "--remount-ro",
- Procfs: "--proc",
- DevTmpfs: "--dev",
- Mqueue: "--mqueue",
-}
-
-func (c *Config) stringArgs() Builder {
- n := stringArg{
- UnsetEnv: c.UnsetEnv,
- LockFile: c.LockFile,
- }
-
- if c.Hostname != "" {
- n[Hostname] = []string{c.Hostname}
- }
- if c.Chdir != "" {
- n[Chdir] = []string{c.Chdir}
- }
-
- // Arg types:
- // RemountRO
- // Procfs
- // DevTmpfs
- // Mqueue
- // are handled by the sequential builder
-
- return &n
-}
-
-type stringArg [len(stringArgs)][]string
-
-func (s *stringArg) Len() (l int) {
- for _, arg := range s {
- l += len(arg) * 2
- }
- return
-}
-
-func (s *stringArg) Append(args *[]string) {
- for i, arg := range s {
- for _, v := range arg {
- *args = append(*args, stringArgs[i], v)
- }
- }
-}
diff --git a/helper/bwrap/config.set.go b/helper/bwrap/builder.go
index 8cbafda3..bd67da1c 100644
--- a/helper/bwrap/config.set.go
+++ b/helper/bwrap/builder.go
@@ -39,46 +39,60 @@ func (c *Config) Bind(src, dest string, opts ...bool) *Config {
if dev {
if try {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[DevBindTry], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{DevBindTry.Unwrap(), src, dest})
} else {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[DevBind], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{DevBind.Unwrap(), src, dest})
}
return c
} else if write {
if try {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[BindTry], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{BindTry.Unwrap(), src, dest})
} else {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[Bind], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{Bind.Unwrap(), src, dest})
}
return c
} else {
if try {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[ROBindTry], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{ROBindTry.Unwrap(), src, dest})
} else {
- c.Filesystem = append(c.Filesystem, &pairF{pairArgs[ROBind], src, dest})
+ c.Filesystem = append(c.Filesystem, &pairF{ROBind.Unwrap(), src, dest})
}
return c
}
}
+// Dir create dir in sandbox
+// (--dir DEST)
+func (c *Config) Dir(dest string) *Config {
+ c.Filesystem = append(c.Filesystem, &stringF{Dir.Unwrap(), dest})
+ return c
+}
+
// RemountRO remount path as readonly; does not recursively remount
// (--remount-ro DEST)
func (c *Config) RemountRO(dest string) *Config {
- c.Filesystem = append(c.Filesystem, &stringF{stringArgs[RemountRO], dest})
+ c.Filesystem = append(c.Filesystem, &stringF{RemountRO.Unwrap(), dest})
return c
}
// Procfs mount new procfs in sandbox
// (--proc DEST)
func (c *Config) Procfs(dest string) *Config {
- c.Filesystem = append(c.Filesystem, &stringF{stringArgs[Procfs], dest})
+ c.Filesystem = append(c.Filesystem, &stringF{Procfs.Unwrap(), dest})
return c
}
// DevTmpfs mount new dev in sandbox
// (--dev DEST)
func (c *Config) DevTmpfs(dest string) *Config {
- c.Filesystem = append(c.Filesystem, &stringF{stringArgs[DevTmpfs], dest})
+ c.Filesystem = append(c.Filesystem, &stringF{DevTmpfs.Unwrap(), dest})
+ return c
+}
+
+// Mqueue mount new mqueue in sandbox
+// (--mqueue DEST)
+func (c *Config) Mqueue(dest string) *Config {
+ c.Filesystem = append(c.Filesystem, &stringF{Mqueue.Unwrap(), dest})
return c
}
@@ -121,20 +135,6 @@ func (c *Config) Persist(dest, rwsrc, workdir string, src ...string) *Config {
return c
}
-// Mqueue mount new mqueue in sandbox
-// (--mqueue DEST)
-func (c *Config) Mqueue(dest string) *Config {
- c.Filesystem = append(c.Filesystem, &stringF{stringArgs[Mqueue], dest})
- return c
-}
-
-// Dir create dir in sandbox
-// (--dir DEST)
-func (c *Config) Dir(dest string) *Config {
- c.Filesystem = append(c.Filesystem, &stringF{awkwardArgs[Dir], dest})
- return c
-}
-
// Symlink create symlink within sandbox
// (--symlink SRC DEST)
func (c *Config) Symlink(src, dest string, perm ...os.FileMode) *Config {
diff --git a/helper/bwrap/config.go b/helper/bwrap/config.go
index c1ab37ca..6ca67793 100644
--- a/helper/bwrap/config.go
+++ b/helper/bwrap/config.go
@@ -1,17 +1,9 @@
package bwrap
import (
- "encoding/gob"
"os"
- "strconv"
)
-func init() {
- gob.Register(new(PermConfig[SymlinkConfig]))
- gob.Register(new(PermConfig[*TmpfsConfig]))
- gob.Register(new(OverlayConfig))
-}
-
type Config struct {
// unshare every namespace we support by default if nil
// (--unshare-all)
@@ -124,153 +116,3 @@ type UnshareConfig struct {
// create new cgroup namespace
CGroup bool `json:"cgroup"`
}
-
-type PermConfig[T FSBuilder] struct {
- // set permissions of next argument
- // (--perms OCTAL)
- Mode *os.FileMode `json:"mode,omitempty"`
- // path to get the new permission
- // (--bind-data, --file, etc.)
- Inner T `json:"path"`
-}
-
-func (p *PermConfig[T]) Path() string {
- return p.Inner.Path()
-}
-
-func (p *PermConfig[T]) Len() int {
- if p.Mode != nil {
- return p.Inner.Len() + 2
- } else {
- return p.Inner.Len()
- }
-}
-
-func (p *PermConfig[T]) Append(args *[]string) {
- if p.Mode != nil {
- *args = append(*args, intArgs[Perms], strconv.FormatInt(int64(*p.Mode), 8))
- }
- p.Inner.Append(args)
-}
-
-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"`
-}
-
-func (t *TmpfsConfig) Path() string {
- return t.Dir
-}
-
-func (t *TmpfsConfig) Len() int {
- if t.Size > 0 {
- return 4
- } else {
- return 2
- }
-}
-
-func (t *TmpfsConfig) Append(args *[]string) {
- if t.Size > 0 {
- *args = append(*args, intArgs[Size], strconv.Itoa(t.Size))
- }
- *args = append(*args, awkwardArgs[Tmpfs], t.Dir)
-}
-
-type OverlayConfig struct {
- /*
- read files from SRC in the following overlay
- (--overlay-src SRC)
- */
- Src []string `json:"src,omitempty"`
-
- /*
- mount overlayfs on DEST, with RWSRC as the host path for writes and
- WORKDIR an empty directory on the same filesystem as RWSRC
- (--overlay RWSRC WORKDIR DEST)
-
- if nil, mount overlayfs on DEST, with writes going to an invisible tmpfs
- (--tmp-overlay DEST)
-
- if either strings are empty, mount overlayfs read-only on DEST
- (--ro-overlay DEST)
- */
- Persist *[2]string `json:"persist,omitempty"`
-
- /*
- --overlay RWSRC WORKDIR DEST
-
- --tmp-overlay DEST
-
- --ro-overlay DEST
- */
- Dest string `json:"dest"`
-}
-
-func (o *OverlayConfig) Path() string {
- return o.Dest
-}
-
-func (o *OverlayConfig) Len() int {
- // (--tmp-overlay DEST) or (--ro-overlay DEST)
- p := 2
- // (--overlay RWSRC WORKDIR DEST)
- if o.Persist != nil && o.Persist[0] != "" && o.Persist[1] != "" {
- p = 4
- }
-
- return p + len(o.Src)*2
-}
-
-func (o *OverlayConfig) Append(args *[]string) {
- // --overlay-src SRC
- for _, src := range o.Src {
- *args = append(*args, awkwardArgs[OverlaySrc], src)
- }
-
- if o.Persist != nil {
- if o.Persist[0] != "" && o.Persist[1] != "" {
- // --overlay RWSRC WORKDIR
- *args = append(*args, awkwardArgs[Overlay], o.Persist[0], o.Persist[1])
- } else {
- // --ro-overlay
- *args = append(*args, awkwardArgs[ROOverlay])
- }
- } else {
- // --tmp-overlay
- *args = append(*args, awkwardArgs[TmpOverlay])
- }
-
- // DEST
- *args = append(*args, o.Dest)
-}
-
-type SymlinkConfig [2]string
-
-func (s SymlinkConfig) Path() string {
- return s[1]
-}
-
-func (s SymlinkConfig) Len() int {
- return 3
-}
-
-func (s SymlinkConfig) Append(args *[]string) {
- *args = append(*args, awkwardArgs[Symlink], s[0], s[1])
-}
-
-type ChmodConfig map[string]os.FileMode
-
-func (c ChmodConfig) Len() int {
- return len(c)
-}
-
-func (c ChmodConfig) Append(args *[]string) {
- for path, mode := range c {
- *args = append(*args, pairArgs[Chmod], strconv.FormatInt(int64(mode), 8), path)
- }
-}
diff --git a/helper/bwrap/config_test.go b/helper/bwrap/config_test.go
index afbcedea..95749f09 100644
--- a/helper/bwrap/config_test.go
+++ b/helper/bwrap/config_test.go
@@ -1,6 +1,7 @@
package bwrap_test
import (
+ "os"
"slices"
"testing"
@@ -14,6 +15,83 @@ func TestConfig_Args(t *testing.T) {
want []string
}{
{
+ name: "bind",
+ conf: (new(bwrap.Config)).
+ Bind("/etc", "/.fortify/etc").
+ Bind("/etc", "/.fortify/etc", true).
+ Bind("/run", "/.fortify/run", false, true).
+ Bind("/sys/devices", "/.fortify/sys/devices", true, true).
+ Bind("/dev/dri", "/.fortify/dev/dri", false, true, true).
+ Bind("/dev/dri", "/.fortify/dev/dri", true, true, true),
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // Bind("/etc", "/.fortify/etc")
+ "--ro-bind", "/etc", "/.fortify/etc",
+ // Bind("/etc", "/.fortify/etc", true)
+ "--ro-bind-try", "/etc", "/.fortify/etc",
+ // Bind("/run", "/.fortify/run", false, true)
+ "--bind", "/run", "/.fortify/run",
+ // Bind("/sys/devices", "/.fortify/sys/devices", true, true)
+ "--bind-try", "/sys/devices", "/.fortify/sys/devices",
+ // Bind("/dev/dri", "/.fortify/dev/dri", false, true, true)
+ "--dev-bind", "/dev/dri", "/.fortify/dev/dri",
+ // Bind("/dev/dri", "/.fortify/dev/dri", true, true, true)
+ "--dev-bind-try", "/dev/dri", "/.fortify/dev/dri",
+ },
+ },
+ {
+ name: "dir remount-ro proc dev mqueue",
+ conf: (new(bwrap.Config)).
+ Dir("/.fortify").
+ RemountRO("/home").
+ Procfs("/proc").
+ DevTmpfs("/dev").
+ Mqueue("/dev/mqueue"),
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // Dir("/.fortify")
+ "--dir", "/.fortify",
+ // RemountRO("/home")
+ "--remount-ro", "/home",
+ // Procfs("/proc")
+ "--proc", "/proc",
+ // DevTmpfs("/dev")
+ "--dev", "/dev",
+ // Mqueue("/dev/mqueue")
+ "--mqueue", "/dev/mqueue",
+ },
+ },
+ {
+ name: "tmpfs",
+ conf: (new(bwrap.Config)).
+ Tmpfs("/run/user", 8192).
+ Tmpfs("/run/dbus", 8192, 0755),
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // Tmpfs("/run/user", 8192)
+ "--size", "8192", "--tmpfs", "/run/user",
+ // Tmpfs("/run/dbus", 8192, 0755)
+ "--perms", "755", "--size", "8192", "--tmpfs", "/run/dbus",
+ },
+ },
+ {
+ name: "symlink",
+ conf: (new(bwrap.Config)).
+ Symlink("/.fortify/sbin/init", "/sbin/init").
+ Symlink("/.fortify/sbin/init", "/sbin/init", 0755),
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // Symlink("/.fortify/sbin/init", "/sbin/init")
+ "--symlink", "/.fortify/sbin/init", "/sbin/init",
+ // Symlink("/.fortify/sbin/init", "/sbin/init", 0755)
+ "--perms", "755", "--symlink", "/.fortify/sbin/init", "/sbin/init",
+ },
+ },
+ {
name: "overlayfs",
conf: (new(bwrap.Config)).
Overlay("/etc", "/etc").
@@ -33,6 +111,64 @@ func TestConfig_Args(t *testing.T) {
},
},
{
+ name: "unshare",
+ conf: &bwrap.Config{Unshare: &bwrap.UnshareConfig{
+ User: false,
+ IPC: false,
+ PID: false,
+ Net: false,
+ UTS: false,
+ CGroup: false,
+ }},
+ want: []string{"--disable-userns", "--assert-userns-disabled"},
+ },
+ {
+ name: "uid gid sync",
+ conf: (new(bwrap.Config)).
+ SetUID(1971).
+ SetGID(100).
+ SetSync(os.Stdin),
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // SetUID(1971)
+ "--uid", "1971",
+ // SetGID(100)
+ "--gid", "100",
+ // SetSync(os.Stdin)
+ // this is set when the process is created
+ },
+ },
+ {
+ name: "hostname chdir setenv unsetenv lockfile chmod",
+ conf: &bwrap.Config{
+ Hostname: "fortify",
+ Chdir: "/.fortify",
+ SetEnv: map[string]string{"FORTIFY_INIT": "/.fortify/sbin/init"},
+ UnsetEnv: []string{"HOME", "HOST"},
+ LockFile: []string{"/.fortify/lock"},
+ Chmod: map[string]os.FileMode{"/.fortify/sbin/init": 0755},
+ },
+ want: []string{
+ "--unshare-all", "--unshare-user",
+ "--disable-userns", "--assert-userns-disabled",
+ // Hostname: "fortify"
+ "--hostname", "fortify",
+ // Chdir: "/.fortify"
+ "--chdir", "/.fortify",
+ // UnsetEnv: []string{"HOME", "HOST"}
+ "--unsetenv", "HOME",
+ "--unsetenv", "HOST",
+ // LockFile: []string{"/.fortify/lock"},
+ "--lock-file", "/.fortify/lock",
+ // SetEnv: map[string]string{"FORTIFY_INIT": "/.fortify/sbin/init"}
+ "--setenv", "FORTIFY_INIT", "/.fortify/sbin/init",
+ // Chmod: map[string]os.FileMode{"/.fortify/sbin/init": 0755}
+ "--chmod", "755", "/.fortify/sbin/init",
+ },
+ },
+
+ {
name: "xdg-dbus-proxy constraint sample",
conf: (&bwrap.Config{
Unshare: nil,
@@ -90,148 +226,6 @@ func TestConfig_Args(t *testing.T) {
"--ro-bind", "/etc", "/etc",
},
},
- {
- name: "fortify permissive default nixos",
- conf: (&bwrap.Config{
- Unshare: nil,
- Net: true,
- UserNS: true,
- Clearenv: true,
- SetEnv: map[string]string{
- "HOME": "/home/chronos",
- "TERM": "xterm-256color",
- "FORTIFY_INIT": "3",
- "XDG_RUNTIME_DIR": "/run/user/150",
- "XDG_SESSION_CLASS": "user",
- "XDG_SESSION_TYPE": "tty",
- "SHELL": "/run/current-system/sw/bin/zsh",
- "USER": "chronos",
- },
- DieWithParent: true,
- AsInit: true,
- }).SetUID(65534).SetGID(65534).
- Procfs("/proc").DevTmpfs("/dev").Mqueue("/dev/mqueue").
- Bind("/bin", "/bin", false, true).
- Bind("/boot", "/boot", false, true).
- Bind("/etc", "/etc", false, true).
- Bind("/home", "/home", false, true).
- Bind("/lib", "/lib", false, true).
- Bind("/lib64", "/lib64", false, true).
- Bind("/nix", "/nix", false, true).
- Bind("/root", "/root", false, true).
- Bind("/srv", "/srv", false, true).
- Bind("/sys", "/sys", false, true).
- Bind("/usr", "/usr", false, true).
- Bind("/var", "/var", false, true).
- Bind("/run/NetworkManager", "/run/NetworkManager", false, true).
- Bind("/run/agetty.reload", "/run/agetty.reload", false, true).
- Bind("/run/binfmt", "/run/binfmt", false, true).
- Bind("/run/booted-system", "/run/booted-system", false, true).
- Bind("/run/credentials", "/run/credentials", false, true).
- Bind("/run/cryptsetup", "/run/cryptsetup", false, true).
- Bind("/run/current-system", "/run/current-system", false, true).
- Bind("/run/host", "/run/host", false, true).
- Bind("/run/keys", "/run/keys", false, true).
- Bind("/run/libvirt", "/run/libvirt", false, true).
- Bind("/run/libvirtd.pid", "/run/libvirtd.pid", false, true).
- Bind("/run/lock", "/run/lock", false, true).
- Bind("/run/log", "/run/log", false, true).
- Bind("/run/lvm", "/run/lvm", false, true).
- Bind("/run/mount", "/run/mount", false, true).
- Bind("/run/nginx", "/run/nginx", false, true).
- Bind("/run/nscd", "/run/nscd", false, true).
- Bind("/run/opengl-driver", "/run/opengl-driver", false, true).
- Bind("/run/pppd", "/run/pppd", false, true).
- Bind("/run/resolvconf", "/run/resolvconf", false, true).
- Bind("/run/sddm", "/run/sddm", false, true).
- Bind("/run/syncoid", "/run/syncoid", false, true).
- Bind("/run/systemd", "/run/systemd", false, true).
- Bind("/run/tmpfiles.d", "/run/tmpfiles.d", false, true).
- Bind("/run/udev", "/run/udev", false, true).
- Bind("/run/udisks2", "/run/udisks2", false, true).
- Bind("/run/utmp", "/run/utmp", false, true).
- Bind("/run/virtlogd.pid", "/run/virtlogd.pid", false, true).
- Bind("/run/wrappers", "/run/wrappers", false, true).
- Bind("/run/zed.pid", "/run/zed.pid", false, true).
- Bind("/run/zed.state", "/run/zed.state", false, true).
- Bind("/tmp/fortify.1971/tmpdir/150", "/tmp", false, true).
- Tmpfs("/tmp/fortify.1971", 1048576).
- Tmpfs("/run/user", 1048576).
- Tmpfs("/run/user/150", 8388608).
- Bind("/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd").
- Bind("/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group").
- Bind("/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd", "/etc/passwd").
- Bind("/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group", "/etc/group").
- Tmpfs("/var/run/nscd", 8192),
- want: []string{
- "--unshare-all", "--unshare-user", "--share-net",
- "--clearenv", "--die-with-parent", "--as-pid-1",
- "--uid", "65534",
- "--gid", "65534",
- "--setenv", "FORTIFY_INIT", "3",
- "--setenv", "HOME", "/home/chronos",
- "--setenv", "SHELL", "/run/current-system/sw/bin/zsh",
- "--setenv", "TERM", "xterm-256color",
- "--setenv", "USER", "chronos",
- "--setenv", "XDG_RUNTIME_DIR", "/run/user/150",
- "--setenv", "XDG_SESSION_CLASS", "user",
- "--setenv", "XDG_SESSION_TYPE", "tty",
- "--proc", "/proc", "--dev", "/dev",
- "--mqueue", "/dev/mqueue",
- "--bind", "/bin", "/bin",
- "--bind", "/boot", "/boot",
- "--bind", "/etc", "/etc",
- "--bind", "/home", "/home",
- "--bind", "/lib", "/lib",
- "--bind", "/lib64", "/lib64",
- "--bind", "/nix", "/nix",
- "--bind", "/root", "/root",
- "--bind", "/srv", "/srv",
- "--bind", "/sys", "/sys",
- "--bind", "/usr", "/usr",
- "--bind", "/var", "/var",
- "--bind", "/run/NetworkManager", "/run/NetworkManager",
- "--bind", "/run/agetty.reload", "/run/agetty.reload",
- "--bind", "/run/binfmt", "/run/binfmt",
- "--bind", "/run/booted-system", "/run/booted-system",
- "--bind", "/run/credentials", "/run/credentials",
- "--bind", "/run/cryptsetup", "/run/cryptsetup",
- "--bind", "/run/current-system", "/run/current-system",
- "--bind", "/run/host", "/run/host",
- "--bind", "/run/keys", "/run/keys",
- "--bind", "/run/libvirt", "/run/libvirt",
- "--bind", "/run/libvirtd.pid", "/run/libvirtd.pid",
- "--bind", "/run/lock", "/run/lock",
- "--bind", "/run/log", "/run/log",
- "--bind", "/run/lvm", "/run/lvm",
- "--bind", "/run/mount", "/run/mount",
- "--bind", "/run/nginx", "/run/nginx",
- "--bind", "/run/nscd", "/run/nscd",
- "--bind", "/run/opengl-driver", "/run/opengl-driver",
- "--bind", "/run/pppd", "/run/pppd",
- "--bind", "/run/resolvconf", "/run/resolvconf",
- "--bind", "/run/sddm", "/run/sddm",
- "--bind", "/run/syncoid", "/run/syncoid",
- "--bind", "/run/systemd", "/run/systemd",
- "--bind", "/run/tmpfiles.d", "/run/tmpfiles.d",
- "--bind", "/run/udev", "/run/udev",
- "--bind", "/run/udisks2", "/run/udisks2",
- "--bind", "/run/utmp", "/run/utmp",
- "--bind", "/run/virtlogd.pid", "/run/virtlogd.pid",
- "--bind", "/run/wrappers", "/run/wrappers",
- "--bind", "/run/zed.pid", "/run/zed.pid",
- "--bind", "/run/zed.state", "/run/zed.state",
- "--bind", "/tmp/fortify.1971/tmpdir/150", "/tmp",
- "--size", "1048576", "--tmpfs", "/tmp/fortify.1971",
- "--size", "1048576", "--tmpfs", "/run/user",
- "--size", "8388608", "--tmpfs", "/run/user/150",
- "--ro-bind", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd",
- "--ro-bind", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group",
- "--ro-bind", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/passwd", "/etc/passwd",
- "--ro-bind", "/tmp/fortify.1971/67a97cc824a64ef789f16b20ca6ce311/group", "/etc/group",
- "--size", "8192", "--tmpfs", "/var/run/nscd",
- },
- },
}
for _, tc := range testCases {
@@ -241,4 +235,21 @@ func TestConfig_Args(t *testing.T) {
}
})
}
+
+ // test persist validation
+ t.Run("invalid persist", func(t *testing.T) {
+ defer func() {
+ wantPanic := "persist called without required paths"
+ if r := recover(); r != wantPanic {
+ t.Errorf("Persist() panic = %v; wantPanic %v", r, wantPanic)
+ }
+ }()
+ (new(bwrap.Config)).Persist("/run", "", "")
+ })
+
+ t.Run("sync file", func(t *testing.T) {
+ if s := (new(bwrap.Config)).SetSync(os.Stdout).Sync(); s != os.Stdout {
+ t.Errorf("Sync() = %v", s)
+ }
+ })
}
diff --git a/helper/bwrap/sequential.go b/helper/bwrap/sequential.go
new file mode 100644
index 00000000..42a4b2b5
--- /dev/null
+++ b/helper/bwrap/sequential.go
@@ -0,0 +1,223 @@
+package bwrap
+
+import (
+ "encoding/gob"
+ "os"
+ "strconv"
+)
+
+func init() {
+ gob.Register(new(PermConfig[SymlinkConfig]))
+ gob.Register(new(PermConfig[*TmpfsConfig]))
+ gob.Register(new(OverlayConfig))
+}
+
+type PositionalArg int
+
+func (p PositionalArg) Unwrap() string {
+ return positionalArgs[p]
+}
+
+const (
+ Tmpfs PositionalArg = iota
+ Symlink
+
+ Bind
+ BindTry
+ DevBind
+ DevBindTry
+ ROBind
+ ROBindTry
+
+ Chmod
+ Dir
+ RemountRO
+ Procfs
+ DevTmpfs
+ Mqueue
+
+ Perms
+ Size
+
+ OverlaySrc
+ Overlay
+ TmpOverlay
+ ROOverlay
+)
+
+var positionalArgs = [...]string{
+ Tmpfs: "--tmpfs",
+ Symlink: "--symlink",
+
+ Bind: "--bind",
+ BindTry: "--bind-try",
+ DevBind: "--dev-bind",
+ DevBindTry: "--dev-bind-try",
+ ROBind: "--ro-bind",
+ ROBindTry: "--ro-bind-try",
+
+ Chmod: "--chmod",
+ Dir: "--dir",
+ RemountRO: "--remount-ro",
+ Procfs: "--proc",
+ DevTmpfs: "--dev",
+ Mqueue: "--mqueue",
+
+ Perms: "--perms",
+ Size: "--size",
+
+ OverlaySrc: "--overlay-src",
+ Overlay: "--overlay",
+ TmpOverlay: "--tmp-overlay",
+ ROOverlay: "--ro-overlay",
+}
+
+type PermConfig[T FSBuilder] struct {
+ // set permissions of next argument
+ // (--perms OCTAL)
+ Mode *os.FileMode `json:"mode,omitempty"`
+ // path to get the new permission
+ // (--bind-data, --file, etc.)
+ Inner T `json:"path"`
+}
+
+func (p *PermConfig[T]) Path() string {
+ return p.Inner.Path()
+}
+
+func (p *PermConfig[T]) Len() int {
+ if p.Mode != nil {
+ return p.Inner.Len() + 2
+ } else {
+ return p.Inner.Len()
+ }
+}
+
+func (p *PermConfig[T]) Append(args *[]string) {
+ if p.Mode != nil {
+ *args = append(*args, Perms.Unwrap(), strconv.FormatInt(int64(*p.Mode), 8))
+ }
+ p.Inner.Append(args)
+}
+
+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"`
+}
+
+func (t *TmpfsConfig) Path() string {
+ return t.Dir
+}
+
+func (t *TmpfsConfig) Len() int {
+ if t.Size > 0 {
+ return 4
+ } else {
+ return 2
+ }
+}
+
+func (t *TmpfsConfig) Append(args *[]string) {
+ if t.Size > 0 {
+ *args = append(*args, Size.Unwrap(), strconv.Itoa(t.Size))
+ }
+ *args = append(*args, Tmpfs.Unwrap(), t.Dir)
+}
+
+type OverlayConfig struct {
+ /*
+ read files from SRC in the following overlay
+ (--overlay-src SRC)
+ */
+ Src []string `json:"src,omitempty"`
+
+ /*
+ mount overlayfs on DEST, with RWSRC as the host path for writes and
+ WORKDIR an empty directory on the same filesystem as RWSRC
+ (--overlay RWSRC WORKDIR DEST)
+
+ if nil, mount overlayfs on DEST, with writes going to an invisible tmpfs
+ (--tmp-overlay DEST)
+
+ if either strings are empty, mount overlayfs read-only on DEST
+ (--ro-overlay DEST)
+ */
+ Persist *[2]string `json:"persist,omitempty"`
+
+ /*
+ --overlay RWSRC WORKDIR DEST
+
+ --tmp-overlay DEST
+
+ --ro-overlay DEST
+ */
+ Dest string `json:"dest"`
+}
+
+func (o *OverlayConfig) Path() string {
+ return o.Dest
+}
+
+func (o *OverlayConfig) Len() int {
+ // (--tmp-overlay DEST) or (--ro-overlay DEST)
+ p := 2
+ // (--overlay RWSRC WORKDIR DEST)
+ if o.Persist != nil && o.Persist[0] != "" && o.Persist[1] != "" {
+ p = 4
+ }
+
+ return p + len(o.Src)*2
+}
+
+func (o *OverlayConfig) Append(args *[]string) {
+ // --overlay-src SRC
+ for _, src := range o.Src {
+ *args = append(*args, OverlaySrc.Unwrap(), src)
+ }
+
+ if o.Persist != nil {
+ if o.Persist[0] != "" && o.Persist[1] != "" {
+ // --overlay RWSRC WORKDIR
+ *args = append(*args, Overlay.Unwrap(), o.Persist[0], o.Persist[1])
+ } else {
+ // --ro-overlay
+ *args = append(*args, ROOverlay.Unwrap())
+ }
+ } else {
+ // --tmp-overlay
+ *args = append(*args, TmpOverlay.Unwrap())
+ }
+
+ // DEST
+ *args = append(*args, o.Dest)
+}
+
+type SymlinkConfig [2]string
+
+func (s SymlinkConfig) Path() string {
+ return s[1]
+}
+
+func (s SymlinkConfig) Len() int {
+ return 3
+}
+
+func (s SymlinkConfig) Append(args *[]string) {
+ *args = append(*args, Symlink.Unwrap(), s[0], s[1])
+}
+
+type ChmodConfig map[string]os.FileMode
+
+func (c ChmodConfig) Len() int {
+ return len(c)
+}
+
+func (c ChmodConfig) Append(args *[]string) {
+ for path, mode := range c {
+ *args = append(*args, Chmod.Unwrap(), strconv.FormatInt(int64(mode), 8), path)
+ }
+}
diff --git a/helper/bwrap/static.go b/helper/bwrap/static.go
new file mode 100644
index 00000000..7a8535cc
--- /dev/null
+++ b/helper/bwrap/static.go
@@ -0,0 +1,249 @@
+package bwrap
+
+import (
+ "slices"
+ "strconv"
+)
+
+/*
+ static boolean args
+*/
+
+type BoolArg int
+
+func (b BoolArg) Unwrap() []string {
+ return boolArgs[b]
+}
+
+const (
+ UnshareAll BoolArg = iota
+ UnshareUser
+ UnshareIPC
+ UnsharePID
+ UnshareNet
+ UnshareUTS
+ UnshareCGroup
+ ShareNet
+
+ UserNS
+ Clearenv
+
+ NewSession
+ DieWithParent
+ AsInit
+)
+
+var boolArgs = [...][]string{
+ UnshareAll: {"--unshare-all", "--unshare-user"},
+ UnshareUser: {"--unshare-user"},
+ UnshareIPC: {"--unshare-ipc"},
+ UnsharePID: {"--unshare-pid"},
+ UnshareNet: {"--unshare-net"},
+ UnshareUTS: {"--unshare-uts"},
+ UnshareCGroup: {"--unshare-cgroup"},
+ ShareNet: {"--share-net"},
+
+ UserNS: {"--disable-userns", "--assert-userns-disabled"},
+ Clearenv: {"--clearenv"},
+
+ NewSession: {"--new-session"},
+ DieWithParent: {"--die-with-parent"},
+ AsInit: {"--as-pid-1"},
+}
+
+func (c *Config) boolArgs() Builder {
+ b := boolArg{
+ UserNS: !c.UserNS,
+ Clearenv: c.Clearenv,
+
+ NewSession: c.NewSession,
+ DieWithParent: c.DieWithParent,
+ AsInit: c.AsInit,
+ }
+
+ if c.Unshare == nil {
+ b[UnshareAll] = true
+ b[ShareNet] = c.Net
+ } else {
+ b[UnshareUser] = c.Unshare.User
+ b[UnshareIPC] = c.Unshare.IPC
+ b[UnsharePID] = c.Unshare.PID
+ b[UnshareNet] = c.Unshare.Net
+ b[UnshareUTS] = c.Unshare.UTS
+ b[UnshareCGroup] = c.Unshare.CGroup
+ }
+
+ return &b
+}
+
+type boolArg [len(boolArgs)]bool
+
+func (b *boolArg) Len() (l int) {
+ for i, v := range b {
+ if v {
+ l += len(boolArgs[i])
+ }
+ }
+ return
+}
+
+func (b *boolArg) Append(args *[]string) {
+ for i, v := range b {
+ if v {
+ *args = append(*args, BoolArg(i).Unwrap()...)
+ }
+ }
+}
+
+/*
+ static integer args
+*/
+
+type IntArg int
+
+func (i IntArg) Unwrap() string {
+ return intArgs[i]
+}
+
+const (
+ UID IntArg = iota
+ GID
+)
+
+var intArgs = [...]string{
+ UID: "--uid",
+ GID: "--gid",
+}
+
+func (c *Config) intArgs() Builder {
+ return &intArg{
+ UID: c.UID,
+ GID: c.GID,
+ }
+}
+
+type intArg [len(intArgs)]*int
+
+func (n *intArg) Len() (l int) {
+ for _, v := range n {
+ if v != nil {
+ l += 2
+ }
+ }
+ return
+}
+
+func (n *intArg) Append(args *[]string) {
+ for i, v := range n {
+ if v != nil {
+ *args = append(*args, IntArg(i).Unwrap(), strconv.Itoa(*v))
+ }
+ }
+}
+
+/*
+ static string args
+*/
+
+type StringArg int
+
+func (s StringArg) Unwrap() string {
+ return stringArgs[s]
+}
+
+const (
+ Hostname StringArg = iota
+ Chdir
+ UnsetEnv
+ LockFile
+)
+
+var stringArgs = [...]string{
+ Hostname: "--hostname",
+ Chdir: "--chdir",
+ UnsetEnv: "--unsetenv",
+ LockFile: "--lock-file",
+}
+
+func (c *Config) stringArgs() Builder {
+ n := stringArg{
+ UnsetEnv: c.UnsetEnv,
+ LockFile: c.LockFile,
+ }
+
+ if c.Hostname != "" {
+ n[Hostname] = []string{c.Hostname}
+ }
+ if c.Chdir != "" {
+ n[Chdir] = []string{c.Chdir}
+ }
+
+ return &n
+}
+
+type stringArg [len(stringArgs)][]string
+
+func (s *stringArg) Len() (l int) {
+ for _, arg := range s {
+ l += len(arg) * 2
+ }
+ return
+}
+
+func (s *stringArg) Append(args *[]string) {
+ for i, arg := range s {
+ for _, v := range arg {
+ *args = append(*args, StringArg(i).Unwrap(), v)
+ }
+ }
+}
+
+/*
+ static pair args
+*/
+
+type PairArg int
+
+func (p PairArg) Unwrap() string {
+ return pairArgs[p]
+}
+
+const (
+ SetEnv PairArg = iota
+)
+
+var pairArgs = [...]string{
+ SetEnv: "--setenv",
+}
+
+func (c *Config) pairArgs() Builder {
+ var n pairArg
+ n[SetEnv] = make([][2]string, len(c.SetEnv))
+ keys := make([]string, 0, len(c.SetEnv))
+ for k := range c.SetEnv {
+ keys = append(keys, k)
+ }
+ slices.Sort(keys)
+ for i, k := range keys {
+ n[SetEnv][i] = [2]string{k, c.SetEnv[k]}
+ }
+
+ return &n
+}
+
+type pairArg [len(pairArgs)][][2]string
+
+func (p *pairArg) Len() (l int) {
+ for _, v := range p {
+ l += len(v) * 3
+ }
+ return
+}
+
+func (p *pairArg) Append(args *[]string) {
+ for i, arg := range p {
+ for _, v := range arg {
+ *args = append(*args, PairArg(i).Unwrap(), v[0], v[1])
+ }
+ }
+}