Compare commits

...

8 Commits

Author SHA1 Message Date
12be7bc78e
release: 0.3.3
All checks were successful
Release / Create release (push) Successful in 34s
Test / Sandbox (push) Successful in 35s
Test / Fortify (push) Successful in 2m47s
Test / Sandbox (race detector) (push) Successful in 3m0s
Test / Create distribution (push) Successful in 20s
Test / Fpkg (push) Successful in 4m12s
Test / Fortify (race detector) (push) Successful in 4m18s
Test / Flake checks (push) Successful in 1m20s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-04-01 01:42:10 +09:00
0ba8be659f
sandbox: document less obvious parts of setup
All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (push) Successful in 2m8s
Test / Fortify (push) Successful in 3m3s
Test / Sandbox (race detector) (push) Successful in 3m9s
Test / Fpkg (push) Successful in 4m22s
Test / Fortify (race detector) (push) Successful in 4m37s
Test / Flake checks (push) Successful in 1m19s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-04-01 01:21:04 +09:00
022242a84a
app: wayland socket in process share
All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (push) Successful in 1m9s
Test / Fortify (push) Successful in 2m16s
Test / Sandbox (race detector) (push) Successful in 3m8s
Test / Fpkg (push) Successful in 3m35s
Test / Fortify (race detector) (push) Successful in 4m32s
Test / Flake checks (push) Successful in 1m24s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-04-01 00:53:04 +09:00
8aeb06f53c
app: share path setup on demand
All checks were successful
Test / Create distribution (push) Successful in 28s
Test / Sandbox (race detector) (push) Successful in 34s
Test / Sandbox (push) Successful in 34s
Test / Fpkg (push) Successful in 39s
Test / Fortify (push) Successful in 2m16s
Test / Fortify (race detector) (push) Successful in 2m58s
Test / Flake checks (push) Successful in 1m33s
This removes the unnecessary creation and destruction of share paths when none of the enablements making use of them are set.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-04-01 00:47:32 +09:00
4036da3b5c
fst: optional configured shell path
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 1m45s
Test / Fortify (push) Successful in 2m28s
Test / Sandbox (race detector) (push) Successful in 2m45s
Test / Fpkg (push) Successful in 3m32s
Test / Fortify (race detector) (push) Successful in 4m5s
Test / Flake checks (push) Successful in 1m2s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-03-31 21:27:31 +09:00
986105958c
fortify: update show output
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 1m48s
Test / Fortify (push) Successful in 2m31s
Test / Sandbox (race detector) (push) Successful in 2m49s
Test / Fpkg (push) Successful in 3m27s
Test / Fortify (race detector) (push) Successful in 4m8s
Test / Flake checks (push) Successful in 1m0s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-03-31 04:54:10 +09:00
ecdd4d8202
fortify: clean ps output
All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Sandbox (push) Successful in 1m40s
Test / Fortify (push) Successful in 2m34s
Test / Sandbox (race detector) (push) Successful in 2m52s
Test / Fpkg (push) Successful in 3m36s
Test / Fortify (race detector) (push) Successful in 4m5s
Test / Flake checks (push) Successful in 1m3s
This format never changed ever since it was added. It used to show everything there is in a process state but that is no longer true for a long time. This change cleans it up in favour of `fortify show` displaying extra information.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-03-31 04:41:08 +09:00
bdee0c3921
nix: update flake lock
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 5m6s
Test / Sandbox (race detector) (push) Successful in 5m12s
Test / Fortify (push) Successful in 6m5s
Test / Fortify (race detector) (push) Successful in 6m39s
Test / Fpkg (push) Successful in 9m53s
Test / Flake checks (push) Successful in 1m20s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-03-30 23:15:18 +09:00
15 changed files with 188 additions and 210 deletions

View File

