helper/bwrap: implement overlayfs builder
All checks were successful
Tests / Go tests (push) Successful in 33s
Nix / NixOS tests (push) Successful in 4m5s

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-01-05 20:09:35 +09:00
parent 2e3f6a4c51
commit e2489059c1
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 124 additions and 0 deletions

View File

@ -4,10 +4,20 @@ 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",
}

View File

@ -78,6 +78,8 @@ type Config struct {
--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)
--bind-fd FD DEST Bind open directory or path fd on DEST
--ro-bind-fd FD DEST Bind open directory or path fd read-only on DEST
--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
@ -178,6 +180,74 @@ func (t *TmpfsConfig) Append(args *[]string) {
*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 {

View File

@ -96,6 +96,31 @@ func (c *Config) Tmpfs(dest string, size int, perm ...os.FileMode) *Config {
return c
}
// Overlay mount overlayfs on DEST, with writes going to an invisible tmpfs
// (--tmp-overlay DEST)
func (c *Config) Overlay(dest string, src ...string) *Config {
c.Filesystem = append(c.Filesystem, &OverlayConfig{Src: src, Dest: dest})
return c
}
// Join mount overlayfs read-only on DEST
// (--ro-overlay DEST)
func (c *Config) Join(dest string, src ...string) *Config {
c.Filesystem = append(c.Filesystem, &OverlayConfig{Src: src, Dest: dest, Persist: new([2]string)})
return c
}
// Persist 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)
func (c *Config) Persist(dest, rwsrc, workdir string, src ...string) *Config {
if rwsrc == "" || workdir == "" {
panic("persist called without required paths")
}
c.Filesystem = append(c.Filesystem, &OverlayConfig{Src: src, Dest: dest, Persist: &[2]string{rwsrc, workdir}})
return c
}
// Mqueue mount new mqueue in sandbox
// (--mqueue DEST)
func (c *Config) Mqueue(dest string) *Config {

View File

@ -13,6 +13,25 @@ func TestConfig_Args(t *testing.T) {
conf *bwrap.Config
want []string
}{
{
name: "overlayfs",
conf: (new(bwrap.Config)).
Overlay("/etc", "/etc").
Join("/.fortify/bin", "/bin", "/usr/bin", "/usr/local/bin").
Persist("/nix", "/data/data/org.chromium.Chromium/overlay/rwsrc", "/data/data/org.chromium.Chromium/workdir", "/data/app/org.chromium.Chromium/nix"),
want: []string{
"--unshare-all", "--unshare-user",
"--disable-userns", "--assert-userns-disabled",
// Overlay("/etc", "/etc")
"--overlay-src", "/etc", "--tmp-overlay", "/etc",
// Join("/.fortify/bin", "/bin", "/usr/bin", "/usr/local/bin")
"--overlay-src", "/bin", "--overlay-src", "/usr/bin",
"--overlay-src", "/usr/local/bin", "--ro-overlay", "/.fortify/bin",
// Persist("/nix", "/data/data/org.chromium.Chromium/overlay/rwsrc", "/data/data/org.chromium.Chromium/workdir", "/data/app/org.chromium.Chromium/nix")
"--overlay-src", "/data/app/org.chromium.Chromium/nix",
"--overlay", "/data/data/org.chromium.Chromium/overlay/rwsrc", "/data/data/org.chromium.Chromium/workdir", "/nix",
},
},
{
name: "xdg-dbus-proxy constraint sample",
conf: (&bwrap.Config{