aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-03-25 01:52:49 +0900
committerOphestra <cat@gensokyo.uk>2025-03-25 01:52:49 +0900
commit5c4058d5ac7e7944973ca8216c258fc50c194e22 (patch)
treec06597903233c75eca5a1edae8b06d93f0f81774
parente732dca7629edb503df0c65df5f37a99600d567d (diff)
app: run in native sandbox
Signed-off-by: Ophestra <cat@gensokyo.uk>
-rw-r--r--cmd/fpkg/app.go (renamed from cmd/fpkg/bundle.go)77
-rw-r--r--cmd/fpkg/main.go57
-rw-r--r--cmd/fpkg/proc.go4
-rw-r--r--cmd/fpkg/test/test.py4
-rw-r--r--cmd/fpkg/with.go34
-rw-r--r--fst/app.go3
-rw-r--r--fst/config.go51
-rw-r--r--fst/sandbox.go263
-rw-r--r--internal/app/app.go11
-rw-r--r--internal/app/app_nixos_test.go267
-rw-r--r--internal/app/app_pd_test.go516
-rw-r--r--internal/app/app_stub_test.go6
-rw-r--r--internal/app/app_test.go28
-rw-r--r--internal/app/export_test.go4
-rw-r--r--internal/app/init0/early.go18
-rw-r--r--internal/app/init0/main.go165
-rw-r--r--internal/app/init0/payload.go13
-rw-r--r--internal/app/process.go50
-rw-r--r--internal/app/seal.go181
-rw-r--r--internal/app/shim/main.go129
-rw-r--r--internal/app/shim/payload.go23
-rw-r--r--internal/app/shim/proc.go (renamed from internal/app/shim/manager.go)35
-rw-r--r--internal/state/state_test.go2
-rw-r--r--main.go20
-rw-r--r--nixos.nix21
-rw-r--r--options.nix16
-rw-r--r--print.go12
-rw-r--r--print_test.go70
-rw-r--r--sandbox/seccomp/export_test.go2
-rw-r--r--sandbox/sequential.go10
-rw-r--r--sandbox/syscall.go8
-rw-r--r--test/sandbox/case/mapuid.nix23
-rw-r--r--test/sandbox/case/preset.nix19
-rw-r--r--test/sandbox/case/tty.nix17
-rw-r--r--test/test.py9
35 files changed, 946 insertions, 1222 deletions
diff --git a/cmd/fpkg/bundle.go b/cmd/fpkg/app.go
index d1641168..a1e5de91 100644
--- a/cmd/fpkg/bundle.go
+++ b/cmd/fpkg/app.go
@@ -4,12 +4,15 @@ import (
"encoding/json"
"log"
"os"
+ "path"
"git.gensokyo.uk/security/fortify/dbus"
+ "git.gensokyo.uk/security/fortify/fst"
+ "git.gensokyo.uk/security/fortify/sandbox/seccomp"
"git.gensokyo.uk/security/fortify/system"
)
-type bundleInfo struct {
+type appInfo struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -20,13 +23,15 @@ type bundleInfo struct {
// passed through to [fst.Config]
Groups []string `json:"groups,omitempty"`
// passed through to [fst.Config]
- UserNS bool `json:"userns,omitempty"`
+ Devel bool `json:"devel,omitempty"`
+ // passed through to [fst.Config]
+ Userns bool `json:"userns,omitempty"`
// passed through to [fst.Config]
Net bool `json:"net,omitempty"`
// passed through to [fst.Config]
Dev bool `json:"dev,omitempty"`
// passed through to [fst.Config]
- NoNewSession bool `json:"no_new_session,omitempty"`
+ Tty bool `json:"tty,omitempty"`
// passed through to [fst.Config]
MapRealUID bool `json:"map_real_uid,omitempty"`
// passed through to [fst.Config]
@@ -38,11 +43,9 @@ type bundleInfo struct {
// passed through to [fst.Config]
Enablements system.Enablements `json:"enablements"`
- // passed through inverted to [bwrap.SyscallPolicy]
- Devel bool `json:"devel,omitempty"`
- // passed through to [bwrap.SyscallPolicy]
+ // passed through to [fst.Config]
Multiarch bool `json:"multiarch,omitempty"`
- // passed through to [bwrap.SyscallPolicy]
+ // passed through to [fst.Config]
Bluetooth bool `json:"bluetooth,omitempty"`
// allow gpu access within sandbox
@@ -59,8 +62,64 @@ type bundleInfo struct {
ActivationPackage string `json:"activation_package"`
}
-func loadBundleInfo(name string, beforeFail func()) *bundleInfo {
- bundle := new(bundleInfo)
+func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool) *fst.Config {
+ config := &fst.Config{
+ ID: app.ID,
+ Path: argv[0],
+ Args: argv,
+ Confinement: fst.ConfinementConfig{
+ AppID: app.AppID,
+ Groups: app.Groups,
+ Username: "fortify",
+ Inner: path.Join("/data/data", app.ID),
+ Outer: pathSet.homeDir,
+ Sandbox: &fst.SandboxConfig{
+ Hostname: formatHostname(app.Name),
+ Devel: app.Devel,
+ Userns: app.Userns,
+ Net: app.Net,
+ Dev: app.Dev,
+ Tty: app.Tty || flagDropShell,
+ MapRealUID: app.MapRealUID,
+ DirectWayland: app.DirectWayland,
+ Filesystem: []*fst.FilesystemConfig{
+ {Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true},
+ {Src: pathSet.metaPath, Dst: path.Join(fst.Tmp, "app"), Must: true},
+ {Src: "/etc/resolv.conf"},
+ {Src: "/sys/block"},
+ {Src: "/sys/bus"},
+ {Src: "/sys/class"},
+ {Src: "/sys/dev"},
+ {Src: "/sys/devices"},
+ },
+ Link: [][2]string{
+ {app.CurrentSystem, "/run/current-system"},
+ {"/run/current-system/sw/bin", "/bin"},
+ {"/run/current-system/sw/bin", "/usr/bin"},
+ },
+ Etc: path.Join(pathSet.cacheDir, "etc"),
+ AutoEtc: true,
+ },
+ ExtraPerms: []*fst.ExtraPermConfig{
+ {Path: dataHome, Execute: true},
+ {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true},
+ },
+ SystemBus: app.SystemBus,
+ SessionBus: app.SessionBus,
+ Enablements: app.Enablements,
+ },
+ }
+ if app.Multiarch {
+ config.Confinement.Sandbox.Seccomp |= seccomp.FlagMultiarch
+ }
+ if app.Bluetooth {
+ config.Confinement.Sandbox.Seccomp |= seccomp.FlagBluetooth
+ }
+ return config
+}
+
+func loadAppInfo(name string, beforeFail func()) *appInfo {
+ bundle := new(appInfo)
if f, err := os.Open(name); err != nil {
beforeFail()
log.Fatalf("cannot open bundle: %v", err)
diff --git a/cmd/fpkg/main.go b/cmd/fpkg/main.go
index e23ce500..268c2d48 100644
--- a/cmd/fpkg/main.go
+++ b/cmd/fpkg/main.go
@@ -12,9 +12,7 @@ import (
"git.gensokyo.uk/security/fortify/command"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
"git.gensokyo.uk/security/fortify/internal"
- "git.gensokyo.uk/security/fortify/internal/app/init0"
"git.gensokyo.uk/security/fortify/internal/app/shim"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/internal/sys"
@@ -39,7 +37,6 @@ func init() {
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
sandbox.TryArgv0(fmsg.Output{}, fmsg.Prepare, internal.InstallFmsg)
- init0.TryArgv0()
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
@@ -65,9 +62,7 @@ func main() {
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next fortify action")
- // internal commands
c.Command("shim", command.UsageInternal, func([]string) error { shim.Main(); return errSuccess })
- c.Command("init", command.UsageInternal, func([]string) error { init0.Main(); return errSuccess })
{
var (
@@ -124,7 +119,7 @@ func main() {
Parse bundle and app metadata, do pre-install checks.
*/
- bundle := loadBundleInfo(path.Join(workDir, "bundle.json"), cleanup)
+ bundle := loadAppInfo(path.Join(workDir, "bundle.json"), cleanup)
pathSet := pathSetByApp(bundle.ID)
app := bundle
@@ -140,7 +135,7 @@ func main() {
log.Printf("metadata path %q is not a file", pathSet.metaPath)
return syscall.EBADMSG
} else {
- app = loadBundleInfo(pathSet.metaPath, cleanup)
+ app = loadAppInfo(pathSet.metaPath, cleanup)
if app.ID != bundle.ID {
cleanup()
log.Printf("app %q claims to have identifier %q",
@@ -273,7 +268,7 @@ func main() {
id := args[0]
pathSet := pathSetByApp(id)
- app := loadBundleInfo(pathSet.metaPath, func() {})
+ app := loadAppInfo(pathSet.metaPath, func() {})
if app.ID != id {
log.Printf("app %q claims to have identifier %q", id, app.ID)
return syscall.EBADE
@@ -322,51 +317,7 @@ func main() {
}
argv = append(argv, args[1:]...)
- config := &fst.Config{
- ID: app.ID,
- Command: argv,
- Confinement: fst.ConfinementConfig{
- AppID: app.AppID,
- Groups: app.Groups,
- Username: "fortify",
- Inner: path.Join("/data/data", app.ID),
- Outer: pathSet.homeDir,
- Sandbox: &fst.SandboxConfig{
- Hostname: formatHostname(app.Name),
- UserNS: app.UserNS,
- Net: app.Net,
- Dev: app.Dev,
- Syscall: &bwrap.SyscallPolicy{DenyDevel: !app.Devel, Multiarch: app.Multiarch, Bluetooth: app.Bluetooth},
- NoNewSession: app.NoNewSession || flagDropShell,
- MapRealUID: app.MapRealUID,
- DirectWayland: app.DirectWayland,
- Filesystem: []*fst.FilesystemConfig{
- {Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true},
- {Src: pathSet.metaPath, Dst: path.Join(fst.Tmp, "app"), Must: true},
- {Src: "/etc/resolv.conf"},
- {Src: "/sys/block"},
- {Src: "/sys/bus"},
- {Src: "/sys/class"},
- {Src: "/sys/dev"},
- {Src: "/sys/devices"},
- },
- Link: [][2]string{
- {app.CurrentSystem, "/run/current-system"},
- {"/run/current-system/sw/bin", "/bin"},
- {"/run/current-system/sw/bin", "/usr/bin"},
- },
- Etc: path.Join(pathSet.cacheDir, "etc"),
- AutoEtc: true,
- },
- ExtraPerms: []*fst.ExtraPermConfig{
- {Path: dataHome, Execute: true},
- {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true},
- },
- SystemBus: app.SystemBus,
- SessionBus: app.SessionBus,
- Enablements: app.Enablements,
- },
- }
+ config := app.toFst(pathSet, argv, flagDropShell)
/*
Expose GPU devices.
diff --git a/cmd/fpkg/proc.go b/cmd/fpkg/proc.go
index ea106adf..311ae4f2 100644
--- a/cmd/fpkg/proc.go
+++ b/cmd/fpkg/proc.go
@@ -11,14 +11,14 @@ import (
func mustRunApp(ctx context.Context, config *fst.Config, beforeFail func()) {
rs := new(fst.RunState)
- a := app.MustNew(std)
+ a := app.MustNew(ctx, std)
if sa, err := a.Seal(config); err != nil {
fmsg.PrintBaseError(err, "cannot seal app:")
rs.ExitCode = 1
} else {
// this updates ExitCode
- app.PrintRunStateErr(rs, sa.Run(ctx, rs))
+ app.PrintRunStateErr(rs, sa.Run(rs))
}
if rs.ExitCode != 0 {
diff --git a/cmd/fpkg/test/test.py b/cmd/fpkg/test/test.py
index eaf07fc5..7d4e0d58 100644
--- a/cmd/fpkg/test/test.py
+++ b/cmd/fpkg/test/test.py
@@ -62,8 +62,8 @@ def check_state(name, enablements):
config = instance['config']
- if len(config['command']) != 1 or not (config['command'][0].startswith("/nix/store/")) or f"fortify-{name}-" not in (config['command'][0]):
- raise Exception(f"unexpected command {instance['config']['command']}")
+ if len(config['args']) != 1 or not (config['args'][0].startswith("/nix/store/")) or f"fortify-{name}-" not in (config['args'][0]):
+ raise Exception(f"unexpected args {instance['config']['args']}")
if config['confinement']['enablements'] != enablements:
raise Exception(f"unexpected enablements {instance['config']['confinement']['enablements']}")
diff --git a/cmd/fpkg/with.go b/cmd/fpkg/with.go
index d7b0e968..d321de3f 100644
--- a/cmd/fpkg/with.go
+++ b/cmd/fpkg/with.go
@@ -6,18 +6,19 @@ import (
"strings"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
"git.gensokyo.uk/security/fortify/internal"
+ "git.gensokyo.uk/security/fortify/sandbox/seccomp"
)
func withNixDaemon(
ctx context.Context,
action string, command []string, net bool, updateConfig func(config *fst.Config) *fst.Config,
- app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
+ app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
) {
mustRunAppDropShell(ctx, updateConfig(&fst.Config{
- ID: app.ID,
- Command: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " +
+ ID: app.ID,
+ Path: shellPath,
+ Args: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " +
// start nix-daemon
"nix-daemon --store / & " +
// wait for socket to appear
@@ -34,11 +35,11 @@ func withNixDaemon(
Inner: path.Join("/data/data", app.ID),
Outer: pathSet.homeDir,
Sandbox: &fst.SandboxConfig{
- Hostname: formatHostname(app.Name) + "-" + action,
- UserNS: true, // nix sandbox requires userns
- Net: net,
- Syscall: &bwrap.SyscallPolicy{Multiarch: true},
- NoNewSession: dropShell,
+ Hostname: formatHostname(app.Name) + "-" + action,
+ Userns: true, // nix sandbox requires userns
+ Net: net,
+ Seccomp: seccomp.FlagMultiarch,
+ Tty: dropShell,
Filesystem: []*fst.FilesystemConfig{
{Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true},
},
@@ -61,19 +62,20 @@ func withNixDaemon(
func withCacheDir(
ctx context.Context,
action string, command []string, workDir string,
- app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
+ app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
mustRunAppDropShell(ctx, &fst.Config{
- ID: app.ID,
- Command: []string{shellPath, "-lc", strings.Join(command, " && ")},
+ ID: app.ID,
+ Path: shellPath,
+ Args: []string{shellPath, "-lc", strings.Join(command, " && ")},
Confinement: fst.ConfinementConfig{
AppID: app.AppID,
Username: "nixos",
Inner: path.Join("/data/data", app.ID, "cache"),
Outer: pathSet.cacheDir, // this also ensures cacheDir via shim
Sandbox: &fst.SandboxConfig{
- Hostname: formatHostname(app.Name) + "-" + action,
- Syscall: &bwrap.SyscallPolicy{Multiarch: true},
- NoNewSession: dropShell,
+ Hostname: formatHostname(app.Name) + "-" + action,
+ Seccomp: seccomp.FlagMultiarch,
+ Tty: dropShell,
Filesystem: []*fst.FilesystemConfig{
{Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true},
{Src: workDir, Dst: path.Join(fst.Tmp, "bundle"), Must: true},
@@ -97,7 +99,7 @@ func withCacheDir(
func mustRunAppDropShell(ctx context.Context, config *fst.Config, dropShell bool, beforeFail func()) {
if dropShell {
- config.Command = []string{shellPath, "-l"}
+ config.Args = []string{shellPath, "-l"}
mustRunApp(ctx, config, beforeFail)
beforeFail()
internal.Exit(0)
diff --git a/fst/app.go b/fst/app.go
index 87a836ca..3e026391 100644
--- a/fst/app.go
+++ b/fst/app.go
@@ -2,7 +2,6 @@
package fst
import (
- "context"
"time"
)
@@ -19,7 +18,7 @@ type App interface {
type SealedApp interface {
// Run commits sealed system setup and starts the app process.
- Run(ctx context.Context, rs *RunState) error
+ Run(rs *RunState) error
}
// RunState stores the outcome of a call to [SealedApp.Run].
diff --git a/fst/config.go b/fst/config.go
index d96934d4..7fc3af6c 100644
--- a/fst/config.go
+++ b/fst/config.go
@@ -2,7 +2,7 @@ package fst
import (
"git.gensokyo.uk/security/fortify/dbus"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/sandbox/seccomp"
"git.gensokyo.uk/security/fortify/system"
)
@@ -14,8 +14,11 @@ type Config struct {
// passed to wayland security-context-v1 as application ID
// and used as part of defaults in dbus session proxy
ID string `json:"id"`
- // final argv, passed to init
- Command []string `json:"command"`
+
+ // absolute path to executable file
+ Path string `json:"path,omitempty"`
+ // final args passed to container init
+ Args []string `json:"args"`
Confinement ConfinementConfig `json:"confinement"`
}
@@ -26,13 +29,13 @@ type ConfinementConfig struct {
AppID int `json:"app_id"`
// list of supplementary groups to inherit
Groups []string `json:"groups"`
- // passwd username in the sandbox, defaults to passwd name of target uid or chronos
+ // passwd username in container, defaults to passwd name of target uid or chronos
Username string `json:"username,omitempty"`
- // home directory in sandbox, empty for outer
+ // home directory in container, empty for outer
Inner string `json:"home_inner"`
// home directory in init namespace
Outer string `json:"home"`
- // bwrap sandbox confinement configuration
+ // abstract sandbox configuration
Sandbox *SandboxConfig `json:"sandbox"`
// extra acl ops, runs after everything else
ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"`
@@ -44,7 +47,7 @@ type ConfinementConfig struct {
// nil value makes session bus proxy assume built-in defaults
SessionBus *dbus.Config `json:"session_bus,omitempty"`
- // system resources to expose to the sandbox
+ // system resources to expose to the container
Enablements system.Enablements `json:"enablements"`
}
@@ -76,24 +79,12 @@ func (e *ExtraPermConfig) String() string {
return string(buf)
}
-type FilesystemConfig struct {
- // mount point in sandbox, same as src if empty
- Dst string `json:"dst,omitempty"`
- // host filesystem path to make available to sandbox
- Src string `json:"src"`
- // write access
- Write bool `json:"write,omitempty"`
- // device access
- Device bool `json:"dev,omitempty"`
- // fail if mount fails
- Must bool `json:"require,omitempty"`
-}
-
// Template returns a fully populated instance of Config.
func Template() *Config {
return &Config{
- ID: "org.chromium.Chromium",
- Command: []string{
+ ID: "org.chromium.Chromium",
+ Path: "/run/current-system/sw/bin/chromium",
+ Args: []string{
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
@@ -108,11 +99,13 @@ func Template() *Config {
Inner: "/var/lib/fortify",
Sandbox: &SandboxConfig{
Hostname: "localhost",
- UserNS: true,
+ Devel: true,
+ Userns: true,
Net: true,
Dev: true,
- Syscall: &bwrap.SyscallPolicy{DenyDevel: true, Multiarch: true},
- NoNewSession: true,
+ Seccomp: seccomp.FlagMultiarch,
+ Tty: true,
+ Multiarch: true,
MapRealUID: true,
DirectWayland: false,
// example API credentials pulled from Google Chrome
@@ -131,10 +124,10 @@ func Template() *Config {
Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true},
{Src: "/dev/dri", Device: true},
},
- Link: [][2]string{{"/run/user/65534", "/run/user/150"}},
- Etc: "/etc",
- AutoEtc: true,
- Override: []string{"/var/run/nscd"},
+ Link: [][2]string{{"/run/user/65534", "/run/user/150"}},
+ Etc: "/etc",
+ AutoEtc: true,
+ Cover: []string{"/var/run/nscd"},
},
ExtraPerms: []*ExtraPermConfig{
{Path: "/var/lib/fortify/u0", Ensure: true, Execute: true},
diff --git a/fst/sandbox.go b/fst/sandbox.go
index fc12da43..b1240eca 100644
--- a/fst/sandbox.go
+++ b/fst/sandbox.go
@@ -4,125 +4,149 @@ import (
"errors"
"fmt"
"io/fs"
+ "maps"
"path"
+ "slices"
+ "syscall"
"git.gensokyo.uk/security/fortify/dbus"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/sandbox"
+ "git.gensokyo.uk/security/fortify/sandbox/seccomp"
)
// SandboxConfig describes resources made available to the sandbox.
-type SandboxConfig struct {
- // unix hostname within sandbox
- Hostname string `json:"hostname,omitempty"`
- // allow userns within sandbox
- UserNS bool `json:"userns,omitempty"`
- // share net namespace
- Net bool `json:"net,omitempty"`
- // share all devices
- Dev bool `json:"dev,omitempty"`
- // seccomp syscall filter policy
- Syscall *bwrap.SyscallPolicy `json:"syscall"`
- // do not run in new session
- NoNewSession bool `json:"no_new_session,omitempty"`
- // map target user uid to privileged user uid in the user namespace
- MapRealUID bool `json:"map_real_uid"`
- // direct access to wayland socket; when this gets set no attempt is made to attach security-context-v1
- // and the bare socket is mounted to the sandbox
- DirectWayland bool `json:"direct_wayland,omitempty"`
+type (
+ SandboxConfig struct {
+ // container hostname
+ Hostname string `json:"hostname,omitempty"`
- // final environment variables
- Env map[string]string `json:"env"`
- // sandbox host filesystem access
- Filesystem []*FilesystemConfig `json:"filesystem"`
- // symlinks created inside the sandbox
- Link [][2]string `json:"symlink"`
- // read-only /etc directory
- Etc string `json:"etc,omitempty"`
- // automatically set up /etc symlinks
- AutoEtc bool `json:"auto_etc"`
- // mount tmpfs over these paths,
- // runs right before [ConfinementConfig.ExtraPerms]
- Override []string `json:"override"`
-}
+ // extra seccomp flags
+ Seccomp seccomp.SyscallOpts `json:"seccomp"`
+ // allow ptrace and friends
+ Devel bool `json:"devel,omitempty"`
+ // allow userns creation in container
+ Userns bool `json:"userns,omitempty"`
+ // share host net namespace
+ Net bool `json:"net,omitempty"`
+ // expose main process tty
+ Tty bool `json:"tty,omitempty"`
+ // allow multiarch
+ Multiarch bool `json:"multiarch,omitempty"`
-// SandboxSys encapsulates system functions used during the creation of [bwrap.Config].
-type SandboxSys interface {
- Getuid() int
- Paths() Paths
- ReadDir(name string) ([]fs.DirEntry, error)
- EvalSymlinks(path string) (string, error)
+ // initial process environment variables
+ Env map[string]string `json:"env"`
+ // map target user uid to privileged user uid in the user namespace
+ MapRealUID bool `json:"map_real_uid"`
- Println(v ...any)
- Printf(format string, v ...any)
-}
+ // expose all devices
+ Dev bool `json:"dev,omitempty"`
+ // container host filesystem bind mounts
+ Filesystem []*FilesystemConfig `json:"filesystem"`
+ // create symlinks inside container filesystem
+ Link [][2]string `json:"symlink"`
-// Bwrap returns the address of the corresponding bwrap.Config to s.
-// Note that remaining tmpfs entries must be queued by the caller prior to launch.
-func (s *SandboxConfig) Bwrap(sys SandboxSys, uid *int) (*bwrap.Config, error) {
- if s == nil {
- return nil, errors.New("nil sandbox config")
+ // direct access to wayland socket; when this gets set no attempt is made to attach security-context-v1
+ // and the bare socket is mounted to the sandbox
+ DirectWayland bool `json:"direct_wayland,omitempty"`
+
+ // read-only /etc directory
+ Etc string `json:"etc,omitempty"`
+ // automatically set up /etc symlinks
+ AutoEtc bool `json:"auto_etc"`
+ // cover these paths or create them if they do not already exist
+ Cover []string `json:"cover"`
}
- if s.Syscall == nil {
- sys.Println("syscall filter not configured, PROCEED WITH CAUTION")
+ // SandboxSys encapsulates system functions used during [sandbox.Container] initialisation.
+ SandboxSys interface {
+ Getuid() int
+ Getgid() int
+ Paths() Paths
+ ReadDir(name string) ([]fs.DirEntry, error)
+ EvalSymlinks(path string) (string, error)
+
+ Println(v ...any)
+ Printf(format string, v ...any)
}
- if !s.MapRealUID {
- // mapped uid defaults to 65534 to work around file ownership checks due to a bwrap limitation
- *uid = 65534
- } else {
- // some programs fail to connect to dbus session running as a different uid, so a separate workaround
- // is introduced to map priv-side caller uid in namespace
- *uid = sys.Getuid()
+ // FilesystemConfig is a representation of [sandbox.BindMount].
+ FilesystemConfig struct {
+ // mount point in container, same as src if empty
+ Dst string `json:"dst,omitempty"`
+ // host filesystem path to make available to the container
+ Src string `json:"src"`
+ // do not mount filesystem read-only
+ Write bool `json:"write,omitempty"`
+ // do not disable device files
+ Device bool `json:"dev,omitempty"`
+ // fail if the bind mount cannot be established for any reason
+ Must bool `json:"require,omitempty"`
}
+)
- conf := (&bwrap.Config{
- Net: s.Net,
- UserNS: s.UserNS,
- UID: uid,
- GID: uid,
- Hostname: s.Hostname,
- Clearenv: true,
- SetEnv: s.Env,
+// ToContainer initialises [sandbox.Params] via [SandboxConfig].
+// Note that remaining container setup must be queued by the [App] implementation.
+func (s *SandboxConfig) ToContainer(sys SandboxSys, uid, gid *int) (*sandbox.Params, map[string]string, error) {
+ if s == nil {
+ return nil, nil, syscall.EBADE
+ }
- /* this is only 4 KiB of memory on a 64-bit system,
- permissive defaults on NixOS results in around 100 entries
- so this capacity should eliminate copies for most setups */
- Filesystem: make([]bwrap.FSBuilder, 0, 256),
+ container := &sandbox.Params{
+ Hostname: s.Hostname,
+ Ops: new(sandbox.Ops),
+ Seccomp: s.Seccomp,
+ }
- Syscall: s.Syscall,
- NewSession: !s.NoNewSession,
- DieWithParent: true,
- AsInit: true,
+ /* this is only 4 KiB of memory on a 64-bit system,
+ permissive defaults on NixOS results in around 100 entries
+ so this capacity should eliminate copies for most setups */
+ *container.Ops = slices.Grow(*container.Ops, 1<<8)
- // initialise unconditionally as Once cannot be justified
- // for saving such a miniscule amount of memory
- Chmod: make(bwrap.ChmodConfig),
- }).
- Procfs("/proc").
- Tmpfs(Tmp, 4*1024)
+ if s.Devel {
+ container.Flags |= sandbox.FAllowDevel
+ }
+ if s.Userns {
+ container.Flags |= sandbox.FAllowUserns
+ }
+ if s.Net {
+ container.Flags |= sandbox.FAllowNet
+ }
+ if s.Tty {
+ container.Flags |= sandbox.FAllowTTY
+ }
- if !s.Dev {
- conf.DevTmpfs("/dev").Mqueue("/dev/mqueue")
+ if s.MapRealUID {
+ /* some programs fail to connect to dbus session running as a different uid
+ so this workaround is introduced to map priv-side caller uid in container */
+ container.Uid = sys.Getuid()
+ *uid = container.Uid
+ container.Gid = sys.Getgid()
+ *gid = container.Gid
} else {
- conf.Bind("/dev", "/dev", false, true, true)
+ *uid = sandbox.OverflowUid()
+ *gid = sandbox.OverflowGid()
}
- if !s.AutoEtc {
- if s.Etc == "" {
- conf.Dir("/etc")
- } else {
- conf.Bind(s.Etc, "/etc")
- }
+ container.
+ Proc("/proc").
+ Tmpfs(Tmp, 1<<12, 0755)
+
+ if !s.Dev {
+ container.Dev("/dev").Mqueue("/dev/mqueue")
+ } else {
+ container.Bind("/dev", "/dev", sandbox.BindDevice)
}
- // retrieve paths and hide them if they're made available in the sandbox
+ /* retrieve paths and hide them if they're made available in the sandbox;
+ this feature tries to improve user experience of permissive defaults, and
+ to warn about issues in custom configuration; it is NOT a security feature
+ and should not be treated as such, ALWAYS be careful with what you bind */
var hidePaths []string
sc := sys.Paths()
hidePaths = append(hidePaths, sc.RuntimePath, sc.SharePath)
_, systemBusAddr := dbus.Address()
if entries, err := dbus.Parse([]byte(systemBusAddr)); err != nil {
- return nil, err
+ return nil, nil, err
} else {
// there is usually only one, do not preallocate
for _, entry := range entries {
@@ -148,7 +172,7 @@ func (s *SandboxConfig) Bwrap(sys SandboxSys, uid *int) (*bwrap.Config, error) {
hidePathMatch := make([]bool, len(hidePaths))
for i := range hidePaths {
if err := evalSymlinks(sys, &hidePaths[i]); err != nil {
- return nil, err
+ return nil, nil, err
}
}
@@ -158,19 +182,19 @@ func (s *SandboxConfig) Bwrap(sys SandboxSys, uid *int) (*bwrap.Config, error) {
}
if !path.IsAbs(c.Src) {
- return nil, fmt.Errorf("src path %q is not absolute", c.Src)
+ return nil, nil, fmt.Errorf("src path %q is not absolute", c.Src)
}
dest := c.Dst
if c.Dst == "" {
dest = c.Src
} else if !path.IsAbs(dest) {
- return nil, fmt.Errorf("dst path %q is not absolute", dest)
+ return nil, nil, fmt.Errorf("dst path %q is not absolute", dest)
}
srcH := c.Src
if err := evalSymlinks(sys, &srcH); err != nil {
- return nil, err
+ return nil, nil, err
}
for i := range hidePaths {
@@ -180,54 +204,71 @@ func (s *SandboxConfig) Bwrap(sys SandboxSys, uid *int) (*bwrap.Config, error) {
}
if ok, err := deepContainsH(srcH, hidePaths[i]); err != nil {
- return nil, err
+ return nil, nil, err
} else if ok {
hidePathMatch[i] = true
sys.Printf("hiding paths from %q", c.Src)
}
}
- conf.Bind(c.Src, dest, !c.Must, c.Write, c.Device)
+ var flags int
+ if c.Write {
+ flags |= sandbox.BindWritable
+ }
+ if c.Device {
+ flags |= sandbox.BindDevice | sandbox.BindWritable
+ }
+ if !c.Must {
+ flags |= sandbox.BindOptional
+ }
+ container.Bind(c.Src, dest, flags)
}
- // hide marked paths before setting up shares
+ // cover matched paths
for i, ok := range hidePathMatch {
if ok {
- conf.Tmpfs(hidePaths[i], 8192)
+ container.Tmpfs(hidePaths[i], 1<<13, 0755)
}
}
for _, l := range s.Link {
- conf.Symlink(l[0], l[1])
+ container.Link(l[0], l[1])
}
- if s.AutoEtc {
- etc := s.Etc
- if etc == "" {
- etc = "/etc"
+ // perf: this might work better if implemented as a setup op in container init
+ if !s.AutoEtc {
+ if s.Etc != "" {
+ container.Bind(s.Etc, "/etc", 0)
+ }
+ } else {
+ etcPath := s.Etc
+ if etcPath == "" {
+ etcPath = "/etc"
}
- conf.Bind(etc, Tmp+"/etc")
+ container.
+ Bind(etcPath, Tmp+"/etc", 0).
+ Mkdir("/etc", 0700)
- // link host /etc contents to prevent passwd/group from being overwritten
- if d, err := sys.ReadDir(etc); err != nil {
- return nil, err
+ // link host /etc contents to prevent dropping passwd/group bind mounts
+ if d, err := sys.ReadDir(etcPath); err != nil {
+ return nil, nil, err
} else {
for _, ent := range d {
- name := ent.Name()
- switch name {
+ n := ent.Name()
+ switch n {
case "passwd":
case "group":
case "mtab":
- conf.Symlink("/proc/mounts", "/etc/"+name)
+ container.Link("/proc/mounts", "/etc/"+n)
default:
- conf.Symlink(Tmp+"/etc/"+name, "/etc/"+name)
+ container.Link(Tmp+"/etc/"+n, "/etc/"+n)
}
}
}
}
- return conf, nil
+ return container, maps.Clone(s.Env), nil
}
func evalSymlinks(sys SandboxSys, v *string) error {
diff --git a/internal/app/app.go b/internal/app/app.go
index c4668a08..42d67b96 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -1,6 +1,7 @@
package app
import (
+ "context"
"fmt"
"log"
"sync"
@@ -10,9 +11,10 @@ import (
"git.gensokyo.uk/security/fortify/internal/sys"
)
-func New(os sys.State) (fst.App, error) {
+func New(ctx context.Context, os sys.State) (fst.App, error) {
a := new(app)
a.sys = os
+ a.ctx = ctx
id := new(fst.ID)
err := fst.NewAppID(id)
@@ -21,8 +23,8 @@ func New(os sys.State) (fst.App, error) {
return a, err
}
-func MustNew(os sys.State) fst.App {
- a, err := New(os)
+func MustNew(ctx context.Context, os sys.State) fst.App {
+ a, err := New(ctx, os)
if err != nil {
log.Fatalf("cannot create app: %v", err)
}
@@ -32,6 +34,7 @@ func MustNew(os sys.State) fst.App {
type app struct {
id *stringPair[fst.ID]
sys sys.State
+ ctx context.Context
*outcome
mu sync.RWMutex
@@ -71,7 +74,7 @@ func (a *app) Seal(config *fst.Config) (fst.SealedApp, error) {
seal := new(outcome)
seal.id = a.id
- err := seal.finalise(a.sys, config)
+ err := seal.finalise(a.ctx, a.sys, config)
if err == nil {
a.outcome = seal
}
diff --git a/internal/app/app_nixos_test.go b/internal/app/app_nixos_test.go
index 683b179a..ca9fde08 100644
--- a/internal/app/app_nixos_test.go
+++ b/internal/app/app_nixos_test.go
@@ -4,7 +4,7 @@ import (
"git.gensokyo.uk/security/fortify/acl"
"git.gensokyo.uk/security/fortify/dbus"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/sandbox"
"git.gensokyo.uk/security/fortify/system"
)
@@ -12,20 +12,20 @@ var testCasesNixos = []sealTestCase{
{
"nixos chromium direct wayland", new(stubNixOS),
&fst.Config{
- ID: "org.chromium.Chromium",
- Command: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
+ ID: "org.chromium.Chromium",
+ Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
Confinement: fst.ConfinementConfig{
AppID: 1, Groups: []string{}, Username: "u0_a1",
Outer: "/var/lib/persist/module/fortify/0/1",
Sandbox: &fst.SandboxConfig{
- UserNS: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true,
+ Userns: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true,
Filesystem: []*fst.FilesystemConfig{
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
{Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"},
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
},
- Override: []string{"/var/run/nscd"},
+ Cover: []string{"/var/run/nscd"},
},
SystemBus: &dbus.Config{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
@@ -88,136 +88,133 @@ var testCasesNixos = []sealTestCase{
}).
UpdatePerm("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", acl.Read, acl.Write).
UpdatePerm("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", acl.Read, acl.Write),
- (&bwrap.Config{
- Net: true,
- UserNS: true,
- Chdir: "/var/lib/persist/module/fortify/0/1",
- Clearenv: true,
- SetEnv: map[string]string{
- "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1971/bus",
- "DBUS_SYSTEM_BUS_ADDRESS": "unix:path=/run/dbus/system_bus_socket",
- "HOME": "/var/lib/persist/module/fortify/0/1",
- "PULSE_COOKIE": fst.Tmp + "/pulse-cookie",
- "PULSE_SERVER": "unix:/run/user/1971/pulse/native",
- "SHELL": "/run/current-system/sw/bin/zsh",
- "TERM": "xterm-256color",
- "USER": "u0_a1",
- "WAYLAND_DISPLAY": "wayland-0",
- "XDG_RUNTIME_DIR": "/run/user/1971",
- "XDG_SESSION_CLASS": "user",
- "XDG_SESSION_TYPE": "tty",
+ &sandbox.Params{
+ Uid: 1971,
+ Gid: 100,
+ Flags: sandbox.FAllowNet | sandbox.FAllowUserns,
+ Dir: "/var/lib/persist/module/fortify/0/1",
+ Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
+ Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
+ Env: []string{
+ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
+ "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket",
+ "HOME=/var/lib/persist/module/fortify/0/1",
+ "PULSE_COOKIE=" + fst.Tmp + "/pulse-cookie",
+ "PULSE_SERVER=unix:/run/user/1971/pulse/native",
+ "TERM=xterm-256color",
+ "USER=u0_a1",
+ "WAYLAND_DISPLAY=wayland-0",
+ "XDG_RUNTIME_DIR=/run/user/1971",
+ "XDG_SESSION_CLASS=user",
+ "XDG_SESSION_TYPE=tty",
},
- Chmod: make(bwrap.ChmodConfig),
- NewSession: true,
- DieWithParent: true,
- AsInit: true,
- }).SetUID(1971).SetGID(1971).
- Procfs("/proc").
- Tmpfs(fst.Tmp, 4096).
- DevTmpfs("/dev").Mqueue("/dev/mqueue").
- Bind("/bin", "/bin").
- Bind("/usr/bin", "/usr/bin").
- Bind("/nix/store", "/nix/store").
- Bind("/run/current-system", "/run/current-system").
- Bind("/sys/block", "/sys/block", true).
- Bind("/sys/bus", "/sys/bus", true).
- Bind("/sys/class", "/sys/class", true).
- Bind("/sys/dev", "/sys/dev", true).
- Bind("/sys/devices", "/sys/devices", true).
- Bind("/run/opengl-driver", "/run/opengl-driver").
- Bind("/dev/dri", "/dev/dri", true, true, true).
- Bind("/etc", fst.Tmp+"/etc").
- Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
- Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
- Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
- Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
- Symlink(fst.Tmp+"/etc/default", "/etc/default").
- Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
- Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
- Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
- Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
- Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
- Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
- Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
- Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
- Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
- Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
- Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
- Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
- Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
- Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
- Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
- Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
- Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
- Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
- Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
- Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
- Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
- Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
- Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
- Symlink("/proc/mounts", "/etc/mtab").
- Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
- Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
- Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
- Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
- Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
- Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
- Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
- Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
- Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
- Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
- Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
- Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
- Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
- Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
- Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
- Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
- Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
- Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
- Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
- Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
- Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
- Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
- Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
- Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
- Symlink(fst.Tmp+"/etc/services", "/etc/services").
- Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
- Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
- Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
- Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
- Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
- Symlink(fst.Tmp+"/etc/static", "/etc/static").
- Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
- Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
- Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
- Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
- Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
- Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
- Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
- Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
- Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
- Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
- Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
- Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
- Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
- Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
- Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
- Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
- Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
- Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
- Tmpfs("/run/user", 1048576).
- Tmpfs("/run/user/1971", 8388608).
- Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", false, true).
- Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", false, true).
- CopyBind("/etc/passwd", []byte("u0_a1:x:1971:1971:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).
- CopyBind("/etc/group", []byte("fortify:x:1971:\n")).
- Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0").
- Bind("/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse", "/run/user/1971/pulse/native").
- CopyBind(fst.Tmp+"/pulse-cookie", nil).
- Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus").
- Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket").
- Tmpfs("/var/run/nscd", 8192).
- Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
- Symlink("fortify", "/.fortify/sbin/init0"),
+ Ops: new(sandbox.Ops).
+ Proc("/proc").
+ Tmpfs(fst.Tmp, 4096, 0755).
+ Dev("/dev").Mqueue("/dev/mqueue").
+ Bind("/bin", "/bin", 0).
+ Bind("/usr/bin", "/usr/bin", 0).
+ Bind("/nix/store", "/nix/store", 0).
+ Bind("/run/current-system", "/run/current-system", 0).
+ Bind("/sys/block", "/sys/block", sandbox.BindOptional).
+ Bind("/sys/bus", "/sys/bus", sandbox.BindOptional).
+ Bind("/sys/class", "/sys/class", sandbox.BindOptional).
+ Bind("/sys/dev", "/sys/dev", sandbox.BindOptional).
+ Bind("/sys/devices", "/sys/devices", sandbox.BindOptional).
+ Bind("/run/opengl-driver", "/run/opengl-driver", 0).
+ Bind("/dev/dri", "/dev/dri", sandbox.BindDevice|sandbox.BindWritable|sandbox.BindOptional).
+ Bind("/etc", fst.Tmp+"/etc", 0).
+ Mkdir("/etc", 0700).
+ Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
+ Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
+ Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
+ Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
+ Link(fst.Tmp+"/etc/default", "/etc/default").
+ Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
+ Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
+ Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
+ Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
+ Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
+ Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
+ Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
+ Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
+ Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
+ Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
+ Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
+ Link(fst.Tmp+"/etc/issue", "/etc/issue").
+ Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
+ Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
+ Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
+ Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
+ Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
+ Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
+ Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
+ Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
+ Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
+ Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
+ Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
+ Link("/proc/mounts", "/etc/mtab").
+ Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
+ Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
+ Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
+ Link(fst.Tmp+"/etc/nix", "/etc/nix").
+ Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
+ Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
+ Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
+ Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
+ Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
+ Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
+ Link(fst.Tmp+"/etc/pam", "/etc/pam").
+ Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
+ Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
+ Link(fst.Tmp+"/etc/pki", "/etc/pki").
+ Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
+ Link(fst.Tmp+"/etc/profile", "/etc/profile").
+ Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
+ Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
+ Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
+ Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
+ Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
+ Link(fst.Tmp+"/etc/samba", "/etc/samba").
+ Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
+ Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
+ Link(fst.Tmp+"/etc/services", "/etc/services").
+ Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
+ Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
+ Link(fst.Tmp+"/etc/shells", "/etc/shells").
+ Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
+ Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
+ Link(fst.Tmp+"/etc/static", "/etc/static").
+ Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
+ Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
+ Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
+ Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
+ Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
+ Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
+ Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
+ Link(fst.Tmp+"/etc/udev", "/etc/udev").
+ Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
+ Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
+ Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
+ Link(fst.Tmp+"/etc/X11", "/etc/X11").
+ Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
+ Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
+ Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
+ Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
+ Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
+ Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
+ Tmpfs("/run/user", 4096, 0755).
+ Tmpfs("/run/user/1971", 8388608, 0755).
+ Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", sandbox.BindWritable).
+ Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", sandbox.BindWritable).
+ Place("/etc/passwd", []byte("u0_a1:x:1971:100:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).
+ Place("/etc/group", []byte("fortify:x:100:\n")).
+ Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0", 0).
+ Bind("/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse", "/run/user/1971/pulse/native", 0).
+ Place(fst.Tmp+"/pulse-cookie", nil).
+ Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0).
+ Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0).
+ Tmpfs("/var/run/nscd", 8192, 0755),
+ },
},
}
diff --git a/internal/app/app_pd_test.go b/internal/app/app_pd_test.go
index 602965ce..15355797 100644
--- a/internal/app/app_pd_test.go
+++ b/internal/app/app_pd_test.go
@@ -6,7 +6,7 @@ import (
"git.gensokyo.uk/security/fortify/acl"
"git.gensokyo.uk/security/fortify/dbus"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
+ "git.gensokyo.uk/security/fortify/sandbox"
"git.gensokyo.uk/security/fortify/system"
)
@@ -14,7 +14,6 @@ var testCasesPd = []sealTestCase{
{
"nixos permissive defaults no enablements", new(stubNixOS),
&fst.Config{
- Command: make([]string, 0),
Confinement: fst.ConfinementConfig{
AppID: 0,
Username: "chronos",
@@ -35,136 +34,132 @@ var testCasesPd = []sealTestCase{
Ephemeral(system.Process, "/run/user/1971/fortify/4a450b6596d7bc15bd01780eb9a607ac", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/4a450b6596d7bc15bd01780eb9a607ac", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute),
- (&bwrap.Config{
- Net: true,
- UserNS: true,
- Clearenv: true,
- Syscall: new(bwrap.SyscallPolicy),
- Chdir: "/home/chronos",
- SetEnv: map[string]string{
- "HOME": "/home/chronos",
- "SHELL": "/run/current-system/sw/bin/zsh",
- "TERM": "xterm-256color",
- "USER": "chronos",
- "XDG_RUNTIME_DIR": "/run/user/65534",
- "XDG_SESSION_CLASS": "user",
- "XDG_SESSION_TYPE": "tty"},
- Chmod: make(bwrap.ChmodConfig),
- DieWithParent: true,
- AsInit: true,
- }).SetUID(65534).SetGID(65534).
- Procfs("/proc").
- Tmpfs(fst.Tmp, 4096).
- DevTmpfs("/dev").Mqueue("/dev/mqueue").
- Bind("/bin", "/bin", false, true).
- Bind("/boot", "/boot", 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("/run", "/run", false, true).
- Bind("/srv", "/srv", false, true).
- Bind("/sys", "/sys", false, true).
- Bind("/usr", "/usr", false, true).
- Bind("/var", "/var", false, true).
- Bind("/dev/kvm", "/dev/kvm", true, true, true).
- Tmpfs("/run/user/1971", 8192).
- Tmpfs("/run/dbus", 8192).
- Bind("/etc", fst.Tmp+"/etc").
- Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
- Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
- Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
- Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
- Symlink(fst.Tmp+"/etc/default", "/etc/default").
- Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
- Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
- Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
- Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
- Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
- Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
- Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
- Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
- Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
- Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
- Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
- Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
- Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
- Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
- Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
- Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
- Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
- Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
- Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
- Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
- Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
- Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
- Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
- Symlink("/proc/mounts", "/etc/mtab").
- Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
- Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
- Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
- Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
- Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
- Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
- Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
- Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
- Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
- Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
- Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
- Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
- Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
- Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
- Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
- Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
- Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
- Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
- Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
- Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
- Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
- Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
- Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
- Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
- Symlink(fst.Tmp+"/etc/services", "/etc/services").
- Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
- Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
- Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
- Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
- Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
- Symlink(fst.Tmp+"/etc/static", "/etc/static").
- Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
- Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
- Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
- Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
- Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
- Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
- Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
- Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
- Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
- Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
- Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
- Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
- Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
- Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
- Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
- Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
- Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
- Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
- Tmpfs("/run/user", 1048576).
- Tmpfs("/run/user/65534", 8388608).
- Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", false, true).
- Bind("/home/chronos", "/home/chronos", false, true).
- CopyBind("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
- CopyBind("/etc/group", []byte("fortify:x:65534:\n")).
- Tmpfs("/var/run/nscd", 8192).
- Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
- Symlink("fortify", "/.fortify/sbin/init0"),
+ &sandbox.Params{
+ Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
+ Dir: "/home/chronos",
+ Path: "/run/current-system/sw/bin/zsh",
+ Args: []string{"/run/current-system/sw/bin/zsh"},
+ Env: []string{
+ "HOME=/home/chronos",
+ "TERM=xterm-256color",
+ "USER=chronos",
+ "XDG_RUNTIME_DIR=/run/user/65534",
+ "XDG_SESSION_CLASS=user",
+ "XDG_SESSION_TYPE=tty",
+ },
+ Ops: new(sandbox.Ops).
+ Proc("/proc").
+ Tmpfs(fst.Tmp, 4096, 0755).
+ Dev("/dev").Mqueue("/dev/mqueue").
+ Bind("/bin", "/bin", sandbox.BindWritable).
+ Bind("/boot", "/boot", sandbox.BindWritable).
+ Bind("/home", "/home", sandbox.BindWritable).
+ Bind("/lib", "/lib", sandbox.BindWritable).
+ Bind("/lib64", "/lib64", sandbox.BindWritable).
+ Bind("/nix", "/nix", sandbox.BindWritable).
+ Bind("/root", "/root", sandbox.BindWritable).
+ Bind("/run", "/run", sandbox.BindWritable).
+ Bind("/srv", "/srv", sandbox.BindWritable).
+ Bind("/sys", "/sys", sandbox.BindWritable).
+ Bind("/usr", "/usr", sandbox.BindWritable).
+ Bind("/var", "/var", sandbox.BindWritable).
+ Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
+ Tmpfs("/run/user/1971", 8192, 0755).
+ Tmpfs("/run/dbus", 8192, 0755).
+ Bind("/etc", fst.Tmp+"/etc", 0).
+ Mkdir("/etc", 0700).
+ Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
+ Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
+ Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
+ Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
+ Link(fst.Tmp+"/etc/default", "/etc/default").
+ Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
+ Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
+ Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
+ Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
+ Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
+ Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
+ Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
+ Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
+ Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
+ Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
+ Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
+ Link(fst.Tmp+"/etc/issue", "/etc/issue").
+ Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
+ Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
+ Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
+ Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
+ Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
+ Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
+ Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
+ Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
+ Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
+ Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
+ Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
+ Link("/proc/mounts", "/etc/mtab").
+ Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
+ Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
+ Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
+ Link(fst.Tmp+"/etc/nix", "/etc/nix").
+ Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
+ Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
+ Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
+ Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
+ Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
+ Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
+ Link(fst.Tmp+"/etc/pam", "/etc/pam").
+ Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
+ Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
+ Link(fst.Tmp+"/etc/pki", "/etc/pki").
+ Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
+ Link(fst.Tmp+"/etc/profile", "/etc/profile").
+ Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
+ Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
+ Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
+ Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
+ Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
+ Link(fst.Tmp+"/etc/samba", "/etc/samba").
+ Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
+ Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
+ Link(fst.Tmp+"/etc/services", "/etc/services").
+ Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
+ Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
+ Link(fst.Tmp+"/etc/shells", "/etc/shells").
+ Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
+ Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
+ Link(fst.Tmp+"/etc/static", "/etc/static").
+ Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
+ Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
+ Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
+ Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
+ Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
+ Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
+ Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
+ Link(fst.Tmp+"/etc/udev", "/etc/udev").
+ Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
+ Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
+ Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
+ Link(fst.Tmp+"/etc/X11", "/etc/X11").
+ Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
+ Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
+ Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
+ Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
+ Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
+ Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
+ Tmpfs("/run/user", 4096, 0755).
+ Tmpfs("/run/user/65534", 8388608, 0755).
+ Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", sandbox.BindWritable).
+ Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
+ Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
+ Place("/etc/group", []byte("fortify:x:65534:\n")).
+ Tmpfs("/var/run/nscd", 8192, 0755),
+ },
},
{
"nixos permissive defaults chromium", new(stubNixOS),
&fst.Config{
- ID: "org.chromium.Chromium",
- Command: []string{"/run/current-system/sw/bin/zsh", "-c", "exec chromium "},
+ ID: "org.chromium.Chromium",
+ Args: []string{"zsh", "-c", "exec chromium "},
Confinement: fst.ConfinementConfig{
AppID: 9,
Groups: []string{"video"},
@@ -254,141 +249,136 @@ var testCasesPd = []sealTestCase{
}).
UpdatePerm("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write).
UpdatePerm("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write),
- (&bwrap.Config{
- Net: true,
- UserNS: true,
- Chdir: "/home/chronos",
- Clearenv: true,
- Syscall: new(bwrap.SyscallPolicy),
- SetEnv: map[string]string{
- "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/65534/bus",
- "DBUS_SYSTEM_BUS_ADDRESS": "unix:path=/run/dbus/system_bus_socket",
- "HOME": "/home/chronos",
- "PULSE_COOKIE": fst.Tmp + "/pulse-cookie",
- "PULSE_SERVER": "unix:/run/user/65534/pulse/native",
- "SHELL": "/run/current-system/sw/bin/zsh",
- "TERM": "xterm-256color",
- "USER": "chronos",
- "WAYLAND_DISPLAY": "wayland-0",
- "XDG_RUNTIME_DIR": "/run/user/65534",
- "XDG_SESSION_CLASS": "user",
- "XDG_SESSION_TYPE": "tty",
+ &sandbox.Params{
+ Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
+ Dir: "/home/chronos",
+ Path: "/run/current-system/sw/bin/zsh",
+ Args: []string{"zsh", "-c", "exec chromium "},
+ Env: []string{
+ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus",
+ "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket",
+ "HOME=/home/chronos",
+ "PULSE_COOKIE=" + fst.Tmp + "/pulse-cookie",
+ "PULSE_SERVER=unix:/run/user/65534/pulse/native",
+ "TERM=xterm-256color",
+ "USER=chronos",
+ "WAYLAND_DISPLAY=wayland-0",
+ "XDG_RUNTIME_DIR=/run/user/65534",
+ "XDG_SESSION_CLASS=user",
+ "XDG_SESSION_TYPE=tty",
},
- Chmod: make(bwrap.ChmodConfig),
- DieWithParent: true,
- AsInit: true,
- }).SetUID(65534).SetGID(65534).
- Procfs("/proc").
- Tmpfs(fst.Tmp, 4096).
- DevTmpfs("/dev").Mqueue("/dev/mqueue").
- Bind("/bin", "/bin", false, true).
- Bind("/boot", "/boot", 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("/run", "/run", false, true).
- Bind("/srv", "/srv", false, true).
- Bind("/sys", "/sys", false, true).
- Bind("/usr", "/usr", false, true).
- Bind("/var", "/var", false, true).
- Bind("/dev/dri", "/dev/dri", true, true, true).
- Bind("/dev/kvm", "/dev/kvm", true, true, true).
- Tmpfs("/run/user/1971", 8192).
- Tmpfs("/run/dbus", 8192).
- Bind("/etc", fst.Tmp+"/etc").
- Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
- Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
- Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
- Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
- Symlink(fst.Tmp+"/etc/default", "/etc/default").
- Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
- Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
- Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
- Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
- Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
- Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
- Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
- Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
- Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
- Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
- Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
- Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
- Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
- Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
- Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
- Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
- Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
- Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
- Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
- Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
- Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
- Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
- Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
- Symlink("/proc/mounts", "/etc/mtab").
- Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
- Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
- Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
- Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
- Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
- Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
- Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
- Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
- Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
- Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
- Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
- Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
- Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
- Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
- Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
- Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
- Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
- Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
- Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
- Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
- Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
- Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
- Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
- Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
- Symlink(fst.Tmp+"/etc/services", "/etc/services").
- Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
- Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
- Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
- Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
- Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
- Symlink(fst.Tmp+"/etc/static", "/etc/static").
- Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
- Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
- Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
- Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
- Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
- Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
- Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
- Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
- Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
- Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
- Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
- Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
- Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
- Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
- Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
- Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
- Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
- Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
- Tmpfs("/run/user", 1048576).
- Tmpfs("/run/user/65534", 8388608).
- Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", false, true).
- Bind("/home/chronos", "/home/chronos", false, true).
- CopyBind("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
- CopyBind("/etc/group", []byte("fortify:x:65534:\n")).
- Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0").
- Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native").
- CopyBind(fst.Tmp+"/pulse-cookie", nil).
- Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus").
- Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket").
- Tmpfs("/var/run/nscd", 8192).
- Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
- Symlink("fortify", "/.fortify/sbin/init0"),
+ Ops: new(sandbox.Ops).
+ Proc("/proc").
+ Tmpfs(fst.Tmp, 4096, 0755).
+ Dev("/dev").Mqueue("/dev/mqueue").
+ Bind("/bin", "/bin", sandbox.BindWritable).
+ Bind("/boot", "/boot", sandbox.BindWritable).
+ Bind("/home", "/home", sandbox.BindWritable).
+ Bind("/lib", "/lib", sandbox.BindWritable).
+ Bind("/lib64", "/lib64", sandbox.BindWritable).
+ Bind("/nix", "/nix", sandbox.BindWritable).
+ Bind("/root", "/root", sandbox.BindWritable).
+ Bind("/run", "/run", sandbox.BindWritable).
+ Bind("/srv", "/srv", sandbox.BindWritable).
+ Bind("/sys", "/sys", sandbox.BindWritable).
+ Bind("/usr", "/usr", sandbox.BindWritable).
+ Bind("/var", "/var", sandbox.BindWritable).
+ Bind("/dev/dri", "/dev/dri", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
+ Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
+ Tmpfs("/run/user/1971", 8192, 0755).
+ Tmpfs("/run/dbus", 8192, 0755).
+ Bind("/etc", fst.Tmp+"/etc", 0).
+ Mkdir("/etc", 0700).
+ Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
+ Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
+ Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
+ Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
+ Link(fst.Tmp+"/etc/default", "/etc/default").
+ Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
+ Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
+ Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
+ Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
+ Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
+ Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
+ Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
+ Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
+ Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
+ Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
+ Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
+ Link(fst.Tmp+"/etc/issue", "/etc/issue").
+ Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
+ Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
+ Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
+ Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
+ Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
+ Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
+ Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
+ Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
+ Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
+ Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
+ Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
+ Link("/proc/mounts", "/etc/mtab").
+ Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
+ Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
+ Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
+ Link(fst.Tmp+"/etc/nix", "/etc/nix").
+ Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
+ Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
+ Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
+ Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
+ Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
+ Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
+ Link(fst.Tmp+"/etc/pam", "/etc/pam").
+ Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
+ Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
+ Link(fst.Tmp+"/etc/pki", "/etc/pki").
+ Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
+ Link(fst.Tmp+"/etc/profile", "/etc/profile").
+ Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
+ Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
+ Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
+ Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
+ Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
+ Link(fst.Tmp+"/etc/samba", "/etc/samba").
+ Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
+ Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
+ Link(fst.Tmp+"/etc/services", "/etc/services").
+ Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
+ Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
+ Link(fst.Tmp+"/etc/shells", "/etc/shells").
+ Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
+ Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
+ Link(fst.Tmp+"/etc/static", "/etc/static").
+ Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
+ Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
+ Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
+ Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
+ Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
+ Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
+ Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
+ Link(fst.Tmp+"/etc/udev", "/etc/udev").
+ Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
+ Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
+ Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
+ Link(fst.Tmp+"/etc/X11", "/etc/X11").
+ Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
+ Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
+ Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
+ Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
+ Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
+ Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
+ Tmpfs("/run/user", 4096, 0755).
+ Tmpfs("/run/user/65534", 8388608, 0755).
+ Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", sandbox.BindWritable).
+ Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
+ Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
+ Place("/etc/group", []byte("fortify:x:65534:\n")).
+ Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0", 0).
+ Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native", 0).
+ Place(fst.Tmp+"/pulse-cookie", nil).
+ Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0).
+ Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0).
+ Tmpfs("/var/run/nscd", 8192, 0755),
+ },
},
}
diff --git a/internal/app/app_stub_test.go b/internal/app/app_stub_test.go
index be496177..665dfa04 100644
--- a/internal/app/app_stub_test.go
+++ b/internal/app/app_stub_test.go
@@ -55,10 +55,8 @@ func (s *stubNixOS) LookPath(file string) (string, error) {
}
switch file {
- case "sudo":
- return "/run/wrappers/bin/sudo", nil
- case "machinectl":
- return "/home/ophestra/.nix-profile/bin/machinectl", nil
+ case "zsh":
+ return "/run/current-system/sw/bin/zsh", nil
default:
panic(fmt.Sprintf("attempted to look up unexpected executable %q", file))
}
diff --git a/internal/app/app_test.go b/internal/app/app_test.go
index b4481537..fa13ddd9 100644
--- a/internal/app/app_test.go
+++ b/internal/app/app_test.go
@@ -8,19 +8,19 @@ import (
"time"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
"git.gensokyo.uk/security/fortify/internal/app"
"git.gensokyo.uk/security/fortify/internal/sys"
+ "git.gensokyo.uk/security/fortify/sandbox"
"git.gensokyo.uk/security/fortify/system"
)
type sealTestCase struct {
- name string
- os sys.State
- config *fst.Config
- id fst.ID
- wantSys *system.I
- wantBwrap *bwrap.Config
+ name string
+ os sys.State
+ config *fst.Config
+ id fst.ID
+ wantSys *system.I
+ wantContainer *sandbox.Params
}
func TestApp(t *testing.T) {
@@ -30,15 +30,15 @@ func TestApp(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
a := app.NewWithID(tc.id, tc.os)
var (
- gotSys *system.I
- gotBwrap *bwrap.Config
+ gotSys *system.I
+ gotContainer *sandbox.Params
)
if !t.Run("seal", func(t *testing.T) {
if sa, err := a.Seal(tc.config); err != nil {
t.Errorf("Seal: error = %v", err)
return
} else {
- gotSys, gotBwrap = app.AppSystemBwrap(a, sa)
+ gotSys, gotContainer = app.AppIParams(a, sa)
}
}) {
return
@@ -51,10 +51,10 @@ func TestApp(t *testing.T) {
}
})
- t.Run("compare bwrap", func(t *testing.T) {
- if !reflect.DeepEqual(gotBwrap, tc.wantBwrap) {
- t.Errorf("seal: bwrap =\n%s\n, want\n%s",
- mustMarshal(gotBwrap), mustMarshal(tc.wantBwrap))
+ t.Run("compare params", func(t *testing.T) {
+ if !reflect.DeepEqual(gotContainer, tc.wantContainer) {
+ t.Errorf("seal: params =\n%s\n, want\n%s",
+ mustMarshal(gotContainer), mustMarshal(tc.wantContainer))
}
})
})
diff --git a/internal/app/export_test.go b/internal/app/export_test.go
index 199b6c1f..60d97b36 100644
--- a/internal/app/export_test.go
+++ b/internal/app/export_test.go
@@ -2,8 +2,8 @@ package app
import (
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
"git.gensokyo.uk/security/fortify/internal/sys"
+ "git.gensokyo.uk/security/fortify/sandbox"
"git.gensokyo.uk/security/fortify/system"
)
@@ -14,7 +14,7 @@ func NewWithID(id fst.ID, os sys.State) fst.App {
return a
}
-func AppSystemBwrap(a fst.App, sa fst.SealedApp) (*system.I, *bwrap.Config) {
+func AppIParams(a fst.App, sa fst.SealedApp) (*system.I, *sandbox.Params) {
v := a.(*app)
seal := sa.(*outcome)
if v.outcome != seal || v.id != seal.id {
diff --git a/internal/app/init0/early.go b/internal/app/init0/early.go
deleted file mode 100644
index 0bcf094c..00000000
--- a/internal/app/init0/early.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package init0
-
-import (
- "os"
- "path"
-
- "git.gensokyo.uk/security/fortify/internal"
-)
-
-// used by the parent process
-
-// TryArgv0 calls [Main] if the last element of argv0 is "init0".
-func TryArgv0() {
- if len(os.Args) > 0 && path.Base(os.Args[0]) == "init0" {
- Main()
- internal.Exit(0)
- }
-}
diff --git a/internal/app/init0/main.go b/internal/app/init0/main.go
deleted file mode 100644
index 7b8d27c6..00000000
--- a/internal/app/init0/main.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package init0
-
-import (
- "errors"
- "log"
- "os"
- "os/exec"
- "os/signal"
- "syscall"
- "time"
-
- "git.gensokyo.uk/security/fortify/internal"
- "git.gensokyo.uk/security/fortify/internal/fmsg"
- "git.gensokyo.uk/security/fortify/sandbox"
-)
-
-const (
- // time to wait for linger processes after death of initial process
- residualProcessTimeout = 5 * time.Second
-)
-
-// everything beyond this point runs within pid namespace
-// proceed with caution!
-
-func Main() {
- // sharing stdout with shim
- // USE WITH CAUTION
- fmsg.Prepare("init0")
-
- // setting this prevents ptrace
- if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
- log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
- }
-
- if os.Getpid() != 1 {
- log.Fatal("this process must run as pid 1")
- }
-
- // receive setup payload
- var (
- payload Payload
- closeSetup func() error
- )
- if f, err := sandbox.Receive(Env, &payload, nil); err != nil {
- if errors.Is(err, sandbox.ErrInvalid) {
- log.Fatal("invalid config descriptor")
- }
- if errors.Is(err, sandbox.ErrNotSet) {
- log.Fatal("FORTIFY_INIT not set")
- }
-
- log.Fatalf("cannot decode init setup payload: %v", err)
- } else {
- fmsg.Store(payload.Verbose)
- closeSetup = f
-
- // child does not need to see this
- if err = os.Unsetenv(Env); err != nil {
- log.Printf("cannot unset %s: %v", Env, err)
- // not fatal
- } else {
- fmsg.Verbose("received configuration")
- }
- }
-
- // die with parent
- if err := sandbox.SetPdeathsig(syscall.SIGKILL); err != nil {
- log.Fatalf("prctl(PR_SET_PDEATHSIG, SIGKILL): %v", err)
- }
-
- cmd := exec.Command(payload.Argv0)
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
- cmd.Args = payload.Argv
- cmd.Env = os.Environ()
-
- if err := cmd.Start(); err != nil {
- log.Fatalf("cannot start %q: %v", payload.Argv0, err)
- }
- fmsg.Suspend()
-
- // close setup pipe as setup is now complete
- if err := closeSetup(); err != nil {
- log.Println("cannot close setup pipe:", err)
- // not fatal
- }
-
- sig := make(chan os.Signal, 2)
- signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
-
- type winfo struct {
- wpid int
- wstatus syscall.WaitStatus
- }
- info := make(chan winfo, 1)
- done := make(chan struct{})
-
- go func() {
- var (
- err error
- wpid = -2
- wstatus syscall.WaitStatus
- )
-
- // keep going until no child process is left
- for wpid != -1 {
- if err != nil {
- break
- }
-
- if wpid != -2 {
- info <- winfo{wpid, wstatus}
- }
-
- err = syscall.EINTR
- for errors.Is(err, syscall.EINTR) {
- wpid, err = syscall.Wait4(-1, &wstatus, 0, nil)
- }
- }
- if !errors.Is(err, syscall.ECHILD) {
- log.Println("unexpected wait4 response:", err)
- }
-
- close(done)
- }()
-
- // closed after residualProcessTimeout has elapsed after initial process death
- timeout := make(chan struct{})
-
- r := 2
- for {
- select {
- case s := <-sig:
- if fmsg.Resume() {
- fmsg.Verbosef("terminating on %s after process start", s.String())
- } else {
- fmsg.Verbosef("terminating on %s", s.String())
- }
- internal.Exit(0)
- case w := <-info:
- if w.wpid == cmd.Process.Pid {
- // initial process exited, output is most likely available again
- fmsg.Resume()
-
- switch {
- case w.wstatus.Exited():
- r = w.wstatus.ExitStatus()
- case w.wstatus.Signaled():
- r = 128 + int(w.wstatus.Signal())
- default:
- r = 255
- }
-
- go func() {
- time.Sleep(residualProcessTimeout)
- close(timeout)
- }()
- }
- case <-done:
- internal.Exit(r)
- case <-timeout:
- log.Println("timeout exceeded waiting for lingering processes")
- internal.Exit(r)
- }
- }
-}
diff --git a/internal/app/init0/payload.go b/internal/app/init0/payload.go
deleted file mode 100644
index d1dc9ec3..00000000
--- a/internal/app/init0/payload.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package init0
-
-const Env = "FORTIFY_INIT"
-
-type Payload struct {
- // target full exec path
- Argv0 string
- // child full argv
- Argv []string
-
- // verbosity pass through
- Verbose bool
-}
diff --git a/internal/app/process.go b/internal/app/process.go
index 2755230c..6c337142 100644
--- a/internal/app/process.go
+++ b/internal/app/process.go
@@ -3,15 +3,12 @@ package app
import (
"context"
"errors"
- "fmt"
"log"
"os/exec"
- "path/filepath"
"strings"
"time"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper"
"git.gensokyo.uk/security/fortify/internal"
"git.gensokyo.uk/security/fortify/internal/app/shim"
"git.gensokyo.uk/security/fortify/internal/fmsg"
@@ -21,7 +18,7 @@ import (
const shimSetupTimeout = 5 * time.Second
-func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
+func (seal *outcome) Run(rs *fst.RunState) error {
if !seal.f.CompareAndSwap(false, true) {
// run does much more than just starting a process; calling it twice, even if the first call fails, will result
// in inconsistent state that is impossible to clean up; return here to limit damage and hopefully give the
@@ -38,32 +35,10 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
fmsg.Verbosef("setuid helper at %s", internal.MustFsuPath())
/*
- resolve exec paths
- */
-
- shimExec := [2]string{helper.BubblewrapName}
- if len(seal.command) > 0 {
- shimExec[1] = seal.command[0]
- }
- for i, n := range shimExec {
- if len(n) == 0 {
- continue
- }
- if filepath.Base(n) == n {
- if s, err := exec.LookPath(n); err == nil {
- shimExec[i] = s
- } else {
- return fmsg.WrapError(err,
- fmt.Sprintf("executable file %q not found in $PATH", n))
- }
- }
- }
-
- /*
prepare/revert os state
*/
- if err := seal.sys.Commit(ctx); err != nil {
+ if err := seal.sys.Commit(seal.ctx); err != nil {
return err
}
store := state.NewMulti(seal.runDirPath)
@@ -137,7 +112,6 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
if startTime, err := cmd.Start(
seal.user.aid.String(),
seal.user.supp,
- seal.bwrapSync,
); err != nil {
return err
} else {
@@ -145,7 +119,7 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
rs.Time = startTime
}
- c, cancel := context.WithTimeout(ctx, shimSetupTimeout)
+ ctx, cancel := context.WithTimeout(seal.ctx, shimSetupTimeout)
defer cancel()
go func() {
@@ -154,11 +128,9 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
cancel()
}()
- if err := cmd.Serve(c, &shim.Payload{
- Argv: seal.command,
- Exec: shimExec,
- Bwrap: seal.container,
- Home: seal.user.data,
+ if err := cmd.Serve(ctx, &shim.Params{
+ Container: seal.container,
+ Home: seal.user.data,
Verbose: fmsg.Load(),
}); err != nil {
@@ -199,18 +171,22 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
// this is reached when a fault makes an already running shim impossible to continue execution
// however a kill signal could not be delivered (should actually always happen like that since fsu)
// the effects of this is similar to the alternative exit path and ensures shim death
- case err := <-cmd.WaitFallback():
+ case err := <-cmd.Fallback():
rs.ExitCode = 255
log.Printf("cannot terminate shim on faulted setup: %v", err)
// alternative exit path relying on shim behaviour on monitor process exit
- case <-ctx.Done():
+ case <-seal.ctx.Done():
fmsg.Verbose("alternative exit path selected")
}
fmsg.Resume()
+ if seal.sync != nil {
+ if err := seal.sync.Close(); err != nil {
+ log.Printf("cannot close wayland security context: %v", err)
+ }
+ }
if seal.dbusMsg != nil {
- // dump dbus message buffer
seal.dbusMsg()
}
diff --git a/internal/app/seal.go b/internal/app/seal.go
index 30702124..a25897dc 100644
--- a/internal/app/seal.go
+++ b/internal/app/seal.go
@@ -2,24 +2,28 @@ package app
import (
"bytes"
+ "context"
"encoding/gob"
"errors"
"fmt"
"io"
"io/fs"
+ "maps"
"os"
"path"
"regexp"
+ "slices"
"strings"
"sync/atomic"
+ "syscall"
"git.gensokyo.uk/security/fortify/acl"
"git.gensokyo.uk/security/fortify/dbus"
"git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper/bwrap"
"git.gensokyo.uk/security/fortify/internal"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/internal/sys"
+ "git.gensokyo.uk/security/fortify/sandbox"
"git.gensokyo.uk/security/fortify/system"
"git.gensokyo.uk/security/fortify/wl"
)
@@ -65,19 +69,19 @@ type outcome struct {
// copied from [sys.State] response
runDirPath string
- // passed through from [fst.Config]
- command []string
-
// initial [fst.Config] gob stream for state data;
- // this is prepared ahead of time as config is mutated during seal creation
+ // this is prepared ahead of time as config is clobbered during seal creation
ct io.WriterTo
// dump dbus proxy message buffer
dbusMsg func()
- user fsuUser
- sys *system.I
- container *bwrap.Config
- bwrapSync *os.File
+ user fsuUser
+ sys *system.I
+ ctx context.Context
+
+ container *sandbox.Params
+ env map[string]string
+ sync *os.File
f atomic.Bool
}
@@ -100,7 +104,17 @@ type fsuUser struct {
username string
}
-func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
+func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Config) error {
+ if seal.ctx != nil {
+ panic("finalise called twice")
+ }
+ seal.ctx = ctx
+
+ shellPath := "/bin/sh"
+ if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) {
+ shellPath = s
+ }
+
{
// encode initial configuration for state tracking
ct := new(bytes.Buffer)
@@ -111,9 +125,6 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
seal.ct = ct
}
- // pass through command slice; this value is never touched in the main process
- seal.command = config.Command
-
// allowed aid range 0 to 9999, this is checked again in fsu
if config.Confinement.AppID < 0 || config.Confinement.AppID > 9999 {
return fmsg.WrapError(ErrUser,
@@ -167,12 +178,24 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
if config.Confinement.Sandbox == nil {
fmsg.Verbose("sandbox configuration not supplied, PROCEED WITH CAUTION")
+ // fsu clears the environment so resolve paths early
+ if !path.IsAbs(config.Path) {
+ if len(config.Args) > 0 {
+ if p, err := sys.LookPath(config.Args[0]); err != nil {
+ return fmsg.WrapError(err, err.Error())
+ } else {
+ config.Path = p
+ }
+ } else {
+ config.Path = shellPath
+ }
+ }
+
conf := &fst.SandboxConfig{
- UserNS: true,
- Net: true,
- Syscall: new(bwrap.SyscallPolicy),
- NoNewSession: true,
- AutoEtc: true,
+ Userns: true,
+ Net: true,
+ Tty: true,
+ AutoEtc: true,
}
// bind entries in /
if d, err := sys.ReadDir("/"); err != nil {
@@ -198,7 +221,7 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
// hide nscd from sandbox if present
nscd := "/var/run/nscd"
if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
- conf.Override = append(conf.Override, nscd)
+ conf.Cover = append(conf.Cover, nscd)
}
// bind GPU stuff
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) {
@@ -210,17 +233,29 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
config.Confinement.Sandbox = conf
}
- var mapuid *stringPair[int]
+ var mapuid, mapgid *stringPair[int]
{
- var uid int
+ var uid, gid int
var err error
- seal.container, err = config.Confinement.Sandbox.Bwrap(sys, &uid)
+ seal.container, seal.env, err = config.Confinement.Sandbox.ToContainer(sys, &uid, &gid)
if err != nil {
- return err
+ return fmsg.WrapErrorSuffix(err,
+ "cannot initialise container configuration:")
+ }
+ if !path.IsAbs(config.Path) {
+ return fmsg.WrapError(syscall.EINVAL,
+ "invalid program path")
}
+ if len(config.Args) == 0 {
+ config.Args = []string{config.Path}
+ }
+ seal.container.Path = config.Path
+ seal.container.Args = config.Args
+
mapuid = newInt(uid)
- if seal.container.SetEnv == nil {
- seal.container.SetEnv = make(map[string]string)
+ mapgid = newInt(gid)
+ if seal.env == nil {
+ seal.env = make(map[string]string)
}
}
@@ -255,35 +290,27 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as post-fsu user
innerRuntimeDir := path.Join("/run/user", mapuid.String())
- seal.container.Tmpfs("/run/user", 1*1024*1024)
- seal.container.Tmpfs(innerRuntimeDir, 8*1024*1024)
- seal.container.SetEnv[xdgRuntimeDir] = innerRuntimeDir
- seal.container.SetEnv[xdgSessionClass] = "user"
- seal.container.SetEnv[xdgSessionType] = "tty"
+ seal.container.Tmpfs("/run/user", 1<<12, 0755)
+ seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0755)
+ seal.env[xdgRuntimeDir] = innerRuntimeDir
+ seal.env[xdgSessionClass] = "user"
+ seal.env[xdgSessionType] = "tty"
// outer path for inner /tmp
{
tmpdir := path.Join(sc.SharePath, "tmpdir")
seal.sys.Ensure(tmpdir, 0700)
seal.sys.UpdatePermType(system.User, tmpdir, acl.Execute)
- tmpdirProc := path.Join(tmpdir, seal.user.aid.String())
- seal.sys.Ensure(tmpdirProc, 01700)
- seal.sys.UpdatePermType(system.User, tmpdirProc, acl.Read, acl.Write, acl.Execute)
- seal.container.Bind(tmpdirProc, "/tmp", false, true)
+ tmpdirInst := path.Join(tmpdir, seal.user.aid.String())
+ seal.sys.Ensure(tmpdirInst, 01700)
+ seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
+ seal.container.Bind(tmpdirInst, "/tmp", sandbox.BindWritable)
}
/*
Passwd database
*/
- // look up shell
- sh := "/bin/sh"
- if s, ok := sys.LookupEnv(shell); ok {
- seal.container.SetEnv[shell] = s
- sh = s
- }
-
- // bind home directory
homeDir := "/var/empty"
if seal.user.home != "" {
homeDir = seal.user.home
@@ -292,27 +319,25 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
if seal.user.username != "" {
username = seal.user.username
}
- seal.container.Bind(seal.user.data, homeDir, false, true)
- seal.container.Chdir = homeDir
- seal.container.SetEnv["HOME"] = homeDir
- seal.container.SetEnv["USER"] = username
+ seal.container.Bind(seal.user.data, homeDir, sandbox.BindWritable)
+ seal.container.Dir = homeDir
+ seal.env["HOME"] = homeDir
+ seal.env["USER"] = username
- // generate /etc/passwd and /etc/group
- seal.container.CopyBind("/etc/passwd",
- []byte(username+":x:"+mapuid.String()+":"+mapuid.String()+":Fortify:"+homeDir+":"+sh+"\n"))
- seal.container.CopyBind("/etc/group",
- []byte("fortify:x:"+mapuid.String()+":\n"))
+ seal.container.Place("/etc/passwd",
+ []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Fortify:"+homeDir+":"+shellPath+"\n"))
+ seal.container.Place("/etc/group",
+ []byte("fortify:x:"+mapgid.String()+":\n"))
/*
Display servers
*/
- // pass $TERM to launcher
+ // pass $TERM for proper terminal I/O in shell
if t, ok := sys.LookupEnv(term); ok {
- seal.container.SetEnv[term] = t
+ seal.env[term] = t
}
- // set up wayland
if config.Confinement.Enablements.Has(system.EWayland) {
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath string
@@ -326,7 +351,7 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
}
innerPath := path.Join(innerRuntimeDir, wl.FallbackName)
- seal.container.SetEnv[wl.WaylandDisplay] = wl.FallbackName
+ seal.env[wl.WaylandDisplay] = wl.FallbackName
if !config.Confinement.Sandbox.DirectWayland { // set up security-context-v1
socketDir := path.Join(sc.SharePath, "wayland")
@@ -337,25 +362,23 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
// use instance ID in case app id is not set
appID = "uk.gensokyo.fortify." + seal.id.String()
}
- seal.sys.Wayland(&seal.bwrapSync, outerPath, socketPath, appID, seal.id.String())
- seal.container.Bind(outerPath, innerPath)
+ seal.sys.Wayland(&seal.sync, outerPath, socketPath, appID, seal.id.String())
+ seal.container.Bind(outerPath, innerPath, 0)
} else { // bind mount wayland socket (insecure)
fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION")
- seal.container.Bind(socketPath, innerPath)
+ seal.container.Bind(socketPath, innerPath, 0)
seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute)
}
}
- // set up X11
if config.Confinement.Enablements.Has(system.EX11) {
- // discover X11 and grant user permission via the `ChangeHosts` command
if d, ok := sys.LookupEnv(display); !ok {
return fmsg.WrapError(ErrXDisplay,
"DISPLAY is not set")
} else {
seal.sys.ChangeHosts("#" + seal.user.uid.String())
- seal.container.SetEnv[display] = d
- seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix")
+ seal.env[display] = d
+ seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix", 0)
}
}
@@ -396,8 +419,8 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
innerPulseRuntimeDir := path.Join(sharePathLocal, "pulse")
innerPulseSocket := path.Join(innerRuntimeDir, "pulse", "native")
seal.sys.Link(pulseSocket, innerPulseRuntimeDir)
- seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket)
- seal.container.SetEnv[pulseServer] = "unix:" + innerPulseSocket
+ seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0)
+ seal.env[pulseServer] = "unix:" + innerPulseSocket
// publish current user's pulse cookie for target user
if src, err := discoverPulseCookie(sys); err != nil {
@@ -405,9 +428,9 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
fmsg.Verbose(strings.TrimSpace(err.(*fmsg.BaseError).Message()))
} else {
innerDst := fst.Tmp + "/pulse-cookie"
- seal.container.SetEnv[pulseCookie] = innerDst
- payload := new([]byte)
- seal.container.CopyBindRef(innerDst, &payload)
+ seal.env[pulseCookie] = innerDst
+ var payload *[]byte
+ seal.container.PlaceP(innerDst, &payload)
seal.sys.CopyFile(payload, src, 256, 256)
}
}
@@ -437,13 +460,13 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
// share proxy sockets
sessionInner := path.Join(innerRuntimeDir, "bus")
- seal.container.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner
- seal.container.Bind(sessionPath, sessionInner)
+ seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner
+ seal.container.Bind(sessionPath, sessionInner, 0)
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
if config.Confinement.SystemBus != nil {
systemInner := "/run/dbus/system_bus_socket"
- seal.container.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner
- seal.container.Bind(systemPath, systemInner)
+ seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner
+ seal.container.Bind(systemPath, systemInner, 0)
seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write)
}
}
@@ -452,9 +475,8 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
Miscellaneous
*/
- // queue overriding tmpfs at the end of seal.container.Filesystem
- for _, dest := range config.Confinement.Sandbox.Override {
- seal.container.Tmpfs(dest, 8*1024)
+ for _, dest := range config.Confinement.Sandbox.Cover {
+ seal.container.Tmpfs(dest, 1<<13, 0755)
}
// append ExtraPerms last
@@ -480,12 +502,13 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
seal.sys.UpdatePermType(system.User, p.Path, perms...)
}
- // mount fortify in sandbox for init
- seal.container.Bind(sys.MustExecutable(), path.Join(fst.Tmp, "sbin/fortify"))
- seal.container.Symlink("fortify", path.Join(fst.Tmp, "sbin/init0"))
+ // flatten and sort env for deterministic behaviour
+ seal.container.Env = make([]string, 0, len(seal.env))
+ maps.All(seal.env)(func(k string, v string) bool { seal.container.Env = append(seal.container.Env, k+"="+v); return true })
+ slices.Sort(seal.container.Env)
- fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, command: %s",
- seal.user.uid, seal.user.username, config.Confinement.Groups, config.Command)
+ fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, argv: %s",
+ seal.user.uid, seal.user.username, config.Confinement.Groups, seal.container.Args)
return nil
}
diff --git a/internal/app/shim/main.go b/internal/app/shim/main.go
index 5634be4c..17f346a2 100644
--- a/internal/app/shim/main.go
+++ b/internal/app/shim/main.go
@@ -7,18 +7,26 @@ import (
"os"
"os/exec"
"os/signal"
- "path"
- "strconv"
"syscall"
+ "time"
- "git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper"
"git.gensokyo.uk/security/fortify/internal"
- "git.gensokyo.uk/security/fortify/internal/app/init0"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/sandbox"
)
+const Env = "FORTIFY_SHIM"
+
+type Params struct {
+ // finalised container params
+ Container *sandbox.Params
+ // path to outer home directory
+ Home string
+
+ // verbosity pass through
+ Verbose bool
+}
+
// everything beyond this point runs as unconstrained target user
// proceed with caution!
@@ -27,17 +35,15 @@ func Main() {
// USE WITH CAUTION
fmsg.Prepare("shim")
- // setting this prevents ptrace
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
}
- // receive setup payload
var (
- payload Payload
+ params Params
closeSetup func() error
)
- if f, err := sandbox.Receive(Env, &payload, nil); err != nil {
+ if f, err := sandbox.Receive(Env, &params, nil); err != nil {
if errors.Is(err, sandbox.ErrInvalid) {
log.Fatal("invalid config descriptor")
}
@@ -45,32 +51,26 @@ func Main() {
log.Fatal("FORTIFY_SHIM not set")
}
- log.Fatalf("cannot decode shim setup payload: %v", err)
+ log.Fatalf("cannot receive shim setup params: %v", err)
} else {
- internal.InstallFmsg(payload.Verbose)
+ internal.InstallFmsg(params.Verbose)
closeSetup = f
}
- if payload.Bwrap == nil {
- log.Fatal("bwrap config not supplied")
- }
-
- // restore bwrap sync fd
- var syncFd *os.File
- if payload.Sync != nil {
- syncFd = os.NewFile(*payload.Sync, "sync")
+ if params.Container == nil || params.Container.Ops == nil {
+ log.Fatal("invalid container params")
}
// close setup socket
if err := closeSetup(); err != nil {
- log.Println("cannot close setup pipe:", err)
+ log.Printf("cannot close setup pipe: %v", err)
// not fatal
}
// ensure home directory as target user
- if s, err := os.Stat(payload.Home); err != nil {
+ if s, err := os.Stat(params.Home); err != nil {
if os.IsNotExist(err) {
- if err = os.Mkdir(payload.Home, 0700); err != nil {
+ if err = os.Mkdir(params.Home, 0700); err != nil {
log.Fatalf("cannot create home directory: %v", err)
}
} else {
@@ -79,72 +79,37 @@ func Main() {
// home directory is created, proceed
} else if !s.IsDir() {
- log.Fatalf("data path %q is not a directory", payload.Home)
+ log.Fatalf("path %q is not a directory", params.Home)
}
- var ic init0.Payload
-
- // resolve argv0
- ic.Argv = payload.Argv
- if len(ic.Argv) > 0 {
- // looked up from $PATH by parent
- ic.Argv0 = payload.Exec[1]
- } else {
- // no argv, look up shell instead
- var ok bool
- if payload.Bwrap.SetEnv == nil {
- log.Fatal("no command was specified and environment is unset")
- }
- if ic.Argv0, ok = payload.Bwrap.SetEnv["SHELL"]; !ok {
- log.Fatal("no command was specified and $SHELL was unset")
- }
-
- ic.Argv = []string{ic.Argv0}
+ var name string
+ if len(params.Container.Args) > 0 {
+ name = params.Container.Args[0]
}
-
- conf := payload.Bwrap
-
- var extraFiles []*os.File
-
- // serve setup payload
- if fd, encoder, err := sandbox.Setup(&extraFiles); err != nil {
- log.Fatalf("cannot pipe: %v", err)
- } else {
- conf.SetEnv[init0.Env] = strconv.Itoa(fd)
- go func() {
- fmsg.Verbose("transmitting config to init")
- if err = encoder.Encode(&ic); err != nil {
- log.Fatalf("cannot transmit init config: %v", err)
- }
- }()
- }
-
- helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
-
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop() // unreachable
- if b, err := helper.NewBwrap(
- ctx, path.Join(fst.Tmp, "sbin/init0"),
- nil, false,
- func(int, int) []string { return make([]string, 0) },
- func(cmd *exec.Cmd) { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr },
- extraFiles,
- conf, syncFd,
- ); err != nil {
- log.Fatalf("malformed sandbox config: %v", err)
- } else {
- // run and pass through exit code
- if err = b.Start(); err != nil {
- log.Fatalf("cannot start target process: %v", err)
- } else if err = b.Wait(); err != nil {
- var exitError *exec.ExitError
- if !errors.As(err, &exitError) {
- log.Printf("wait: %v", err)
- internal.Exit(127)
- panic("unreachable")
+ container := sandbox.New(ctx, name)
+ container.Params = *params.Container
+ container.Stdin, container.Stdout, container.Stderr = os.Stdin, os.Stdout, os.Stderr
+ container.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
+ container.WaitDelay = 2 * time.Second
+
+ if err := container.Start(); err != nil {
+ fmsg.PrintBaseError(err, "cannot start container:")
+ os.Exit(1)
+ }
+ if err := container.Serve(); err != nil {
+ fmsg.PrintBaseError(err, "cannot configure container:")
+ }
+ if err := container.Wait(); err != nil {
+ var exitError *exec.ExitError
+ if !errors.As(err, &exitError) {
+ if errors.Is(err, context.Canceled) {
+ os.Exit(2)
}
- internal.Exit(exitError.ExitCode())
- panic("unreachable")
+ log.Printf("wait: %v", err)
+ os.Exit(127)
}
+ os.Exit(exitError.ExitCode())
}
}
diff --git a/internal/app/shim/payload.go b/internal/app/shim/payload.go
deleted file mode 100644
index e659e3fd..00000000
--- a/internal/app/shim/payload.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package shim
-
-import (
- "git.gensokyo.uk/security/fortify/helper/bwrap"
-)
-
-const Env = "FORTIFY_SHIM"
-
-type Payload struct {
- // child full argv
- Argv []string
- // bwrap, target full exec path
- Exec [2]string
- // bwrap config
- Bwrap *bwrap.Config
- // path to outer home directory
- Home string
- // sync fd
- Sync *uintptr
-
- // verbosity pass through
- Verbose bool
-}
diff --git a/internal/app/shim/manager.go b/internal/app/shim/proc.go
index 4ed75e53..8c73e697 100644
--- a/internal/app/shim/manager.go
+++ b/internal/app/shim/proc.go
@@ -8,9 +8,9 @@ import (
"os/exec"
"strconv"
"strings"
+ "syscall"
"time"
- "git.gensokyo.uk/security/fortify/helper/proc"
"git.gensokyo.uk/security/fortify/internal"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/sandbox"
@@ -25,10 +25,11 @@ type Shim struct {
killFallback chan error
// monitor to shim encoder
encoder *gob.Encoder
- // bwrap --sync-fd value
- sync *uintptr
}
+func (s *Shim) Unwrap() *exec.Cmd { return s.cmd }
+func (s *Shim) Fallback() chan error { return s.killFallback }
+
func (s *Shim) String() string {
if s.cmd == nil {
return "(unused shim manager)"
@@ -36,21 +37,9 @@ func (s *Shim) String() string {
return s.cmd.String()
}
-func (s *Shim) Unwrap() *exec.Cmd {
- return s.cmd
-}
-
-func (s *Shim) WaitFallback() chan error {
- return s.killFallback
-}
-
func (s *Shim) Start(
- // string representation of application id
aid string,
- // string representation of supplementary group ids
supp []string,
- // bwrap --sync-fd
- syncFd *os.File,
) (*time.Time, error) {
// prepare user switcher invocation
fsuPath := internal.MustFsuPath()
@@ -76,12 +65,6 @@ func (s *Shim) Start(
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
s.cmd.Dir = "/"
- // pass sync fd if set
- if syncFd != nil {
- fd := proc.ExtraFile(s.cmd, syncFd)
- s.sync = &fd
- }
-
fmsg.Verbose("starting shim via fsu:", s.cmd)
// withhold messages to stderr
fmsg.Suspend()
@@ -90,10 +73,11 @@ func (s *Shim) Start(
"cannot start fsu:")
}
startTime := time.Now().UTC()
+
return &startTime, nil
}
-func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
+func (s *Shim) Serve(ctx context.Context, params *Params) error {
// kill shim if something goes wrong and an error is returned
s.killFallback = make(chan error, 1)
killShim := func() {
@@ -103,9 +87,8 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
}
defer func() { killShim() }()
- payload.Sync = s.sync
encodeErr := make(chan error)
- go func() { encodeErr <- s.encoder.Encode(payload) }()
+ go func() { encodeErr <- s.encoder.Encode(params) }()
select {
// encode return indicates setup completion
@@ -121,11 +104,11 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
case <-ctx.Done():
err := ctx.Err()
if errors.Is(err, context.Canceled) {
- return fmsg.WrapError(errors.New("shim setup canceled"),
+ return fmsg.WrapError(syscall.ECANCELED,
"shim setup canceled")
}
if errors.Is(err, context.DeadlineExceeded) {
- return fmsg.WrapError(errors.New("deadline exceeded waiting for shim"),
+ return fmsg.WrapError(syscall.ETIMEDOUT,
"deadline exceeded waiting for shim")
}
// unreachable
diff --git a/internal/state/state_test.go b/internal/state/state_test.go
index ee2a88cc..22fd3cd6 100644
--- a/internal/state/state_test.go
+++ b/internal/state/state_test.go
@@ -96,7 +96,7 @@ func testStore(t *testing.T, s state.Store) {
} else {
slices.Sort(aids)
want := []int{0, 1}
- if slices.Compare(aids, want) != 0 {
+ if !slices.Equal(aids, want) {
t.Fatalf("List() = %#v, want %#v", aids, want)
}
}
diff --git a/main.go b/main.go
index 0505e593..cd9489ae 100644
--- a/main.go
+++ b/main.go
@@ -20,7 +20,6 @@ import (
"git.gensokyo.uk/security/fortify/fst"
"git.gensokyo.uk/security/fortify/internal"
"git.gensokyo.uk/security/fortify/internal/app"
- "git.gensokyo.uk/security/fortify/internal/app/init0"
"git.gensokyo.uk/security/fortify/internal/app/shim"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/internal/state"
@@ -43,7 +42,6 @@ var std sys.State = new(sys.Std)
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
sandbox.TryArgv0(fmsg.Output{}, fmsg.Prepare, internal.InstallFmsg)
- init0.TryArgv0()
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
@@ -76,9 +74,7 @@ func buildCommand(out io.Writer) command.Command {
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output as JSON when applicable")
- // internal commands
c.Command("shim", command.UsageInternal, func([]string) error { shim.Main(); return errSuccess })
- c.Command("init", command.UsageInternal, func([]string) error { init0.Main(); return errSuccess })
c.Command("app", "Launch app defined by the specified config file", func(args []string) error {
if len(args) < 1 {
@@ -87,10 +83,9 @@ func buildCommand(out io.Writer) command.Command {
// config extraArgs...
config := tryPath(args[0])
- config.Command = append(config.Command, args[1:]...)
+ config.Args = append(config.Args, args[1:]...)
- // invoke app
- runApp(app.MustNew(std), config)
+ runApp(config)
panic("unreachable")
})
@@ -112,8 +107,8 @@ func buildCommand(out io.Writer) command.Command {
c.NewCommand("run", "Configure and start a permissive default sandbox", func(args []string) error {
// initialise config from flags
config := &fst.Config{
- ID: fid,
- Command: args,
+ ID: fid,
+ Args: args,
}
if aid < 0 || aid > 9999 {
@@ -199,7 +194,7 @@ func buildCommand(out io.Writer) command.Command {
}
// invoke app
- runApp(app.MustNew(std), config)
+ runApp(config)
panic("unreachable")
}).
Flag(&dbusConfigSession, "dbus-config", command.StringFlag("builtin"),
@@ -279,10 +274,11 @@ func buildCommand(out io.Writer) command.Command {
return c
}
-func runApp(a fst.App, config *fst.Config) {
+func runApp(config *fst.Config) {
ctx, stop := signal.NotifyContext(context.Background(),
syscall.SIGINT, syscall.SIGTERM)
defer stop() // unreachable
+ a := app.MustNew(ctx, std)
rs := new(fst.RunState)
if sa, err := a.Seal(config); err != nil {
@@ -290,7 +286,7 @@ func runApp(a fst.App, config *fst.Config) {
rs.ExitCode = 1
} else {
// this updates ExitCode
- app.PrintRunStateErr(rs, sa.Run(ctx, rs))
+ app.PrintRunStateErr(rs, sa.Run(rs))
}
internal.Exit(rs.ExitCode)
}
diff --git a/nixos.nix b/nixos.nix
index 37ef5563..2c5b5db9 100644
--- a/nixos.nix
+++ b/nixos.nix
@@ -86,12 +86,11 @@ in
enablements = with app.capability; (if wayland then 1 else 0) + (if x11 then 2 else 0) + (if dbus then 4 else 0) + (if pulse then 8 else 0);
conf = {
inherit (app) id;
- command = [
- (pkgs.writeScript "${app.name}-start" ''
- #!${pkgs.zsh}${pkgs.zsh.shellPath}
- ${script}
- '')
- ];
+ path = pkgs.writeScript "${app.name}-start" ''
+ #!${pkgs.zsh}${pkgs.zsh.shellPath}
+ ${script}
+ '';
+ args = [ "${app.name}-start" ];
confinement = {
app_id = aid;
inherit (app) groups;
@@ -99,17 +98,15 @@ in
home = getsubhome fid aid;
sandbox = {
inherit (app)
+ devel
userns
net
dev
+ tty
+ multiarch
env
;
- syscall = {
- inherit (app) compat multiarch bluetooth;
- deny_devel = !app.devel;
- };
map_real_uid = app.mapRealUid;
- no_new_session = app.tty;
direct_wayland = app.insecureWayland;
filesystem =
let
@@ -149,7 +146,7 @@ in
]
++ app.extraPaths;
auto_etc = true;
- override = [ "/var/run/nscd" ];
+ cover = [ "/var/run/nscd" ];
};
inherit enablements;
inherit (dbusConfig) session_bus system_bus;
diff --git a/options.nix b/options.nix
index d9ccfb7a..9e367732 100644
--- a/options.nix
+++ b/options.nix
@@ -148,21 +148,19 @@ in
'';
};
- nix = mkEnableOption "nix daemon";
- userns = mkEnableOption "user namespace";
- mapRealUid = mkEnableOption "mapping to priv-user uid";
- dev = mkEnableOption "access to all devices";
+ devel = mkEnableOption "debugging-related kernel interfaces";
+ userns = mkEnableOption "user namespace creation";
tty = mkEnableOption "access to the controlling terminal";
- insecureWayland = mkEnableOption "direct access to the Wayland socket";
+ multiarch = mkEnableOption "multiarch kernel-level support";
net = mkEnableOption "network access" // {
default = true;
};
- compat = mkEnableOption "disable syscall filter extensions";
- devel = mkEnableOption "development kernel APIs";
- multiarch = mkEnableOption "multiarch kernel support";
- bluetooth = mkEnableOption "AF_BLUETOOTH socket operations";
+ nix = mkEnableOption "nix daemon access";
+ mapRealUid = mkEnableOption "mapping to priv-user uid";
+ dev = mkEnableOption "access to all devices";
+ insecureWayland = mkEnableOption "direct access to the Wayland socket";
gpu = mkOption {
type = nullOr bool;
diff --git a/print.go b/print.go
index 198136bf..7c6663bd 100644
--- a/print.go
+++ b/print.go
@@ -89,10 +89,10 @@ func printShowInstance(
flags = append(flags, name)
}
}
- writeFlag("userns", sandbox.UserNS)
+ writeFlag("userns", sandbox.Userns)
writeFlag("net", sandbox.Net)
writeFlag("dev", sandbox.Dev)
- writeFlag("tty", sandbox.NoNewSession)
+ writeFlag("tty", sandbox.Tty)
writeFlag("mapuid", sandbox.MapRealUID)
writeFlag("directwl", sandbox.DirectWayland)
writeFlag("autoetc", sandbox.AutoEtc)
@@ -107,14 +107,14 @@ func printShowInstance(
}
t.Printf(" Etc:\t%s\n", etc)
- if len(sandbox.Override) > 0 {
- t.Printf(" Overrides:\t%s\n", strings.Join(sandbox.Override, " "))
+ if len(sandbox.Cover) > 0 {
+ t.Printf(" Cover:\t%s\n", strings.Join(sandbox.Cover, " "))
}
// Env map[string]string `json:"env"`
// Link [][2]string `json:"symlink"`
}
- t.Printf(" Command:\t%s\n", strings.Join(config.Command, " "))
+ t.Printf(" Command:\t%s\n", strings.Join(config.Args, " "))
t.Printf("\n")
if !short {
@@ -256,7 +256,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
)
if e.Config != nil {
es = e.Config.Confinement.Enablements.String()
- cs = fmt.Sprintf("%q", e.Config.Command)
+ cs = fmt.Sprintf("%q", e.Config.Args)
as = strconv.Itoa(e.Config.Confinement.AppID)
}
t.Printf("\t%s\t%d\t%s\t%s\t%s\t%s\n",
diff --git a/print_test.go b/print_test.go
index 9e837225..3c708997 100644
--- a/print_test.go
+++ b/print_test.go
@@ -43,7 +43,7 @@ func Test_printShowInstance(t *testing.T) {
Hostname: "localhost"
Flags: userns net dev tty mapuid autoetc
Etc: /etc
- Overrides: /var/run/nscd
+ Cover: /var/run/nscd
Command: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem
@@ -127,7 +127,7 @@ App
Hostname: "localhost"
Flags: userns net dev tty mapuid autoetc
Etc: /etc
- Overrides: /var/run/nscd
+ Cover: /var/run/nscd
Command: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem
@@ -192,7 +192,8 @@ App
"pid": 3735928559,
"config": {
"id": "org.chromium.Chromium",
- "command": [
+ "path": "/run/current-system/sw/bin/chromium",
+ "args": [
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
@@ -209,24 +210,19 @@ App
"home": "/var/lib/persist/home/org.chromium.Chromium",
"sandbox": {
"hostname": "localhost",
+ "seccomp": 32,
+ "devel": true,
"userns": true,
"net": true,
- "dev": true,
- "syscall": {
- "compat": false,
- "deny_devel": true,
- "multiarch": true,
- "linux32": false,
- "can": false,
- "bluetooth": false
- },
- "no_new_session": true,
- "map_real_uid": 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,
+ "dev": true,
"filesystem": [
{
"src": "/nix/store"
@@ -259,7 +255,7 @@ App
],
"etc": "/etc",
"auto_etc": true,
- "override": [
+ "cover": [
"/var/run/nscd"
]
},
@@ -320,7 +316,8 @@ App
`},
{"json config", nil, fst.Template(), false, true, `{
"id": "org.chromium.Chromium",
- "command": [
+ "path": "/run/current-system/sw/bin/chromium",
+ "args": [
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
@@ -337,24 +334,19 @@ App
"home": "/var/lib/persist/home/org.chromium.Chromium",
"sandbox": {
"hostname": "localhost",
+ "seccomp": 32,
+ "devel": true,
"userns": true,
"net": true,
- "dev": true,
- "syscall": {
- "compat": false,
- "deny_devel": true,
- "multiarch": true,
- "linux32": false,
- "can": false,
- "bluetooth": false
- },
- "no_new_session": true,
- "map_real_uid": 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,
+ "dev": true,
"filesystem": [
{
"src": "/nix/store"
@@ -387,7 +379,7 @@ App
],
"etc": "/etc",
"auto_etc": true,
- "override": [
+ "cover": [
"/var/run/nscd"
]
},
@@ -506,7 +498,8 @@ func Test_printPs(t *testing.T) {
"pid": 3735928559,
"config": {
"id": "org.chromium.Chromium",
- "command": [
+ "path": "/run/current-system/sw/bin/chromium",
+ "args": [
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
@@ -523,24 +516,19 @@ func Test_printPs(t *testing.T) {
"home": "/var/lib/persist/home/org.chromium.Chromium",
"sandbox": {
"hostname": "localhost",
+ "seccomp": 32,
+ "devel": true,
"userns": true,
"net": true,
- "dev": true,
- "syscall": {
- "compat": false,
- "deny_devel": true,
- "multiarch": true,
- "linux32": false,
- "can": false,
- "bluetooth": false
- },
- "no_new_session": true,
- "map_real_uid": 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,
+ "dev": true,
"filesystem": [
{
"src": "/nix/store"
@@ -573,7 +561,7 @@ func Test_printPs(t *testing.T) {
],
"etc": "/etc",
"auto_etc": true,
- "override": [
+ "cover": [
"/var/run/nscd"
]
},
diff --git a/sandbox/seccomp/export_test.go b/sandbox/seccomp/export_test.go
index 5b23106f..761d0f85 100644
--- a/sandbox/seccomp/export_test.go
+++ b/sandbox/seccomp/export_test.go
@@ -93,7 +93,7 @@ func TestExport(t *testing.T) {
t.Errorf("Close: error = %v", err)
return
}
- if got := digest.Sum(nil); slices.Compare(got, tc.want) != 0 {
+ if got := digest.Sum(nil); !slices.Equal(got, tc.want) {
t.Fatalf("Export() hash = %x, want %x",
got, tc.want)
return
diff --git a/sandbox/sequential.go b/sandbox/sequential.go
index d6967035..d0729622 100644
--- a/sandbox/sequential.go
+++ b/sandbox/sequential.go
@@ -301,7 +301,15 @@ func (l *Symlink) apply(*Params) error {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", l[1]))
}
- if err := os.Symlink(l[0], toSysroot(l[1])); err != nil {
+
+ target := toSysroot(l[1])
+ if err := ensureFile(target, 0444, 0755); err != nil {
+ return err
+ }
+ if err := os.Remove(target); err != nil {
+ return msg.WrapErr(err, err.Error())
+ }
+ if err := os.Symlink(l[0], target); err != nil {
return msg.WrapErr(err, err.Error())
}
return nil
diff --git a/sandbox/syscall.go b/sandbox/syscall.go
index dd1679f0..d477dbc0 100644
--- a/sandbox/syscall.go
+++ b/sandbox/syscall.go
@@ -22,14 +22,6 @@ func SetDumpable(dumpable uintptr) error {
return nil
}
-func SetPdeathsig(sig syscall.Signal) error {
- if _, _, errno := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(sig), 0); errno != 0 {
- return errno
- }
-
- return nil
-}
-
// IgnoringEINTR makes a function call and repeats it if it returns an
// EINTR error. This appears to be required even though we install all
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
diff --git a/test/sandbox/case/mapuid.nix b/test/sandbox/case/mapuid.nix
index 11e1e733..634824bf 100644
--- a/test/sandbox/case/mapuid.nix
+++ b/test/sandbox/case/mapuid.nix
@@ -12,14 +12,9 @@
fs = fs "dead" {
".fortify" = fs "800001ed" {
etc = fs "800001ed" null null;
- sbin = fs "800001c0" {
- fortify = fs "16d" null null;
- init0 = fs "80001ff" null null;
- } null;
} null;
bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" {
- console = fs "4200190" null null;
core = fs "80001ff" null null;
dri = fs "800001ed" {
by-path = fs "800001ed" {
@@ -59,7 +54,7 @@
"fstab" = fs "80001ff" null null;
"fsurc" = fs "80001ff" null null;
"fuse.conf" = fs "80001ff" null null;
- "group" = fs "180" null "fortify:x:1000:\n";
+ "group" = fs "180" null "fortify:x:100:\n";
"host.conf" = fs "80001ff" null null;
"hostname" = fs "80001ff" null null;
"hosts" = fs "80001ff" null null;
@@ -84,7 +79,7 @@
"os-release" = fs "80001ff" null null;
"pam" = fs "80001ff" null null;
"pam.d" = fs "80001ff" null null;
- "passwd" = fs "180" null "u0_a3:x:1000:1000:Fortify:/var/lib/fortify/u0/a3:/run/current-system/sw/bin/bash\n";
+ "passwd" = fs "180" null "u0_a3:x:1000:100:Fortify:/var/lib/fortify/u0/a3:/run/current-system/sw/bin/bash\n";
"pipewire" = fs "80001ff" null null;
"pki" = fs "80001ff" null null;
"polkit-1" = fs "80001ff" null null;
@@ -185,10 +180,10 @@
} null;
mount = [
- (ent "/newroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000003,gid=1000003")
+ (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.fortify" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003")
- (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755,uid=1000003,gid=1000003")
+ (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000003,gid=1000003")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@@ -196,8 +191,7 @@
(ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,relatime" "mqueue" "mqueue" "rw")
+ (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
@@ -210,17 +204,16 @@
(ent ignore "/run/opengl-driver" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=1024k,mode=755,uid=1000003,gid=1000003")
+ (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000003,gid=1000003")
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000003,gid=1000003")
(ent "/tmp/fortify.1000/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a3" "/var/lib/fortify/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000003,gid=1000003")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000003,gid=1000003")
+ (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")
+ (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000003,gid=1000003")
(ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000003,gid=1000003")
- (ent ignore "/.fortify/sbin/fortify" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
];
seccomp = true;
diff --git a/test/sandbox/case/preset.nix b/test/sandbox/case/preset.nix
index ecb35e2b..c3ea564f 100644
--- a/test/sandbox/case/preset.nix
+++ b/test/sandbox/case/preset.nix
@@ -12,14 +12,9 @@
fs = fs "dead" {
".fortify" = fs "800001ed" {
etc = fs "800001ed" null null;
- sbin = fs "800001c0" {
- fortify = fs "16d" null null;
- init0 = fs "80001ff" null null;
- } null;
} null;
bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" {
- console = fs "4200190" null null;
core = fs "80001ff" null null;
dri = fs "800001ed" {
by-path = fs "800001ed" {
@@ -185,10 +180,10 @@
} null;
mount = [
- (ent "/newroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000001,gid=1000001")
+ (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.fortify" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
- (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755,uid=1000001,gid=1000001")
+ (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000001,gid=1000001")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@@ -196,8 +191,7 @@
(ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
- (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,relatime" "mqueue" "mqueue" "rw")
+ (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
@@ -210,17 +204,16 @@
(ent ignore "/run/opengl-driver" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=1024k,mode=755,uid=1000001,gid=1000001")
+ (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000001,gid=1000001")
(ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000001,gid=1000001")
(ent "/tmp/fortify.1000/tmpdir/1" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a1" "/var/lib/fortify/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000001,gid=1000001")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000001,gid=1000001")
+ (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
+ (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000001,gid=1000001")
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000001,gid=1000001")
- (ent ignore "/.fortify/sbin/fortify" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
];
seccomp = true;
diff --git a/test/sandbox/case/tty.nix b/test/sandbox/case/tty.nix
index 85320d4b..1f29e1d6 100644
--- a/test/sandbox/case/tty.nix
+++ b/test/sandbox/case/tty.nix
@@ -12,10 +12,6 @@
fs = fs "dead" {
".fortify" = fs "800001ed" {
etc = fs "800001ed" null null;
- sbin = fs "800001c0" {
- fortify = fs "16d" null null;
- init0 = fs "80001ff" null null;
- } null;
} null;
bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" {
@@ -185,10 +181,10 @@
} null;
mount = [
- (ent "/newroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000002,gid=1000002")
+ (ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.fortify" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
- (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755,uid=1000002,gid=1000002")
+ (ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000002,gid=1000002")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
@@ -197,7 +193,7 @@
(ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
(ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
- (ent "/" "/dev/mqueue" "rw,relatime" "mqueue" "mqueue" "rw")
+ (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
@@ -210,17 +206,16 @@
(ent ignore "/run/opengl-driver" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" "/.fortify/etc" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=1024k,mode=755,uid=1000002,gid=1000002")
+ (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000002,gid=1000002")
(ent "/" "/run/user/65534" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8192k,mode=755,uid=1000002,gid=1000002")
(ent "/tmp/fortify.1000/tmpdir/2" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/fortify/u0/a2" "/var/lib/fortify/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
- (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000002,gid=1000002")
- (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,uid=1000002,gid=1000002")
+ (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
+ (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000002,gid=1000002")
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000002,gid=1000002")
- (ent ignore "/.fortify/sbin/fortify" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
];
seccomp = true;
diff --git a/test/test.py b/test/test.py
index b3f8e9ed..e53d5921 100644
--- a/test/test.py
+++ b/test/test.py
@@ -62,9 +62,12 @@ def check_state(name, enablements):
config = instance['config']
- if len(config['command']) != 1 or not (config['command'][0].startswith("/nix/store/")) or not (
- config['command'][0].endswith(f"{name}-start")):
- raise Exception(f"unexpected command {instance['config']['command']}")
+ command = f"{name}-start"
+ if not (config['path'].startswith("/nix/store/")) or not (config['path'].endswith(command)):
+ raise Exception(f"unexpected path {config['path']}")
+
+ if len(config['args']) != 1 or config['args'][0] != command:
+ raise Exception(f"unexpected args {config['args']}")
if config['confinement']['enablements'] != enablements:
raise Exception(f"unexpected enablements {instance['config']['confinement']['enablements']}")