@ -73,6 +73,7 @@ func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool
Username: "fortify", Username: "fortify",
Inner: path.Join("/data/data", app.ID), Inner: path.Join("/data/data", app.ID),
Outer: pathSet.homeDir, Outer: pathSet.homeDir,
Shell: shellPath,
Sandbox: &fst.SandboxConfig{ Sandbox: &fst.SandboxConfig{
Hostname: formatHostname(app.Name), Hostname: formatHostname(app.Name),
Devel: app.Devel, Devel: app.Devel,

View File

@ -34,6 +34,7 @@ func withNixDaemon(
Username: "fortify", Username: "fortify",
Inner: path.Join("/data/data", app.ID), Inner: path.Join("/data/data", app.ID),
Outer: pathSet.homeDir, Outer: pathSet.homeDir,
Shell: shellPath,
Sandbox: &fst.SandboxConfig{ Sandbox: &fst.SandboxConfig{
Hostname: formatHostname(app.Name) + "-" + action, Hostname: formatHostname(app.Name) + "-" + action,
Userns: true, // nix sandbox requires userns Userns: true, // nix sandbox requires userns
@ -72,6 +73,7 @@ func withCacheDir(
Username: "nixos", Username: "nixos",
Inner: path.Join("/data/data", app.ID, "cache"), Inner: path.Join("/data/data", app.ID, "cache"),
Outer: pathSet.cacheDir, // this also ensures cacheDir via shim Outer: pathSet.cacheDir, // this also ensures cacheDir via shim
Shell: shellPath,
Sandbox: &fst.SandboxConfig{ Sandbox: &fst.SandboxConfig{
Hostname: formatHostname(app.Name) + "-" + action, Hostname: formatHostname(app.Name) + "-" + action,
Seccomp: seccomp.FlagMultiarch, Seccomp: seccomp.FlagMultiarch,

12
flake.lock generated
View File

@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1742234739, "lastModified": 1742655702,
"narHash": "sha256-zFL6zsf/5OztR1NSNQF33dvS1fL/BzVUjabZq4qrtY4=", "narHash": "sha256-jbqlw4sPArFtNtA1s3kLg7/A4fzP4GLk9bGbtUJg0JQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "f6af7280a3390e65c2ad8fd059cdc303426cbd59", "rev": "0948aeedc296f964140d9429223c7e4a0702a1ff",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -23,11 +23,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1742512142, "lastModified": 1743231893,
"narHash": "sha256-8XfURTDxOm6+33swQJu/hx6xw1Tznl8vJJN5HwVqckg=", "narHash": "sha256-tpJsHMUPEhEnzySoQxx7+kA+KUtgWqvlcUBqROYNNt0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7105ae3957700a9646cc4b766f5815b23ed0c682", "rev": "c570c1f5304493cafe133b8d843c7c1c4a10d3a6",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -35,6 +35,8 @@ type ConfinementConfig struct {
Inner string `json:"home_inner"` Inner string `json:"home_inner"`
// home directory in init namespace // home directory in init namespace
Outer string `json:"home"` Outer string `json:"home"`
// absolute path to shell, empty for host shell
Shell string `json:"shell,omitempty"`
// abstract sandbox configuration // abstract sandbox configuration
Sandbox *SandboxConfig `json:"sandbox"` Sandbox *SandboxConfig `json:"sandbox"`
// extra acl ops, runs after everything else // extra acl ops, runs after everything else
@ -97,6 +99,7 @@ func Template() *Config {
Username: "chronos", Username: "chronos",
Outer: "/var/lib/persist/home/org.chromium.Chromium", Outer: "/var/lib/persist/home/org.chromium.Chromium",
Inner: "/var/lib/fortify", Inner: "/var/lib/fortify",
Shell: "/run/current-system/sw/bin/zsh",
Sandbox: &SandboxConfig{ Sandbox: &SandboxConfig{
Hostname: "localhost", Hostname: "localhost",
Devel: true, Devel: true,

View File

@ -56,15 +56,15 @@ var testCasesNixos = []sealTestCase{
}, },
system.New(1000001). system.New(1000001).
Ensure("/tmp/fortify.1971", 0711). Ensure("/tmp/fortify.1971", 0711).
Ensure("/run/user/1971/fortify", 0700).UpdatePermType(system.User, "/run/user/1971/fortify", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
Ephemeral(system.Process, "/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1", 0711).
Ephemeral(system.Process, "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute). Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir/1", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/1", acl.Read, acl.Write, acl.Execute). Ensure("/tmp/fortify.1971/tmpdir/1", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/1", acl.Read, acl.Write, acl.Execute).
Ensure("/run/user/1971/fortify", 0700).UpdatePermType(system.User, "/run/user/1971/fortify", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
UpdatePermType(system.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute). UpdatePermType(system.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute).
Ephemeral(system.Process, "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1", acl.Execute).
Link("/run/user/1971/pulse/native", "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse"). Link("/run/user/1971/pulse/native", "/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse").
CopyFile(nil, "/home/ophestra/xdg/config/pulse/cookie", 256, 256). CopyFile(nil, "/home/ophestra/xdg/config/pulse/cookie", 256, 256).
Ephemeral(system.Process, "/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1", 0711).
MustProxyDBus("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", &dbus.Config{ MustProxyDBus("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", &dbus.Config{
Talk: []string{ Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.Notifications",

View File

@ -28,10 +28,6 @@ var testCasesPd = []sealTestCase{
}, },
system.New(1000000). system.New(1000000).
Ensure("/tmp/fortify.1971", 0711). Ensure("/tmp/fortify.1971", 0711).
Ensure("/run/user/1971/fortify", 0700).UpdatePermType(system.User, "/run/user/1971/fortify", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
Ephemeral(system.Process, "/tmp/fortify.1971/4a450b6596d7bc15bd01780eb9a607ac", 0711).
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", 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), Ensure("/tmp/fortify.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute),
&sandbox.Params{ &sandbox.Params{
@ -207,14 +203,13 @@ var testCasesPd = []sealTestCase{
}, },
system.New(1000009). system.New(1000009).
Ensure("/tmp/fortify.1971", 0711). Ensure("/tmp/fortify.1971", 0711).
Ensure("/run/user/1971/fortify", 0700).UpdatePermType(system.User, "/run/user/1971/fortify", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
Ephemeral(system.Process, "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c", 0711).
Ephemeral(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute). Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute).
Ensure("/tmp/fortify.1971/tmpdir/9", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/9", acl.Read, acl.Write, acl.Execute). Ensure("/tmp/fortify.1971/tmpdir/9", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/9", acl.Read, acl.Write, acl.Execute).
Ensure("/tmp/fortify.1971/wayland", 0711). Ephemeral(system.Process, "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c", 0711).
Wayland(new(*os.File), "/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/1971/wayland-0", "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). Wayland(new(*os.File), "/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/1971/wayland-0", "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
Ensure("/run/user/1971/fortify", 0700).UpdatePermType(system.User, "/run/user/1971/fortify", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
Ephemeral(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c", acl.Execute).
Link("/run/user/1971/pulse/native", "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse"). Link("/run/user/1971/pulse/native", "/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse").
CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 256, 256). CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 256, 256).
MustProxyDBus("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", &dbus.Config{ MustProxyDBus("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", &dbus.Config{
@ -373,7 +368,7 @@ var testCasesPd = []sealTestCase{
Bind("/home/chronos", "/home/chronos", 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/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place("/etc/group", []byte("fortify:x:65534:\n")). Place("/etc/group", []byte("fortify:x:65534:\n")).
Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0", 0). Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/wayland", "/run/user/65534/wayland-0", 0).
Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native", 0). Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native", 0).
Place(fst.Tmp+"/pulse-cookie", nil). Place(fst.Tmp+"/pulse-cookie", nil).
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0). Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0).

View File

@ -85,6 +85,53 @@ type outcome struct {
f atomic.Bool f atomic.Bool
} }
// shareHost holds optional share directory state that must not be accessed directly
type shareHost struct {
// whether XDG_RUNTIME_DIR is used post fsu
useRuntimeDir bool
// process-specific directory in tmpdir, empty if unused
sharePath string
// process-specific directory in XDG_RUNTIME_DIR, empty if unused
runtimeSharePath string
seal *outcome
sc fst.Paths
}
// ensureRuntimeDir must be called if direct access to paths within XDG_RUNTIME_DIR is required
func (share *shareHost) ensureRuntimeDir() {
if share.useRuntimeDir {
return
}
share.useRuntimeDir = true
share.seal.sys.Ensure(share.sc.RunDirPath, 0700)
share.seal.sys.UpdatePermType(system.User, share.sc.RunDirPath, acl.Execute)
share.seal.sys.Ensure(share.sc.RuntimePath, 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset
share.seal.sys.UpdatePermType(system.User, share.sc.RuntimePath, acl.Execute)
}
// instance returns a process-specific share path within tmpdir
func (share *shareHost) instance() string {
if share.sharePath != "" {
return share.sharePath
}
share.sharePath = path.Join(share.sc.SharePath, share.seal.id.String())
share.seal.sys.Ephemeral(system.Process, share.sharePath, 0711)
return share.sharePath
}
// runtime returns a process-specific share path within XDG_RUNTIME_DIR
func (share *shareHost) runtime() string {
if share.runtimeSharePath != "" {
return share.runtimeSharePath
}
share.ensureRuntimeDir()
share.runtimeSharePath = path.Join(share.sc.RunDirPath, share.seal.id.String())
share.seal.sys.Ephemeral(system.Process, share.runtimeSharePath, 0700)
share.seal.sys.UpdatePerm(share.runtimeSharePath, acl.Execute)
return share.runtimeSharePath
}
// fsuUser stores post-fsu credentials and metadata // fsuUser stores post-fsu credentials and metadata
type fsuUser struct { type fsuUser struct {
// application id // application id
@ -109,11 +156,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
seal.ctx = ctx seal.ctx = ctx
shellPath := "/bin/sh"
if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) {
shellPath = s
}
{ {
// encode initial configuration for state tracking // encode initial configuration for state tracking
ct := new(bytes.Buffer) ct := new(bytes.Buffer)
@ -130,10 +172,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
fmt.Sprintf("aid %d out of range", config.Confinement.AppID)) fmt.Sprintf("aid %d out of range", config.Confinement.AppID))
} }
/*
Resolve post-fsu user state
*/
seal.user = fsuUser{ seal.user = fsuUser{
aid: newInt(config.Confinement.AppID), aid: newInt(config.Confinement.AppID),
data: config.Confinement.Outer, data: config.Confinement.Outer,
@ -169,9 +207,14 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
/* // this also falls back to host path if encountering an invalid path
Resolve initial container state if !path.IsAbs(config.Confinement.Shell) {
*/ config.Confinement.Shell = "/bin/sh"
if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) {
config.Confinement.Shell = s
}
}
// do not use the value of shell before this point
// permissive defaults // permissive defaults
if config.Confinement.Sandbox == nil { if config.Confinement.Sandbox == nil {
@ -186,7 +229,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
config.Path = p config.Path = p
} }
} else { } else {
config.Path = shellPath config.Path = config.Confinement.Shell
} }
} }
@ -256,39 +299,9 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
if seal.env == nil { if seal.env == nil {
seal.env = make(map[string]string, 1<<6) seal.env = make(map[string]string, 1<<6)
} }
seal.env[shell] = shellPath
} }
/* // inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as mapped uid
Initialise externals
*/
sc := sys.Paths()
seal.runDirPath = sc.RunDirPath
seal.sys = system.New(seal.user.uid.unwrap())
/*
Work directories
*/
// base fortify share path
seal.sys.Ensure(sc.SharePath, 0711)
// outer paths used by the main process
seal.sys.Ensure(sc.RunDirPath, 0700)
seal.sys.UpdatePermType(system.User, sc.RunDirPath, acl.Execute)
seal.sys.Ensure(sc.RuntimePath, 0700) // ensure this dir in case XDG_RUNTIME_DIR is unset
seal.sys.UpdatePermType(system.User, sc.RuntimePath, acl.Execute)
// outer process-specific share directory
sharePath := path.Join(sc.SharePath, seal.id.String())
seal.sys.Ephemeral(system.Process, sharePath, 0711)
// similar to share but within XDG_RUNTIME_DIR
sharePathLocal := path.Join(sc.RunDirPath, seal.id.String())
seal.sys.Ephemeral(system.Process, sharePathLocal, 0700)
seal.sys.UpdatePerm(sharePathLocal, acl.Execute)
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as post-fsu user
innerRuntimeDir := path.Join("/run/user", mapuid.String()) innerRuntimeDir := path.Join("/run/user", mapuid.String())
seal.container.Tmpfs("/run/user", 1<<12, 0755) seal.container.Tmpfs("/run/user", 1<<12, 0755)
seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0700) seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0700)
@ -296,44 +309,44 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.env[xdgSessionClass] = "user" seal.env[xdgSessionClass] = "user"
seal.env[xdgSessionType] = "tty" seal.env[xdgSessionType] = "tty"
// outer path for inner /tmp share := &shareHost{seal: seal, sc: sys.Paths()}
seal.runDirPath = share.sc.RunDirPath
seal.sys = system.New(seal.user.uid.unwrap())
{ {
tmpdir := path.Join(sc.SharePath, "tmpdir") seal.sys.Ensure(share.sc.SharePath, 0711)
tmpdir := path.Join(share.sc.SharePath, "tmpdir")
seal.sys.Ensure(tmpdir, 0700) seal.sys.Ensure(tmpdir, 0700)
seal.sys.UpdatePermType(system.User, tmpdir, acl.Execute) seal.sys.UpdatePermType(system.User, tmpdir, acl.Execute)
tmpdirInst := path.Join(tmpdir, seal.user.aid.String()) tmpdirInst := path.Join(tmpdir, seal.user.aid.String())
seal.sys.Ensure(tmpdirInst, 01700) seal.sys.Ensure(tmpdirInst, 01700)
seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute) seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
// mount inner /tmp from share so it shares persistence and storage behaviour of host /tmp
seal.container.Bind(tmpdirInst, "/tmp", sandbox.BindWritable) seal.container.Bind(tmpdirInst, "/tmp", sandbox.BindWritable)
} }
/* {
Passwd database homeDir := "/var/empty"
*/ if seal.user.home != "" {
homeDir = seal.user.home
}
username := "chronos"
if seal.user.username != "" {
username = seal.user.username
}
seal.container.Bind(seal.user.data, homeDir, sandbox.BindWritable)
seal.container.Dir = homeDir
seal.env["HOME"] = homeDir
seal.env["USER"] = username
seal.env[shell] = config.Confinement.Shell
homeDir := "/var/empty" seal.container.Place("/etc/passwd",
if seal.user.home != "" { []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Fortify:"+homeDir+":"+config.Confinement.Shell+"\n"))
homeDir = seal.user.home seal.container.Place("/etc/group",
[]byte("fortify:x:"+mapgid.String()+":\n"))
} }
username := "chronos"
if seal.user.username != "" {
username = seal.user.username
}
seal.container.Bind(seal.user.data, homeDir, sandbox.BindWritable)
seal.container.Dir = homeDir
seal.env["HOME"] = homeDir
seal.env["USER"] = username
seal.container.Place("/etc/passwd", // pass TERM for proper terminal I/O in initial process
[]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 for proper terminal I/O in shell
if t, ok := sys.LookupEnv(term); ok { if t, ok := sys.LookupEnv(term); ok {
seal.env[term] = t seal.env[term] = t
} }
@ -343,9 +356,9 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
var socketPath string var socketPath string
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok { if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
fmsg.Verbose(wl.WaylandDisplay + " is not set, assuming " + wl.FallbackName) fmsg.Verbose(wl.WaylandDisplay + " is not set, assuming " + wl.FallbackName)
socketPath = path.Join(sc.RuntimePath, wl.FallbackName) socketPath = path.Join(share.sc.RuntimePath, wl.FallbackName)
} else if !path.IsAbs(name) { } else if !path.IsAbs(name) {
socketPath = path.Join(sc.RuntimePath, name) socketPath = path.Join(share.sc.RuntimePath, name)
} else { } else {
socketPath = name socketPath = name
} }
@ -354,18 +367,18 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.env[wl.WaylandDisplay] = wl.FallbackName seal.env[wl.WaylandDisplay] = wl.FallbackName
if !config.Confinement.Sandbox.DirectWayland { // set up security-context-v1 if !config.Confinement.Sandbox.DirectWayland { // set up security-context-v1
socketDir := path.Join(sc.SharePath, "wayland")
outerPath := path.Join(socketDir, seal.id.String())
seal.sys.Ensure(socketDir, 0711)
appID := config.ID appID := config.ID
if appID == "" { if appID == "" {
// use instance ID in case app id is not set // use instance ID in case app id is not set
appID = "uk.gensokyo.fortify." + seal.id.String() appID = "uk.gensokyo.fortify." + seal.id.String()
} }
// downstream socket paths
outerPath := path.Join(share.instance(), "wayland")
seal.sys.Wayland(&seal.sync, outerPath, socketPath, appID, seal.id.String()) seal.sys.Wayland(&seal.sync, outerPath, socketPath, appID, seal.id.String())
seal.container.Bind(outerPath, innerPath, 0) seal.container.Bind(outerPath, innerPath, 0)
} else { // bind mount wayland socket (insecure) } else { // bind mount wayland socket (insecure)
fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION") fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION")
share.ensureRuntimeDir()
seal.container.Bind(socketPath, innerPath, 0) seal.container.Bind(socketPath, innerPath, 0)
seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute) seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute)
} }
@ -382,13 +395,9 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
/*
PulseAudio server and authentication
*/
if config.Confinement.Enablements&system.EPulse != 0 { if config.Confinement.Enablements&system.EPulse != 0 {
// PulseAudio runtime directory (usually `/run/user/%d/pulse`) // PulseAudio runtime directory (usually `/run/user/%d/pulse`)
pulseRuntimeDir := path.Join(sc.RuntimePath, "pulse") pulseRuntimeDir := path.Join(share.sc.RuntimePath, "pulse")
// PulseAudio socket (usually `/run/user/%d/pulse/native`) // PulseAudio socket (usually `/run/user/%d/pulse/native`)
pulseSocket := path.Join(pulseRuntimeDir, "native") pulseSocket := path.Join(pulseRuntimeDir, "native")
@ -416,7 +425,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
// hard link pulse socket into target-executable share // hard link pulse socket into target-executable share
innerPulseRuntimeDir := path.Join(sharePathLocal, "pulse") innerPulseRuntimeDir := path.Join(share.runtime(), "pulse")
innerPulseSocket := path.Join(innerRuntimeDir, "pulse", "native") innerPulseSocket := path.Join(innerRuntimeDir, "pulse", "native")
seal.sys.Link(pulseSocket, innerPulseRuntimeDir) seal.sys.Link(pulseSocket, innerPulseRuntimeDir)
seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0) seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0)
@ -435,10 +444,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
/*
D-Bus proxy
*/
if config.Confinement.Enablements&system.EDBus != 0 { if config.Confinement.Enablements&system.EDBus != 0 {
// ensure dbus session bus defaults // ensure dbus session bus defaults
if config.Confinement.SessionBus == nil { if config.Confinement.SessionBus == nil {
@ -446,6 +451,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
// downstream socket paths // downstream socket paths
sharePath := share.instance()
sessionPath, systemPath := path.Join(sharePath, "bus"), path.Join(sharePath, "system_bus_socket") sessionPath, systemPath := path.Join(sharePath, "bus"), path.Join(sharePath, "system_bus_socket")
// configure dbus proxy // configure dbus proxy
@ -471,10 +477,6 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
/*
Miscellaneous
*/
for _, dest := range config.Confinement.Sandbox.Cover { for _, dest := range config.Confinement.Sandbox.Cover {
seal.container.Tmpfs(dest, 1<<13, 0755) seal.container.Tmpfs(dest, 1<<13, 0755)
} }

View File

@ -35,7 +35,7 @@ package
*Default:* *Default:*
` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.2> ` ` <derivation fortify-static-x86_64-unknown-linux-musl-0.3.3> `
@ -644,7 +644,7 @@ package
*Default:* *Default:*
` <derivation fortify-fsu-0.3.2> ` ` <derivation fortify-fsu-0.3.3> `

View File

@ -31,7 +31,7 @@
buildGoModule rec { buildGoModule rec {
pname = "fortify"; pname = "fortify";
version = "0.3.2"; version = "0.3.3";
src = builtins.path { src = builtins.path {
name = "${pname}-src"; name = "${pname}-src";

View File

@ -77,7 +77,9 @@ func printShowInstance(
if len(config.Confinement.Groups) > 0 { if len(config.Confinement.Groups) > 0 {
t.Printf(" Groups:\t%q\n", config.Confinement.Groups) t.Printf(" Groups:\t%q\n", config.Confinement.Groups)
} }
t.Printf(" Directory:\t%s\n", config.Confinement.Outer) if config.Confinement.Outer != "" {
t.Printf(" Directory:\t%s\n", config.Confinement.Outer)
}
if config.Confinement.Sandbox != nil { if config.Confinement.Sandbox != nil {
sandbox := config.Confinement.Sandbox sandbox := config.Confinement.Sandbox
if sandbox.Hostname != "" { if sandbox.Hostname != "" {
@ -114,7 +116,12 @@ func printShowInstance(
// Env map[string]string `json:"env"` // Env map[string]string `json:"env"`
// Link [][2]string `json:"symlink"` // Link [][2]string `json:"symlink"`
} }
t.Printf(" Command:\t%s\n", strings.Join(config.Args, " ")) if config.Confinement.Sandbox != nil {
t.Printf(" Path:\t%s\n", config.Path)
}
if len(config.Args) > 0 {
t.Printf(" Arguments:\t%s\n", strings.Join(config.Args, " "))
}
t.Printf("\n") t.Printf("\n")
if !short { if !short {
@ -247,22 +254,18 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
t := newPrinter(output) t := newPrinter(output)
defer t.MustFlush() defer t.MustFlush()
t.Println("\tInstance\tPID\tApp\tUptime\tEnablements\tCommand") t.Println("\tInstance\tPID\tApplication\tUptime")
for _, e := range exp { for _, e := range exp {
var ( as := "(No configuration information)"
es = "(No confinement information)"
cs = "(No command information)"
as = "(No configuration information)"
)
if e.Config != nil { if e.Config != nil {
es = e.Config.Confinement.Enablements.String()
cs = fmt.Sprintf("%q", e.Config.Args)
as = strconv.Itoa(e.Config.Confinement.AppID) as = strconv.Itoa(e.Config.Confinement.AppID)
if e.Config.ID != "" {
as += " (" + e.Config.ID + ")"
}
} }
t.Printf("\t%s\t%d\t%s\t%s\t%s\t%s\n", t.Printf("\t%s\t%d\t%s\t%s\n",
e.s[:8], e.PID, as, now.Sub(e.Time).Round(time.Second).String(), strings.TrimPrefix(es, ", "), cs) e.s[:8], e.PID, as, now.Sub(e.Time).Round(time.Second).String())
} }
t.Println()
} }
type expandedStateEntry struct { type expandedStateEntry struct {

View File

@ -44,7 +44,8 @@ func Test_printShowInstance(t *testing.T) {
Flags: userns net dev tty mapuid autoetc Flags: userns net dev tty mapuid autoetc
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
Command: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem Filesystem
+/nix/store +/nix/store
@ -75,26 +76,22 @@ System bus
App App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Directory:
Command:
`}, `},
{"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App {"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Directory:
Flags: none Flags: none
Etc: /etc Etc: /etc
Command: Path:
`}, `},
{"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App {"config nil entries", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: &fst.SandboxConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}}, false, false, `App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Directory:
Flags: none Flags: none
Etc: /etc Etc: /etc
Command: Path:
Filesystem Filesystem
@ -106,8 +103,6 @@ Extra ACL
App App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Directory:
Command:
Session bus Session bus
Filter: false Filter: false
@ -128,7 +123,8 @@ App
Flags: userns net dev tty mapuid autoetc Flags: userns net dev tty mapuid autoetc
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
Command: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
Filesystem Filesystem
+/nix/store +/nix/store
@ -163,8 +159,6 @@ State
App App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Directory:
Command:
`}, `},
@ -208,6 +202,7 @@ App
"username": "chronos", "username": "chronos",
"home_inner": "/var/lib/fortify", "home_inner": "/var/lib/fortify",
"home": "/var/lib/persist/home/org.chromium.Chromium", "home": "/var/lib/persist/home/org.chromium.Chromium",
"shell": "/run/current-system/sw/bin/zsh",
"sandbox": { "sandbox": {
"hostname": "localhost", "hostname": "localhost",
"seccomp": 32, "seccomp": 32,
@ -332,6 +327,7 @@ App
"username": "chronos", "username": "chronos",
"home_inner": "/var/lib/fortify", "home_inner": "/var/lib/fortify",
"home": "/var/lib/persist/home/org.chromium.Chromium", "home": "/var/lib/persist/home/org.chromium.Chromium",
"shell": "/run/current-system/sw/bin/zsh",
"sandbox": { "sandbox": {
"hostname": "localhost", "hostname": "localhost",
"seccomp": 32, "seccomp": 32,
@ -458,20 +454,16 @@ func Test_printPs(t *testing.T) {
short, json bool short, json bool
want string want string
}{ }{
{"no entries", make(state.Entries), false, false, ` Instance PID App Uptime Enablements Command {"no entries", make(state.Entries), false, false, ` Instance PID Application Uptime
`}, `},
{"no entries short", make(state.Entries), true, false, ``}, {"no entries short", make(state.Entries), true, false, ``},
{"nil instance", state.Entries{testID: nil}, false, false, ` Instance PID App Uptime Enablements Command {"nil instance", state.Entries{testID: nil}, false, false, ` Instance PID Application Uptime
`}, `},
{"state corruption", state.Entries{fst.ID{}: testState}, false, false, ` Instance PID App Uptime Enablements Command {"state corruption", state.Entries{fst.ID{}: testState}, false, false, ` Instance PID Application Uptime
`}, `},
{"valid", state.Entries{testID: testState}, false, false, ` Instance PID App Uptime Enablements Command {"valid", state.Entries{testID: testState}, false, false, ` Instance PID Application Uptime
8e2c76b0 3735928559 9 1h2m32s wayland, dbus, pulseaudio ["chromium" "--ignore-gpu-blocklist" "--disable-smooth-scrolling" "--enable-features=UseOzonePlatform" "--ozone-platform=wayland"] 8e2c76b0 3735928559 9 (org.chromium.Chromium) 1h2m32s
`}, `},
{"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0 {"valid short", state.Entries{testID: testState}, true, false, `8e2c76b0
`}, `},
@ -514,6 +506,7 @@ func Test_printPs(t *testing.T) {
"username": "chronos", "username": "chronos",
"home_inner": "/var/lib/fortify", "home_inner": "/var/lib/fortify",
"home": "/var/lib/persist/home/org.chromium.Chromium", "home": "/var/lib/persist/home/org.chromium.Chromium",
"shell": "/run/current-system/sw/bin/zsh",
"sandbox": { "sandbox": {
"hostname": "localhost", "hostname": "localhost",
"seccomp": 32, "seccomp": 32,

View File

@ -104,16 +104,6 @@ type (
Flags HardeningFlags Flags HardeningFlags
} }
Ops []Op
Op interface {
early(params *Params) error
apply(params *Params) error
prefix() string
Is(op Op) bool
fmt.Stringer
}
) )
func (p *Container) Start() error { func (p *Container) Start() error {

View File

@ -45,10 +45,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatal("this process must run as pid 1") log.Fatal("this process must run as pid 1")
} }
/*
receive setup payload
*/
var ( var (
params initParams params initParams
closeSetup func() error closeSetup func() error
@ -111,10 +107,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
// cache sysctl before pivot_root // cache sysctl before pivot_root
LastCap() LastCap()
/*
set up mount points from intermediate root
*/
if err := syscall.Mount("", "/", "", if err := syscall.Mount("", "/", "",
syscall.MS_SILENT|syscall.MS_SLAVE|syscall.MS_REC, syscall.MS_SILENT|syscall.MS_SLAVE|syscall.MS_REC,
""); err != nil { ""); err != nil {
@ -155,6 +147,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := os.Mkdir(hostDir, 0755); err != nil { if err := os.Mkdir(hostDir, 0755); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
// pivot_root uncovers basePath in hostDir
if err := syscall.PivotRoot(basePath, hostDir); err != nil { if err := syscall.PivotRoot(basePath, hostDir); err != nil {
log.Fatalf("cannot pivot into intermediate root: %v", err) log.Fatalf("cannot pivot into intermediate root: %v", err)
} }
@ -173,10 +166,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
} }
/* // setup requiring host root complete at this point
pivot to sysroot
*/
if err := syscall.Mount(hostDir, hostDir, "", if err := syscall.Mount(hostDir, hostDir, "",
syscall.MS_SILENT|syscall.MS_REC|syscall.MS_PRIVATE, syscall.MS_SILENT|syscall.MS_REC|syscall.MS_PRIVATE,
""); err != nil { ""); err != nil {
@ -216,10 +206,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
} }
/*
caps/securebits and seccomp filter
*/
if _, _, errno := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 { if _, _, errno := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 {
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno) log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno)
} }
@ -255,20 +241,13 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("cannot load syscall filter: %v", err) log.Fatalf("cannot load syscall filter: %v", err)
} }
/*
pass through extra files
*/
extraFiles := make([]*os.File, params.Count) extraFiles := make([]*os.File, params.Count)
for i := range extraFiles { for i := range extraFiles {
// setup fd is placed before all extra files
extraFiles[i] = os.NewFile(uintptr(offsetSetup+i), "extra file "+strconv.Itoa(i)) extraFiles[i] = os.NewFile(uintptr(offsetSetup+i), "extra file "+strconv.Itoa(i))
} }
syscall.Umask(oldmask) syscall.Umask(oldmask)
/*
prepare initial process
*/
cmd := exec.Command(params.Path) cmd := exec.Command(params.Path)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.Args = params.Args cmd.Args = params.Args
@ -281,22 +260,11 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
msg.Suspend() msg.Suspend()
/*
close setup pipe
*/
if err := closeSetup(); err != nil { if err := closeSetup(); err != nil {
log.Println("cannot close setup pipe:", err) log.Println("cannot close setup pipe:", err)
// not fatal // not fatal
} }
/*
perform init duties
*/
sig := make(chan os.Signal, 2)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
type winfo struct { type winfo struct {
wpid int wpid int
wstatus syscall.WaitStatus wstatus syscall.WaitStatus
@ -333,6 +301,10 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
close(done) close(done)
}() }()
// handle signals to dump withheld messages
sig := make(chan os.Signal, 2)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
// closed after residualProcessTimeout has elapsed after initial process death // closed after residualProcessTimeout has elapsed after initial process death
timeout := make(chan struct{}) timeout := make(chan struct{})
@ -345,7 +317,6 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} else { } else {
msg.Verbosef("terminating on %s", s.String()) msg.Verbosef("terminating on %s", s.String())
} }
msg.BeforeExit()
os.Exit(0) os.Exit(0)
case w := <-info: case w := <-info:
if w.wpid == cmd.Process.Pid { if w.wpid == cmd.Process.Pid {

View File

@ -13,6 +13,22 @@ import (
"unsafe" "unsafe"
) )
type (
Ops []Op
Op interface {
// early is called in host root.
early(params *Params) error
// apply is called in intermediate root.
apply(params *Params) error
prefix() string
Is(op Op) bool
fmt.Stringer
}
)
func (f *Ops) Grow(n int) { *f = slices.Grow(*f, n) }
func init() { gob.Register(new(BindMount)) } func init() { gob.Register(new(BindMount)) }
// BindMount bind mounts host path Source on container path Target. // BindMount bind mounts host path Source on container path Target.

View File

@ -176,25 +176,11 @@ machine.send_chars("clear; wayland-info && touch /tmp/client-ok\n")
machine.wait_for_file(tmpdir_path(0, "client-ok"), timeout=15) machine.wait_for_file(tmpdir_path(0, "client-ok"), timeout=15)
collect_state_ui("foot_wayland") collect_state_ui("foot_wayland")
check_state("ne-foot", 1) check_state("ne-foot", 1)
# Verify acl on XDG_RUNTIME_DIR: # Verify lack of acl on XDG_RUNTIME_DIR:
print(machine.succeed(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}")) machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}")
machine.send_chars("exit\n") machine.send_chars("exit\n")
machine.wait_until_fails("pgrep foot", timeout=5) machine.wait_until_fails("pgrep foot", timeout=5)
# Verify acl cleanup on XDG_RUNTIME_DIR: machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}", timeout=5)
machine.wait_until_fails(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {aid(0) + 1000000}", timeout=5)
# Start app (foot) with Wayland enablement from a terminal:
swaymsg("exec foot $SHELL -c '(ne-foot) & sleep 1 && fortify show $(fortify ps --short) && touch /tmp/ps-show-ok && cat'")
wait_for_window(f"u0_a{aid(0)}@machine")
machine.send_chars("clear; wayland-info && touch /tmp/term-ok\n")
machine.wait_for_file(tmpdir_path(0, "term-ok"), timeout=15)
machine.wait_for_file("/tmp/ps-show-ok", timeout=5)
collect_state_ui("foot_wayland_term")
check_state("ne-foot", 1)
machine.send_chars("exit\n")
wait_for_window("foot")
machine.send_key("ctrl-c")
machine.wait_until_fails("pgrep foot", timeout=5)
# Test PulseAudio (fortify does not support PipeWire yet): # Test PulseAudio (fortify does not support PipeWire yet):
swaymsg("exec pa-foot") swaymsg("exec pa-foot")
@ -233,6 +219,22 @@ machine.wait_until_fails(f"getfacl --absolute-names --omit-header --numeric /run
# Test syscall filter: # Test syscall filter:
print(machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 strace-failure")) print(machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 strace-failure"))
# Start app (foot) with Wayland enablement from a terminal:
swaymsg("exec foot $SHELL -c '(ne-foot) & disown && exec $SHELL'")
wait_for_window(f"u0_a{aid(0)}@machine")
machine.send_chars("clear; wayland-info && touch /tmp/term-ok\n")
machine.wait_for_file(tmpdir_path(0, "term-ok"), timeout=15)
machine.send_key("alt-h")
machine.send_chars("clear; fortify show $(fortify ps --short) && touch /tmp/ps-show-ok && exec cat\n")
machine.wait_for_file("/tmp/ps-show-ok", timeout=5)
collect_state_ui("foot_wayland_term")
check_state("ne-foot", 1)
machine.send_key("alt-l")
machine.send_chars("exit\n")
wait_for_window("alice@machine")
machine.send_key("ctrl-c")
machine.wait_until_fails("pgrep foot", timeout=5)
# Exit Sway and verify process exit status 0: # Exit Sway and verify process exit status 0:
swaymsg("exit", succeed=False) swaymsg("exit", succeed=False)
machine.wait_for_file("/tmp/sway-exit-ok") machine.wait_for_file("/tmp/sway-exit-ok")