From 4c647add0db06cc7a28571aee7502737f0d66b27 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Tue, 14 Oct 2025 06:37:24 +0900 Subject: [PATCH] hst/container: pack boolean options The memory saving is relatively insignificant, however this increases serialisation efficiency. Signed-off-by: Ophestra --- cmd/hakurei/command.go | 7 +- cmd/hakurei/print.go | 20 ++-- cmd/hakurei/print_test.go | 60 ++++++------ cmd/hpkg/app.go | 35 +++++-- cmd/hpkg/with.go | 32 +++++-- hst/container.go | 151 ++++++++++++++++++++++++++----- hst/container_test.go | 76 ++++++++++++++++ hst/hst.go | 17 ++-- hst/hst_test.go | 51 +++++++---- internal/app/app_test.go | 12 ++- internal/app/outcome.go | 2 +- internal/app/spcontainer.go | 22 ++--- internal/app/spcontainer_test.go | 19 ++-- internal/app/spx11.go | 2 +- 14 files changed, 362 insertions(+), 144 deletions(-) create mode 100644 hst/container_test.go diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index 187ed5d..0cf0ef1 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -147,11 +147,6 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr Enablements: hst.NewEnablements(et), Container: &hst.ContainerConfig{ - Userns: true, - HostNet: true, - Tty: true, - HostAbstract: true, - Filesystem: []hst.FilesystemConfigJSON{ // autoroot, includes the home directory {FilesystemConfig: &hst.FSBind{ @@ -167,6 +162,8 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr Path: progPath, Args: args, + + Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty, }, } diff --git a/cmd/hakurei/print.go b/cmd/hakurei/print.go index e013909..db183d7 100644 --- a/cmd/hakurei/print.go +++ b/cmd/hakurei/print.go @@ -87,19 +87,19 @@ func printShowInstance( t.Printf(" Hostname:\t%s\n", params.Hostname) } flags := make([]string, 0, 7) - writeFlag := func(name string, value bool) { - if value { + writeFlag := func(name string, flag uintptr, force bool) { + if params.Flags&flag != 0 || force { flags = append(flags, name) } } - writeFlag("userns", params.Userns) - writeFlag("devel", params.Devel) - writeFlag("net", params.HostNet) - writeFlag("abstract", params.HostAbstract) - writeFlag("device", params.Device) - writeFlag("tty", params.Tty) - writeFlag("mapuid", params.MapRealUID) - writeFlag("directwl", config.DirectWayland) + writeFlag("userns", hst.FUserns, false) + writeFlag("devel", hst.FDevel, false) + writeFlag("net", hst.FHostNet, false) + writeFlag("abstract", hst.FHostAbstract, false) + writeFlag("device", hst.FDevice, false) + writeFlag("tty", hst.FTty, false) + writeFlag("mapuid", hst.FMapRealUID, false) + writeFlag("directwl", 0, config.DirectWayland) if len(flags) == 0 { flags = append(flags, "none") } diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index a0bb198..7679c67 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -252,20 +252,11 @@ App "container": { "hostname": "localhost", "wait_delay": -1, - "seccomp_compat": true, - "devel": true, - "userns": true, - "host_net": true, - "host_abstract": true, - "tty": true, - "multiarch": true, "env": { "GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", "GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT" }, - "map_real_uid": true, - "device": true, "filesystem": [ { "type": "bind", @@ -331,7 +322,16 @@ App "--disable-smooth-scrolling", "--enable-features=UseOzonePlatform", "--ozone-platform=wayland" - ] + ], + "seccomp_compat": true, + "devel": true, + "userns": true, + "host_net": true, + "host_abstract": true, + "tty": true, + "multiarch": true, + "map_real_uid": true, + "device": true } }, "time": "1970-01-01T00:00:00.000000009Z" @@ -402,20 +402,11 @@ App "container": { "hostname": "localhost", "wait_delay": -1, - "seccomp_compat": true, - "devel": true, - "userns": true, - "host_net": true, - "host_abstract": true, - "tty": true, - "multiarch": true, "env": { "GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", "GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT" }, - "map_real_uid": true, - "device": true, "filesystem": [ { "type": "bind", @@ -481,7 +472,16 @@ App "--disable-smooth-scrolling", "--enable-features=UseOzonePlatform", "--ozone-platform=wayland" - ] + ], + "seccomp_compat": true, + "devel": true, + "userns": true, + "host_net": true, + "host_abstract": true, + "tty": true, + "multiarch": true, + "map_real_uid": true, + "device": true } } `, true}, @@ -612,20 +612,11 @@ func TestPrintPs(t *testing.T) { "container": { "hostname": "localhost", "wait_delay": -1, - "seccomp_compat": true, - "devel": true, - "userns": true, - "host_net": true, - "host_abstract": true, - "tty": true, - "multiarch": true, "env": { "GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", "GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT" }, - "map_real_uid": true, - "device": true, "filesystem": [ { "type": "bind", @@ -691,7 +682,16 @@ func TestPrintPs(t *testing.T) { "--disable-smooth-scrolling", "--enable-features=UseOzonePlatform", "--ozone-platform=wayland" - ] + ], + "seccomp_compat": true, + "devel": true, + "userns": true, + "host_net": true, + "host_abstract": true, + "tty": true, + "multiarch": true, + "map_real_uid": true, + "device": true } }, "time": "1970-01-01T00:00:00.000000009Z" diff --git a/cmd/hpkg/app.go b/cmd/hpkg/app.go index d03c8b0..87a2c47 100644 --- a/cmd/hpkg/app.go +++ b/cmd/hpkg/app.go @@ -76,15 +76,7 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *check.Absolute, argv [] Groups: app.Groups, Container: &hst.ContainerConfig{ - Hostname: formatHostname(app.Name), - Devel: app.Devel, - Userns: app.Userns, - HostNet: app.HostNet, - HostAbstract: app.HostAbstract, - Device: app.Device, - Tty: app.Tty || flagDropShell, - MapRealUID: app.MapRealUID, - Multiarch: app.Multiarch, + Hostname: formatHostname(app.Name), Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{Target: fhs.AbsEtc, Source: pathSet.cacheDir.Append("etc"), Special: true}}, {FilesystemConfig: &hst.FSBind{Source: pathSet.nixPath.Append("store"), Target: pathNixStore}}, @@ -113,6 +105,31 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *check.Absolute, argv [] {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, }, } + + if app.Devel { + config.Container.Flags |= hst.FDevel + } + if app.Userns { + config.Container.Flags |= hst.FUserns + } + if app.HostNet { + config.Container.Flags |= hst.FHostNet + } + if app.HostAbstract { + config.Container.Flags |= hst.FHostAbstract + } + if app.Device { + config.Container.Flags |= hst.FDevice + } + if app.Tty || flagDropShell { + config.Container.Flags |= hst.FTty + } + if app.MapRealUID { + config.Container.Flags |= hst.FMapRealUID + } + if app.Multiarch { + config.Container.Flags |= hst.FMultiarch + } return config } diff --git a/cmd/hpkg/with.go b/cmd/hpkg/with.go index 5cb1533..5d6eb87 100644 --- a/cmd/hpkg/with.go +++ b/cmd/hpkg/with.go @@ -17,6 +17,14 @@ func withNixDaemon( action string, command []string, net bool, updateConfig func(config *hst.Config) *hst.Config, app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), ) { + flags := hst.FMultiarch | hst.FUserns // nix sandbox requires userns + if net { + flags |= hst.FHostNet + } + if dropShell { + flags |= hst.FTty + } + mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{ ID: app.ID, @@ -28,11 +36,8 @@ func withNixDaemon( Identity: app.Identity, Container: &hst.ContainerConfig{ - Hostname: formatHostname(app.Name) + "-" + action, - Userns: true, // nix sandbox requires userns - HostNet: net, - Multiarch: true, - Tty: dropShell, + Hostname: formatHostname(app.Name) + "-" + action, + Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{Target: fhs.AbsEtc, Source: pathSet.cacheDir.Append("etc"), Special: true}}, {FilesystemConfig: &hst.FSBind{Source: pathSet.nixPath, Target: pathNix, Write: true}}, @@ -58,6 +63,8 @@ func withNixDaemon( // terminate nix-daemon " && pkill nix-daemon", }, + + Flags: flags, }, }), dropShell, beforeFail) } @@ -66,7 +73,13 @@ func withCacheDir( ctx context.Context, msg message.Msg, action string, command []string, workDir *check.Absolute, - app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { + app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), +) { + flags := hst.FMultiarch + if dropShell { + flags |= hst.FTty + } + mustRunAppDropShell(ctx, msg, &hst.Config{ ID: app.ID, @@ -79,9 +92,8 @@ func withCacheDir( Identity: app.Identity, Container: &hst.ContainerConfig{ - Hostname: formatHostname(app.Name) + "-" + action, - Multiarch: true, - Tty: dropShell, + Hostname: formatHostname(app.Name) + "-" + action, + Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{Target: fhs.AbsEtc, Source: workDir.Append(fhs.Etc), Special: true}}, {FilesystemConfig: &hst.FSBind{Source: workDir.Append("nix"), Target: pathNix}}, @@ -98,6 +110,8 @@ func withCacheDir( Path: pathShell, Args: []string{bash, "-lc", strings.Join(command, " && ")}, + + Flags: flags, }, }, dropShell, beforeFail) } diff --git a/hst/container.go b/hst/container.go index ca75c17..6990938 100644 --- a/hst/container.go +++ b/hst/container.go @@ -1,6 +1,8 @@ package hst import ( + "encoding/json" + "syscall" "time" "hakurei.app/container/check" @@ -29,6 +31,37 @@ const ( ShimExitOrphan = 3 ) +const ( + // FMultiarch unblocks syscalls required for multiarch to work on applicable targets. + FMultiarch uintptr = 1 << iota + + // FSeccompCompat causes emitted seccomp filter programs to be identical to Flatpak. + FSeccompCompat + // FDevel unblocks ptrace and friends. + FDevel + // FUserns unblocks userns creation and container setup syscalls. + FUserns + // FHostNet skips net namespace creation. + FHostNet + // FHostAbstract skips setting up abstract unix socket scope. + FHostAbstract + // FTty unblocks dangerous terminal I/O (faking input). + FTty + + // FMapRealUID maps the target user uid to the privileged user uid in the container user namespace. + // Some programs fail to connect to dbus session running as a different uid, + // this option works around it by mapping priv-side caller uid in container. + FMapRealUID + + // FDevice mount /dev/ from the init mount namespace as-is in the container mount namespace. + FDevice + + fMax + + // FAll is [ContainerConfig.Flags] with all currently defined bits set. + FAll = fMax - 1 +) + // ContainerConfig describes the container configuration to be applied to an underlying [container]. type ContainerConfig struct { // Container UTS namespace hostname. @@ -39,33 +72,9 @@ type ContainerConfig struct { // Values lesser than zero is equivalent to zero, bypassing [WaitDelayDefault]. WaitDelay time.Duration `json:"wait_delay,omitempty"` - // Emit Flatpak-compatible seccomp filter programs. - SeccompCompat bool `json:"seccomp_compat,omitempty"` - // Allow ptrace and friends. - Devel bool `json:"devel,omitempty"` - // Allow userns creation and container setup syscalls. - Userns bool `json:"userns,omitempty"` - // Share host net namespace. - HostNet bool `json:"host_net,omitempty"` - // Share abstract unix socket scope. - HostAbstract bool `json:"host_abstract,omitempty"` - // Allow dangerous terminal I/O (faking input). - Tty bool `json:"tty,omitempty"` - // Allow multiarch. - Multiarch bool `json:"multiarch,omitempty"` - // Initial process environment variables. Env map[string]string `json:"env"` - /* Map target user uid to privileged user uid in the container user namespace. - - Some programs fail to connect to dbus session running as a different uid, - this option works around it by mapping priv-side caller uid in container. */ - MapRealUID bool `json:"map_real_uid"` - - // Mount /dev/ from the init mount namespace as-is in the container mount namespace. - Device bool `json:"device,omitempty"` - /* Container mount points. If the first element targets /, it is inserted early and excluded from path hiding. */ @@ -83,4 +92,98 @@ type ContainerConfig struct { Path *check.Absolute `json:"path,omitempty"` // Final args passed to the initial program. Args []string `json:"args"` + + // Flags holds boolean options of [ContainerConfig]. + Flags uintptr `json:"-"` +} + +// ContainerConfigF is [ContainerConfig] stripped of its methods. +// The [ContainerConfig.Flags] field does not survive a [json] round trip. +type ContainerConfigF ContainerConfig + +// containerConfigJSON is the [json] representation of [ContainerConfig]. +type containerConfigJSON = struct { + *ContainerConfigF + + // Corresponds to [FSeccompCompat]. + SeccompCompat bool `json:"seccomp_compat,omitempty"` + // Corresponds to [FDevel]. + Devel bool `json:"devel,omitempty"` + // Corresponds to [FUserns]. + Userns bool `json:"userns,omitempty"` + // Corresponds to [FHostNet]. + HostNet bool `json:"host_net,omitempty"` + // Corresponds to [FHostAbstract]. + HostAbstract bool `json:"host_abstract,omitempty"` + // Corresponds to [FTty]. + Tty bool `json:"tty,omitempty"` + + // Corresponds to [FMultiarch]. + Multiarch bool `json:"multiarch,omitempty"` + + // Corresponds to [FMapRealUID]. + MapRealUID bool `json:"map_real_uid"` + + // Corresponds to [FDevice]. + Device bool `json:"device,omitempty"` +} + +func (c *ContainerConfig) MarshalJSON() ([]byte, error) { + if c == nil { + return nil, syscall.EINVAL + } + return json.Marshal(&containerConfigJSON{ + ContainerConfigF: (*ContainerConfigF)(c), + + SeccompCompat: c.Flags&FSeccompCompat != 0, + Devel: c.Flags&FDevel != 0, + Userns: c.Flags&FUserns != 0, + HostNet: c.Flags&FHostNet != 0, + HostAbstract: c.Flags&FHostAbstract != 0, + Tty: c.Flags&FTty != 0, + Multiarch: c.Flags&FMultiarch != 0, + MapRealUID: c.Flags&FMapRealUID != 0, + Device: c.Flags&FDevice != 0, + }) +} + +func (c *ContainerConfig) UnmarshalJSON(data []byte) error { + if c == nil { + return syscall.EINVAL + } + + v := new(containerConfigJSON) + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + *c = *(*ContainerConfig)(v.ContainerConfigF) + if v.SeccompCompat { + c.Flags |= FSeccompCompat + } + if v.Devel { + c.Flags |= FDevel + } + if v.Userns { + c.Flags |= FUserns + } + if v.HostNet { + c.Flags |= FHostNet + } + if v.HostAbstract { + c.Flags |= FHostAbstract + } + if v.Tty { + c.Flags |= FTty + } + if v.Multiarch { + c.Flags |= FMultiarch + } + if v.MapRealUID { + c.Flags |= FMapRealUID + } + if v.Device { + c.Flags |= FDevice + } + return nil } diff --git a/hst/container_test.go b/hst/container_test.go new file mode 100644 index 0000000..049d354 --- /dev/null +++ b/hst/container_test.go @@ -0,0 +1,76 @@ +package hst_test + +import ( + "encoding/json" + "errors" + "reflect" + "syscall" + "testing" + + "hakurei.app/hst" +) + +func TestContainerConfig(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + c *hst.ContainerConfig + data string + }{ + {"nil", nil, "null"}, + {"zero", new(hst.ContainerConfig), + `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"map_real_uid":false}`}, + {"seccomp compat", &hst.ContainerConfig{Flags: hst.FSeccompCompat}, + `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"map_real_uid":false}`}, + {"hostnet hostabstract", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract}, + `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":false}`}, + {"hostnet hostabstract mapuid", &hst.ContainerConfig{Flags: hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID}, + `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"host_net":true,"host_abstract":true,"map_real_uid":true}`}, + {"all", &hst.ContainerConfig{Flags: hst.FAll}, + `{"env":null,"filesystem":null,"shell":null,"home":null,"args":null,"seccomp_compat":true,"devel":true,"userns":true,"host_net":true,"host_abstract":true,"tty":true,"multiarch":true,"map_real_uid":true,"device":true}`}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + t.Run("marshal", func(t *testing.T) { + t.Parallel() + if got, err := json.Marshal(tc.c); err != nil { + t.Fatalf("Marshal: error = %v", err) + } else if string(got) != tc.data { + t.Errorf("Marshal:\n%s, want\n%s", string(got), tc.data) + } + }) + + t.Run("unmarshal", func(t *testing.T) { + t.Parallel() + + { + got := new(hst.ContainerConfig) + if err := json.Unmarshal([]byte(tc.data), &got); err != nil { + t.Fatalf("Unmarshal: error = %v", err) + } + if !reflect.DeepEqual(got, tc.c) { + t.Errorf("Unmarshal: %v, want %v", got, tc.c) + } + } + }) + }) + } + + t.Run("passthrough", func(t *testing.T) { + t.Parallel() + + if _, err := (*hst.ContainerConfig)(nil).MarshalJSON(); !errors.Is(err, syscall.EINVAL) { + t.Errorf("MarshalJSON: error = %v", err) + } + if err := (*hst.ContainerConfig)(nil).UnmarshalJSON(nil); !errors.Is(err, syscall.EINVAL) { + t.Errorf("UnmarshalJSON: error = %v", err) + } + if err := new(hst.ContainerConfig).UnmarshalJSON([]byte{}); err == nil { + t.Errorf("UnmarshalJSON: error = %v", err) + } + }) +} diff --git a/hst/hst.go b/hst/hst.go index 11e91e7..11b2b27 100644 --- a/hst/hst.go +++ b/hst/hst.go @@ -3,6 +3,7 @@ package hst import ( "errors" + "math" "net" "os" @@ -96,17 +97,8 @@ func Template() *Config { Groups: []string{"video", "dialout", "plugdev"}, Container: &ContainerConfig{ - Hostname: "localhost", - Devel: true, - Userns: true, - HostNet: true, - HostAbstract: true, - Device: true, - WaitDelay: -1, - SeccompCompat: true, - Tty: true, - Multiarch: true, - MapRealUID: true, + Hostname: "localhost", + WaitDelay: -1, // example API credentials pulled from Google Chrome // DO NOT USE THESE IN A REAL BROWSER Env: map[string]string{ @@ -143,6 +135,9 @@ func Template() *Config { "--enable-features=UseOzonePlatform", "--ozone-platform=wayland", }, + + // Set all bits here so new flags trip the template test. + Flags: math.MaxUint, }, } } diff --git a/hst/hst_test.go b/hst/hst_test.go index 8bca8d6..0dd7488 100644 --- a/hst/hst_test.go +++ b/hst/hst_test.go @@ -5,6 +5,7 @@ import ( "errors" "net" "os" + "reflect" "syscall" "testing" @@ -164,20 +165,11 @@ func TestTemplate(t *testing.T) { "container": { "hostname": "localhost", "wait_delay": -1, - "seccomp_compat": true, - "devel": true, - "userns": true, - "host_net": true, - "host_abstract": true, - "tty": true, - "multiarch": true, "env": { "GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", "GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT" }, - "map_real_uid": true, - "device": true, "filesystem": [ { "type": "bind", @@ -243,14 +235,41 @@ func TestTemplate(t *testing.T) { "--disable-smooth-scrolling", "--enable-features=UseOzonePlatform", "--ozone-platform=wayland" - ] + ], + "seccomp_compat": true, + "devel": true, + "userns": true, + "host_net": true, + "host_abstract": true, + "tty": true, + "multiarch": true, + "map_real_uid": true, + "device": true } }` - if p, err := json.MarshalIndent(hst.Template(), "", "\t"); err != nil { - t.Fatalf("cannot marshal: %v", err) - } else if s := string(p); s != want { - t.Fatalf("Template:\n%s\nwant:\n%s", - s, want) - } + t.Run("marshal", func(t *testing.T) { + t.Parallel() + if p, err := json.MarshalIndent(hst.Template(), "", "\t"); err != nil { + t.Fatalf("cannot marshal: %v", err) + } else if s := string(p); s != want { + t.Fatalf("Template:\n%s\nwant:\n%s", + s, want) + } + }) + + t.Run("unmarshal", func(t *testing.T) { + t.Parallel() + + var got *hst.Config + if err := json.Unmarshal([]byte(want), &got); err != nil { + t.Fatalf("Unmarshal: error = %v", err) + } + + wantVal := hst.Template() + wantVal.Container.Flags = hst.FAll + if !reflect.DeepEqual(got, wantVal) { + t.Fatalf("Unmarshal: %#v, want %#v", got, wantVal) + } + }) } diff --git a/internal/app/app_test.go b/internal/app/app_test.go index c89910a..acecf94 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -44,8 +44,6 @@ func TestApp(t *testing.T) { { "nixos permissive defaults no enablements", new(stubNixOS), &hst.Config{Container: &hst.ContainerConfig{ - Userns: true, HostNet: true, HostAbstract: true, Tty: true, - Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{ Target: fhs.AbsRoot, @@ -71,6 +69,8 @@ func TestApp(t *testing.T) { Path: m("/run/current-system/sw/bin/zsh"), Args: []string{"/run/current-system/sw/bin/zsh"}, + + Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty, }}, state.ID{ 0x4a, 0x45, 0x0b, 0x65, @@ -162,8 +162,6 @@ func TestApp(t *testing.T) { Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse), Container: &hst.ContainerConfig{ - Userns: true, HostNet: true, HostAbstract: true, Tty: true, - Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{ Target: fhs.AbsRoot, @@ -194,6 +192,8 @@ func TestApp(t *testing.T) { Path: m("/run/current-system/sw/bin/zsh"), Args: []string{"zsh", "-c", "exec chromium "}, + + Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty, }, }, state.ID{ @@ -308,7 +308,7 @@ func TestApp(t *testing.T) { ID: "org.chromium.Chromium", Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse), Container: &hst.ContainerConfig{ - Userns: true, HostNet: true, MapRealUID: true, Env: nil, + Env: nil, Filesystem: []hst.FilesystemConfigJSON{ f(&hst.FSBind{Source: m("/bin")}), f(&hst.FSBind{Source: m("/usr/bin/")}), @@ -330,6 +330,8 @@ func TestApp(t *testing.T) { Home: m("/var/lib/persist/module/hakurei/0/1"), Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), + + Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID, }, SystemBus: &hst.BusConfig{ Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, diff --git a/internal/app/outcome.go b/internal/app/outcome.go index e294d94..f2c96e8 100644 --- a/internal/app/outcome.go +++ b/internal/app/outcome.go @@ -94,7 +94,7 @@ func newOutcomeState(k syscallDispatcher, msg message.Msg, id *state.ID, config s.Shim.WaitDelay = s.Container.WaitDelay } - if s.Container.MapRealUID { + if s.Container.Flags&hst.FMapRealUID != 0 { s.Mapuid, s.Mapgid = k.getuid(), k.getgid() } else { s.Mapuid, s.Mapgid = k.overflowUid(msg), k.overflowGid(msg) diff --git a/internal/app/spcontainer.go b/internal/app/spcontainer.go index 7475a1f..a9fadeb 100644 --- a/internal/app/spcontainer.go +++ b/internal/app/spcontainer.go @@ -48,9 +48,9 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { const preallocateOpsCount = 1 << 5 state.params.Hostname = state.Container.Hostname - state.params.RetainSession = state.Container.Tty - state.params.HostNet = state.Container.HostNet - state.params.HostAbstract = state.Container.HostAbstract + state.params.RetainSession = state.Container.Flags&hst.FTty != 0 + state.params.HostNet = state.Container.Flags&hst.FHostNet != 0 + state.params.HostAbstract = state.Container.Flags&hst.FHostAbstract != 0 if state.Container.Path == nil { return newWithMessage("invalid program path") @@ -67,24 +67,24 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { // this behaviour is implemented in the shim state.params.ForwardCancel = state.Shim.WaitDelay > 0 - if state.Container.Multiarch { + if state.Container.Flags&hst.FMultiarch != 0 { state.params.SeccompFlags |= seccomp.AllowMultiarch } - if !state.Container.SeccompCompat { + if state.Container.Flags&hst.FSeccompCompat == 0 { state.params.SeccompPresets |= bits.PresetExt } - if !state.Container.Devel { + if state.Container.Flags&hst.FDevel == 0 { state.params.SeccompPresets |= bits.PresetDenyDevel } - if !state.Container.Userns { + if state.Container.Flags&hst.FUserns == 0 { state.params.SeccompPresets |= bits.PresetDenyNS } - if !state.Container.Tty { + if state.Container.Flags&hst.FTty == 0 { state.params.SeccompPresets |= bits.PresetDenyTTY } - if state.Container.MapRealUID { + if state.Container.Flags&hst.FMapRealUID != 0 { state.params.Uid = state.Mapuid state.params.Gid = state.Mapgid } @@ -106,7 +106,7 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { state.params. Proc(fhs.AbsProc). Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755) - if !state.Container.Device { + if state.Container.Flags&hst.FDevice == 0 { state.params.DevWritable(fhs.AbsDev, true) } else { state.params.Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice) @@ -275,7 +275,7 @@ func (s *spFilesystemOp) toContainer(state *outcomeStateParams) error { } // no more configured paths beyond this point - if !state.Container.Device { + if state.Container.Flags&hst.FDevice == 0 { state.params.Remount(fhs.AbsDev, syscall.MS_RDONLY) } return nil diff --git a/internal/app/spcontainer_test.go b/internal/app/spcontainer_test.go index 6d5713c..458588a 100644 --- a/internal/app/spcontainer_test.go +++ b/internal/app/spcontainer_test.go @@ -51,12 +51,7 @@ func TestSpParamsOp(t *testing.T) { }, func() *hst.Config { c := hst.Template() c.Container.Args = nil - c.Container.Multiarch = false - c.Container.SeccompCompat = false - c.Container.Devel = false - c.Container.Userns = false - c.Container.Tty = false - c.Container.Device = false + c.Container.Flags = hst.FHostNet | hst.FHostAbstract | hst.FMapRealUID return c }, nil, []stub.Call{ call("lookupEnv", stub.ExpectArgs{"TERM"}, "xterm", nil), @@ -65,8 +60,8 @@ func TestSpParamsOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Hostname: config.Container.Hostname, - HostNet: config.Container.HostNet, - HostAbstract: config.Container.HostAbstract, + HostNet: true, + HostAbstract: true, Path: config.Container.Path, Args: []string{config.Container.Path.String()}, SeccompPresets: bits.PresetExt | bits.PresetDenyDevel | bits.PresetDenyNS | bits.PresetDenyTTY, @@ -109,9 +104,9 @@ func TestSpParamsOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Hostname: config.Container.Hostname, - RetainSession: config.Container.Tty, - HostNet: config.Container.HostNet, - HostAbstract: config.Container.HostAbstract, + RetainSession: true, + HostNet: true, + HostAbstract: true, Path: config.Container.Path, Args: config.Container.Args, SeccompFlags: seccomp.AllowMultiarch, @@ -159,7 +154,7 @@ func TestSpFilesystemOp(t *testing.T) { }}}, {FilesystemConfig: &hst.FSEphemeral{Target: hst.AbsPrivateTmp}}, } - c.Container.Device = false + c.Container.Flags &= ^hst.FDevice return c } configSmall := newConfigSmall() diff --git a/internal/app/spx11.go b/internal/app/spx11.go index a597e34..109cf0c 100644 --- a/internal/app/spx11.go +++ b/internal/app/spx11.go @@ -54,7 +54,7 @@ func (s *spX11Op) toSystem(state *outcomeStateSys) error { } } else { state.sys.UpdatePermType(hst.EX11, socketPath, acl.Read, acl.Write, acl.Execute) - if !state.Container.HostAbstract { + if state.Container.Flags&hst.FHostAbstract == 0 { s.Display = "unix:" + socketPath.String() } }