aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-10-14 06:37:24 +0900
committerOphestra <cat@gensokyo.uk>2025-10-14 06:39:00 +0900
commit4c647add0db06cc7a28571aee7502737f0d66b27 (patch)
tree7446453d4380ed21c3ad3c3f8026c708fc53e729
parenta3414669428d4de37b68aff3f2db14e5768e1f59 (diff)
hst/container: pack boolean options
The memory saving is relatively insignificant, however this increases serialisation efficiency. Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/hakurei/command.go7
-rw-r--r--cmd/hakurei/print.go20
-rw-r--r--cmd/hakurei/print_test.go60
-rw-r--r--cmd/hpkg/app.go35
-rw-r--r--cmd/hpkg/with.go32
-rw-r--r--hst/container.go151
-rw-r--r--hst/container_test.go76
-rw-r--r--hst/hst.go17
-rw-r--r--hst/hst_test.go51
-rw-r--r--internal/app/app_test.go12
-rw-r--r--internal/app/outcome.go2
-rw-r--r--internal/app/spcontainer.go22
-rw-r--r--internal/app/spcontainer_test.go19
-rw-r--r--internal/app/spx11.go2
14 files changed, 362 insertions, 144 deletions
diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go
index 187ed5d3..0cf0ef1b 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 e013909d..db183d71 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 a0bb198c..7679c67b 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 d03c8b03..87a2c47c 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 5cb1533e..5d6eb87c 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 ca75c17b..69909389 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 00000000..049d3540
--- /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 11e91e7b..11b2b279 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 8bca8d6e..0dd74886 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 c89910a7..acecf94d 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 e294d948..f2c96e83 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 7475a1f1..a9fadeb0 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 6d5713cf..458588af 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 a597e344..109cf0c0 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()
}
}