fst: improve config
All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Sandbox (push) Successful in 1m50s
Test / Fortify (push) Successful in 2m46s
Test / Sandbox (race detector) (push) Successful in 2m59s
Test / Fortify (race detector) (push) Successful in 4m23s
Test / Fpkg (push) Successful in 5m25s
Test / Flake checks (push) Successful in 1m1s

The config struct more or less "grew" to what it is today. This change moves things around to make more sense and fixes nonsensical comments describing obsolete behaviour.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-04-13 03:23:28 +09:00
parent c460892cbd
commit 31b7ddd122
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
21 changed files with 833 additions and 831 deletions

View File

@ -19,7 +19,7 @@ type appInfo struct {
// passed through to [fst.Config] // passed through to [fst.Config]
ID string `json:"id"` ID string `json:"id"`
// passed through to [fst.Config] // passed through to [fst.Config]
AppID int `json:"app_id"` Identity int `json:"identity"`
// passed through to [fst.Config] // passed through to [fst.Config]
Groups []string `json:"groups,omitempty"` Groups []string `json:"groups,omitempty"`
// passed through to [fst.Config] // passed through to [fst.Config]
@ -64,57 +64,61 @@ type appInfo struct {
func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool) *fst.Config { func (app *appInfo) toFst(pathSet *appPathSet, argv []string, flagDropShell bool) *fst.Config {
config := &fst.Config{ config := &fst.Config{
ID: app.ID, ID: app.ID,
Path: argv[0], Path: argv[0],
Args: argv, Args: argv,
Confinement: fst.ConfinementConfig{
AppID: app.AppID, Enablements: app.Enablements,
Groups: app.Groups,
Username: "fortify", SystemBus: app.SystemBus,
Inner: path.Join("/data/data", app.ID), SessionBus: app.SessionBus,
Outer: pathSet.homeDir, DirectWayland: app.DirectWayland,
Shell: shellPath,
Sandbox: &fst.SandboxConfig{ Username: "fortify",
Hostname: formatHostname(app.Name), Shell: shellPath,
Devel: app.Devel, Data: pathSet.homeDir,
Userns: app.Userns, Dir: path.Join("/data/data", app.ID),
Net: app.Net,
Device: app.Device, Identity: app.Identity,
Tty: app.Tty || flagDropShell, Groups: app.Groups,
MapRealUID: app.MapRealUID,
DirectWayland: app.DirectWayland, Container: &fst.ContainerConfig{
Filesystem: []*fst.FilesystemConfig{ Hostname: formatHostname(app.Name),
{Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true}, Devel: app.Devel,
{Src: pathSet.metaPath, Dst: path.Join(fst.Tmp, "app"), Must: true}, Userns: app.Userns,
{Src: "/etc/resolv.conf"}, Net: app.Net,
{Src: "/sys/block"}, Device: app.Device,
{Src: "/sys/bus"}, Tty: app.Tty || flagDropShell,
{Src: "/sys/class"}, MapRealUID: app.MapRealUID,
{Src: "/sys/dev"}, Filesystem: []*fst.FilesystemConfig{
{Src: "/sys/devices"}, {Src: path.Join(pathSet.nixPath, "store"), Dst: "/nix/store", Must: true},
}, {Src: pathSet.metaPath, Dst: path.Join(fst.Tmp, "app"), Must: true},
Link: [][2]string{ {Src: "/etc/resolv.conf"},
{app.CurrentSystem, "/run/current-system"}, {Src: "/sys/block"},
{"/run/current-system/sw/bin", "/bin"}, {Src: "/sys/bus"},
{"/run/current-system/sw/bin", "/usr/bin"}, {Src: "/sys/class"},
}, {Src: "/sys/dev"},
Etc: path.Join(pathSet.cacheDir, "etc"), {Src: "/sys/devices"},
AutoEtc: true,
}, },
ExtraPerms: []*fst.ExtraPermConfig{ Link: [][2]string{
{Path: dataHome, Execute: true}, {app.CurrentSystem, "/run/current-system"},
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, {"/run/current-system/sw/bin", "/bin"},
{"/run/current-system/sw/bin", "/usr/bin"},
}, },
SystemBus: app.SystemBus, Etc: path.Join(pathSet.cacheDir, "etc"),
SessionBus: app.SessionBus, AutoEtc: true,
Enablements: app.Enablements, },
ExtraPerms: []*fst.ExtraPermConfig{
{Path: dataHome, Execute: true},
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true},
}, },
} }
if app.Multiarch { if app.Multiarch {
config.Confinement.Sandbox.Seccomp |= seccomp.FilterMultiarch config.Container.Seccomp |= seccomp.FilterMultiarch
} }
if app.Bluetooth { if app.Bluetooth {
config.Confinement.Sandbox.Seccomp |= seccomp.FilterBluetooth config.Container.Seccomp |= seccomp.FilterBluetooth
} }
return config return config
} }

View File

@ -31,7 +31,7 @@
'', '',
id ? name, id ? name,
app_id ? throw "app_id is required", identity ? throw "identity is required",
groups ? [ ], groups ? [ ],
userns ? false, userns ? false,
net ? true, net ? true,
@ -147,7 +147,7 @@ let
name name
version version
id id
app_id identity
launcher launcher
groups groups
userns userns

View File

@ -157,11 +157,11 @@ func main() {
return errSuccess return errSuccess
} }
// AppID determines uid // identity determines uid
if a.AppID != bundle.AppID { if a.Identity != bundle.Identity {
cleanup() cleanup()
log.Printf("package %q app id %d differs from installed %d", log.Printf("package %q identity %d differs from installed %d",
pkgPath, bundle.AppID, a.AppID) pkgPath, bundle.Identity, a.Identity)
return syscall.EBADE return syscall.EBADE
} }
@ -292,7 +292,7 @@ func main() {
"--override-input nixpkgs path:/etc/nixpkgs " + "--override-input nixpkgs path:/etc/nixpkgs " +
"path:" + a.NixGL + "#nixVulkanNvidia", "path:" + a.NixGL + "#nixVulkanNvidia",
}, true, func(config *fst.Config) *fst.Config { }, true, func(config *fst.Config) *fst.Config {
config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, []*fst.FilesystemConfig{ config.Container.Filesystem = append(config.Container.Filesystem, []*fst.FilesystemConfig{
{Src: "/etc/resolv.conf"}, {Src: "/etc/resolv.conf"},
{Src: "/sys/block"}, {Src: "/sys/block"},
{Src: "/sys/bus"}, {Src: "/sys/bus"},
@ -324,7 +324,7 @@ func main() {
*/ */
if a.GPU { if a.GPU {
config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, config.Container.Filesystem = append(config.Container.Filesystem,
&fst.FilesystemConfig{Src: path.Join(pathSet.nixPath, ".nixGL"), Dst: path.Join(fst.Tmp, "nixGL")}) &fst.FilesystemConfig{Src: path.Join(pathSet.nixPath, ".nixGL"), Dst: path.Join(fst.Tmp, "nixGL")})
appendGPUFilesystem(config) appendGPUFilesystem(config)
} }

View File

@ -72,7 +72,7 @@ func pathSetByApp(id string) *appPathSet {
} }
func appendGPUFilesystem(config *fst.Config) { func appendGPUFilesystem(config *fst.Config) {
config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, []*fst.FilesystemConfig{ config.Container.Filesystem = append(config.Container.Filesystem, []*fst.FilesystemConfig{
// flatpak commit 763a686d874dd668f0236f911de00b80766ffe79 // flatpak commit 763a686d874dd668f0236f911de00b80766ffe79
{Src: "/dev/dri", Device: true}, {Src: "/dev/dri", Device: true},
// mali // mali

View File

@ -10,7 +10,7 @@ buildPackage {
name = "foot"; name = "foot";
inherit (foot) version; inherit (foot) version;
app_id = 2; identity = 2;
id = "org.codeberg.dnkl.foot"; id = "org.codeberg.dnkl.foot";
modules = [ modules = [

View File

@ -65,8 +65,8 @@ def check_state(name, enablements):
if len(config['args']) != 1 or not (config['args'][0].startswith("/nix/store/")) or f"fortify-{name}-" not in (config['args'][0]): 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']}") raise Exception(f"unexpected args {instance['config']['args']}")
if config['confinement']['enablements'] != enablements: if config['enablements'] != enablements:
raise Exception(f"unexpected enablements {instance['config']['confinement']['enablements']}") raise Exception(f"unexpected enablements {instance['config']['enablements']}")
start_all() start_all()

View File

@ -16,7 +16,8 @@ func withNixDaemon(
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
) { ) {
mustRunAppDropShell(ctx, updateConfig(&fst.Config{ mustRunAppDropShell(ctx, updateConfig(&fst.Config{
ID: app.ID, ID: app.ID,
Path: shellPath, Path: shellPath,
Args: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + Args: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " +
// start nix-daemon // start nix-daemon
@ -29,33 +30,34 @@ func withNixDaemon(
// terminate nix-daemon // terminate nix-daemon
" && pkill nix-daemon", " && pkill nix-daemon",
}, },
Confinement: fst.ConfinementConfig{
AppID: app.AppID, Username: "fortify",
Username: "fortify", Shell: shellPath,
Inner: path.Join("/data/data", app.ID), Data: pathSet.homeDir,
Outer: pathSet.homeDir, Dir: path.Join("/data/data", app.ID),
Shell: shellPath, ExtraPerms: []*fst.ExtraPermConfig{
Sandbox: &fst.SandboxConfig{ {Path: dataHome, Execute: true},
Hostname: formatHostname(app.Name) + "-" + action, {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true},
Userns: true, // nix sandbox requires userns },
Net: net,
Seccomp: seccomp.FilterMultiarch, Identity: app.Identity,
Tty: dropShell,
Filesystem: []*fst.FilesystemConfig{ Container: &fst.ContainerConfig{
{Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true}, Hostname: formatHostname(app.Name) + "-" + action,
}, Userns: true, // nix sandbox requires userns
Link: [][2]string{ Net: net,
{app.CurrentSystem, "/run/current-system"}, Seccomp: seccomp.FilterMultiarch,
{"/run/current-system/sw/bin", "/bin"}, Tty: dropShell,
{"/run/current-system/sw/bin", "/usr/bin"}, Filesystem: []*fst.FilesystemConfig{
}, {Src: pathSet.nixPath, Dst: "/nix", Write: true, Must: true},
Etc: path.Join(pathSet.cacheDir, "etc"),
AutoEtc: true,
}, },
ExtraPerms: []*fst.ExtraPermConfig{ Link: [][2]string{
{Path: dataHome, Execute: true}, {app.CurrentSystem, "/run/current-system"},
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, {"/run/current-system/sw/bin", "/bin"},
{"/run/current-system/sw/bin", "/usr/bin"},
}, },
Etc: path.Join(pathSet.cacheDir, "etc"),
AutoEtc: true,
}, },
}), dropShell, beforeFail) }), dropShell, beforeFail)
} }
@ -65,36 +67,38 @@ func withCacheDir(
action string, command []string, workDir string, action string, command []string, workDir string,
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
mustRunAppDropShell(ctx, &fst.Config{ mustRunAppDropShell(ctx, &fst.Config{
ID: app.ID, ID: app.ID,
Path: shellPath, Path: shellPath,
Args: []string{shellPath, "-lc", strings.Join(command, " && ")}, Args: []string{shellPath, "-lc", strings.Join(command, " && ")},
Confinement: fst.ConfinementConfig{
AppID: app.AppID, Username: "nixos",
Username: "nixos", Shell: shellPath,
Inner: path.Join("/data/data", app.ID, "cache"), Data: pathSet.cacheDir, // this also ensures cacheDir via shim
Outer: pathSet.cacheDir, // this also ensures cacheDir via shim Dir: path.Join("/data/data", app.ID, "cache"),
Shell: shellPath, ExtraPerms: []*fst.ExtraPermConfig{
Sandbox: &fst.SandboxConfig{ {Path: dataHome, Execute: true},
Hostname: formatHostname(app.Name) + "-" + action, {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true},
Seccomp: seccomp.FilterMultiarch, {Path: workDir, Execute: true},
Tty: dropShell, },
Filesystem: []*fst.FilesystemConfig{
{Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true}, Identity: app.Identity,
{Src: workDir, Dst: path.Join(fst.Tmp, "bundle"), Must: true},
}, Container: &fst.ContainerConfig{
Link: [][2]string{ Hostname: formatHostname(app.Name) + "-" + action,
{app.CurrentSystem, "/run/current-system"}, Seccomp: seccomp.FilterMultiarch,
{"/run/current-system/sw/bin", "/bin"}, Tty: dropShell,
{"/run/current-system/sw/bin", "/usr/bin"}, Filesystem: []*fst.FilesystemConfig{
}, {Src: path.Join(workDir, "nix"), Dst: "/nix", Must: true},
Etc: path.Join(workDir, "etc"), {Src: workDir, Dst: path.Join(fst.Tmp, "bundle"), Must: true},
AutoEtc: true,
}, },
ExtraPerms: []*fst.ExtraPermConfig{ Link: [][2]string{
{Path: dataHome, Execute: true}, {app.CurrentSystem, "/run/current-system"},
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, {"/run/current-system/sw/bin", "/bin"},
{Path: workDir, Execute: true}, {"/run/current-system/sw/bin", "/usr/bin"},
}, },
Etc: path.Join(workDir, "etc"),
AutoEtc: true,
}, },
}, dropShell, beforeFail) }, dropShell, beforeFail)
} }

View File

@ -8,7 +8,7 @@ import (
const Tmp = "/.fortify" const Tmp = "/.fortify"
// Config is used to seal an app // Config is used to seal an app implementation.
type Config struct { type Config struct {
// reverse-DNS style arbitrary identifier string from config; // reverse-DNS style arbitrary identifier string from config;
// passed to wayland security-context-v1 as application ID // passed to wayland security-context-v1 as application ID
@ -20,39 +20,40 @@ type Config struct {
// final args passed to container init // final args passed to container init
Args []string `json:"args"` Args []string `json:"args"`
Confinement ConfinementConfig `json:"confinement"` // system services to make available in the container
} Enablements system.Enablement `json:"enablements"`
// session D-Bus proxy configuration;
// nil makes session bus proxy assume built-in defaults
SessionBus *dbus.Config `json:"session_bus,omitempty"`
// system D-Bus proxy configuration;
// nil disables system bus proxy
SystemBus *dbus.Config `json:"system_bus,omitempty"`
// 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"`
// ConfinementConfig defines fortified child's confinement
type ConfinementConfig struct {
// numerical application id, determines uid in the init namespace
AppID int `json:"app_id"`
// list of supplementary groups to inherit
Groups []string `json:"groups"`
// passwd username in container, 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"` Username string `json:"username,omitempty"`
// home directory in container, empty for outer
Inner string `json:"home_inner"`
// home directory in init namespace
Outer string `json:"home"`
// absolute path to shell, empty for host shell // absolute path to shell, empty for host shell
Shell string `json:"shell,omitempty"` Shell string `json:"shell,omitempty"`
// abstract sandbox configuration // absolute path to home directory in the init mount namespace
Sandbox *SandboxConfig `json:"sandbox"` Data string `json:"data"`
// extra acl ops, runs after everything else // directory to enter and use as home in the container mount namespace, empty for Data
Dir string `json:"dir"`
// extra acl ops, dispatches before container init
ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"` ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"`
// reference to a system D-Bus proxy configuration, // numerical application id, used for init user namespace credentials
// nil value disables system bus proxy Identity int `json:"identity"`
SystemBus *dbus.Config `json:"system_bus,omitempty"` // list of supplementary groups inherited by container processes
// reference to a session D-Bus proxy configuration, Groups []string `json:"groups"`
// nil value makes session bus proxy assume built-in defaults
SessionBus *dbus.Config `json:"session_bus,omitempty"`
// system resources to expose to the container // abstract container configuration baseline
Enablements system.Enablement `json:"enablements"` Container *ContainerConfig `json:"container"`
} }
// ExtraPermConfig describes an acl update op.
type ExtraPermConfig struct { type ExtraPermConfig struct {
Ensure bool `json:"ensure,omitempty"` Ensure bool `json:"ensure,omitempty"`
Path string `json:"path"` Path string `json:"path"`

View File

@ -4,9 +4,9 @@ import (
"git.gensokyo.uk/security/fortify/sandbox/seccomp" "git.gensokyo.uk/security/fortify/sandbox/seccomp"
) )
// SandboxConfig describes resources made available to the sandbox.
type ( type (
SandboxConfig struct { // ContainerConfig describes the container configuration baseline to which the app implementation adds upon.
ContainerConfig struct {
// container hostname // container hostname
Hostname string `json:"hostname,omitempty"` Hostname string `json:"hostname,omitempty"`
@ -18,7 +18,7 @@ type (
Userns bool `json:"userns,omitempty"` Userns bool `json:"userns,omitempty"`
// share host net namespace // share host net namespace
Net bool `json:"net,omitempty"` Net bool `json:"net,omitempty"`
// expose main process tty // allow dangerous terminal I/O
Tty bool `json:"tty,omitempty"` Tty bool `json:"tty,omitempty"`
// allow multiarch // allow multiarch
Multiarch bool `json:"multiarch,omitempty"` Multiarch bool `json:"multiarch,omitempty"`
@ -28,17 +28,13 @@ type (
// map target user uid to privileged user uid in the user namespace // map target user uid to privileged user uid in the user namespace
MapRealUID bool `json:"map_real_uid"` MapRealUID bool `json:"map_real_uid"`
// expose all devices // pass through all devices
Device bool `json:"device,omitempty"` Device bool `json:"device,omitempty"`
// container host filesystem bind mounts // container host filesystem bind mounts
Filesystem []*FilesystemConfig `json:"filesystem"` Filesystem []*FilesystemConfig `json:"filesystem"`
// create symlinks inside container filesystem // create symlinks inside container filesystem
Link [][2]string `json:"symlink"` Link [][2]string `json:"symlink"`
// 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 // read-only /etc directory
Etc string `json:"etc,omitempty"` Etc string `json:"etc,omitempty"`
// automatically set up /etc symlinks // automatically set up /etc symlinks
@ -47,7 +43,7 @@ type (
Cover []string `json:"cover"` Cover []string `json:"cover"`
} }
// FilesystemConfig is a representation of [sandbox.BindMount]. // FilesystemConfig is an abstract representation of a bind mount.
FilesystemConfig struct { FilesystemConfig struct {
// mount point in container, same as src if empty // mount point in container, same as src if empty
Dst string `json:"dst,omitempty"` Dst string `json:"dst,omitempty"`

View File

@ -9,7 +9,8 @@ import (
// Template returns a fully populated instance of Config. // Template returns a fully populated instance of Config.
func Template() *Config { func Template() *Config {
return &Config{ return &Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Path: "/run/current-system/sw/bin/chromium", Path: "/run/current-system/sw/bin/chromium",
Args: []string{ Args: []string{
"chromium", "chromium",
@ -18,70 +19,73 @@ func Template() *Config {
"--enable-features=UseOzonePlatform", "--enable-features=UseOzonePlatform",
"--ozone-platform=wayland", "--ozone-platform=wayland",
}, },
Confinement: ConfinementConfig{
AppID: 9, Enablements: system.EWayland | system.EDBus | system.EPulse,
Groups: []string{"video"},
Username: "chronos", SessionBus: &dbus.Config{
Outer: "/var/lib/persist/home/org.chromium.Chromium", See: nil,
Inner: "/var/lib/fortify", Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver",
Shell: "/run/current-system/sw/bin/zsh", "org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"},
Sandbox: &SandboxConfig{ Own: []string{"org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
Hostname: "localhost", "org.mpris.MediaPlayer2.chromium.*"},
Devel: true, Call: map[string]string{"org.freedesktop.portal.*": "*"},
Userns: true, Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
Net: true, Log: false,
Device: true, Filter: true,
Seccomp: seccomp.FilterMultiarch, },
Tty: true, SystemBus: &dbus.Config{
Multiarch: true, See: nil,
MapRealUID: true, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
DirectWayland: false, Own: nil,
// example API credentials pulled from Google Chrome Call: nil,
// DO NOT USE THESE IN A REAL BROWSER Broadcast: nil,
Env: map[string]string{ Log: false,
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", Filter: true,
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", },
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT", DirectWayland: false,
},
Filesystem: []*FilesystemConfig{ Username: "chronos",
{Src: "/nix/store"}, Shell: "/run/current-system/sw/bin/zsh",
{Src: "/run/current-system"}, Data: "/var/lib/fortify/u0/org.chromium.Chromium",
{Src: "/run/opengl-driver"}, Dir: "/data/data/org.chromium.Chromium",
{Src: "/var/db/nix-channels"}, ExtraPerms: []*ExtraPermConfig{
{Src: "/var/lib/fortify/u0/org.chromium.Chromium", {Path: "/var/lib/fortify/u0", Ensure: true, Execute: true},
Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true}, {Path: "/var/lib/fortify/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true},
{Src: "/dev/dri", Device: true}, },
},
Link: [][2]string{{"/run/user/65534", "/run/user/150"}}, Identity: 9,
Etc: "/etc", Groups: []string{"video", "dialout", "plugdev"},
AutoEtc: true,
Cover: []string{"/var/run/nscd"}, Container: &ContainerConfig{
Hostname: "localhost",
Devel: true,
Userns: true,
Net: true,
Device: true,
Seccomp: seccomp.FilterMultiarch,
Tty: true,
Multiarch: true,
MapRealUID: true,
// example API credentials pulled from Google Chrome
// DO NOT USE THESE IN A REAL BROWSER
Env: map[string]string{
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT",
}, },
ExtraPerms: []*ExtraPermConfig{ Filesystem: []*FilesystemConfig{
{Path: "/var/lib/fortify/u0", Ensure: true, Execute: true}, {Src: "/nix/store"},
{Path: "/var/lib/fortify/u0/org.chromium.Chromium", Read: true, Write: true, Execute: true}, {Src: "/run/current-system"},
{Src: "/run/opengl-driver"},
{Src: "/var/db/nix-channels"},
{Src: "/var/lib/fortify/u0/org.chromium.Chromium",
Dst: "/data/data/org.chromium.Chromium", Write: true, Must: true},
{Src: "/dev/dri", Device: true},
}, },
SystemBus: &dbus.Config{ Link: [][2]string{{"/run/user/65534", "/run/user/150"}},
See: nil, Etc: "/etc",
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, AutoEtc: true,
Own: nil, Cover: []string{"/var/run/nscd"},
Call: nil,
Broadcast: nil,
Log: false,
Filter: true,
},
SessionBus: &dbus.Config{
See: nil,
Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver",
"org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"},
Own: []string{"org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"},
Call: map[string]string{"org.freedesktop.portal.*": "*"},
Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"},
Log: false,
Filter: true,
},
Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
} }
} }

View File

@ -18,116 +18,116 @@ func TestTemplate(t *testing.T) {
"--enable-features=UseOzonePlatform", "--enable-features=UseOzonePlatform",
"--ozone-platform=wayland" "--ozone-platform=wayland"
], ],
"confinement": { "enablements": 13,
"app_id": 9, "session_bus": {
"groups": [ "see": null,
"video" "talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
], ],
"username": "chronos", "own": [
"home_inner": "/var/lib/fortify", "org.chromium.Chromium.*",
"home": "/var/lib/persist/home/org.chromium.Chromium", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"shell": "/run/current-system/sw/bin/zsh", "org.mpris.MediaPlayer2.chromium.*"
"sandbox": { ],
"hostname": "localhost", "call": {
"seccomp": 32, "org.freedesktop.portal.*": "*"
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{
"src": "/nix/store"
},
{
"src": "/run/current-system"
},
{
"src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
}
],
"symlink": [
[
"/run/user/65534",
"/run/user/150"
]
],
"etc": "/etc",
"auto_etc": true,
"cover": [
"/var/run/nscd"
]
}, },
"extra_perms": [ "broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"system_bus": {
"see": null,
"talk": [
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower"
],
"own": null,
"call": null,
"broadcast": null,
"filter": true
},
"username": "chronos",
"shell": "/run/current-system/sw/bin/zsh",
"data": "/var/lib/fortify/u0/org.chromium.Chromium",
"dir": "/data/data/org.chromium.Chromium",
"extra_perms": [
{
"ensure": true,
"path": "/var/lib/fortify/u0",
"x": true
},
{
"path": "/var/lib/fortify/u0/org.chromium.Chromium",
"r": true,
"w": true,
"x": true
}
],
"identity": 9,
"groups": [
"video",
"dialout",
"plugdev"
],
"container": {
"hostname": "localhost",
"seccomp": 32,
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{ {
"ensure": true, "src": "/nix/store"
"path": "/var/lib/fortify/u0",
"x": true
}, },
{ {
"path": "/var/lib/fortify/u0/org.chromium.Chromium", "src": "/run/current-system"
"r": true, },
"w": true, {
"x": true "src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
} }
], ],
"system_bus": { "symlink": [
"see": null, [
"talk": [ "/run/user/65534",
"org.bluez", "/run/user/150"
"org.freedesktop.Avahi", ]
"org.freedesktop.UPower" ],
], "etc": "/etc",
"own": null, "auto_etc": true,
"call": null, "cover": [
"broadcast": null, "/var/run/nscd"
"filter": true ]
},
"session_bus": {
"see": null,
"talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
],
"own": [
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"
],
"call": {
"org.freedesktop.portal.*": "*"
},
"broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"enablements": 13
} }
}` }`

View File

@ -16,9 +16,9 @@ import (
"git.gensokyo.uk/security/fortify/sandbox/seccomp" "git.gensokyo.uk/security/fortify/sandbox/seccomp"
) )
// NewContainer initialises [sandbox.Params] via [fst.SandboxConfig]. // NewContainer initialises [sandbox.Params] via [fst.ContainerConfig].
// Note that remaining container setup must be queued by the caller. // Note that remaining container setup must be queued by the caller.
func NewContainer(s *fst.SandboxConfig, os sys.State, uid, gid *int) (*sandbox.Params, map[string]string, error) { func NewContainer(s *fst.ContainerConfig, os sys.State, uid, gid *int) (*sandbox.Params, map[string]string, error) {
if s == nil { if s == nil {
return nil, nil, syscall.EBADE return nil, nil, syscall.EBADE
} }

View File

@ -13,41 +13,43 @@ var testCasesNixos = []sealTestCase{
{ {
"nixos chromium direct wayland", new(stubNixOS), "nixos chromium direct wayland", new(stubNixOS),
&fst.Config{ &fst.Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start", Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
Confinement: fst.ConfinementConfig{ Enablements: system.EWayland | system.EDBus | system.EPulse,
AppID: 1, Groups: []string{}, Username: "u0_a1",
Outer: "/var/lib/persist/module/fortify/0/1", Container: &fst.ContainerConfig{
Sandbox: &fst.SandboxConfig{ Userns: true, Net: true, MapRealUID: true, Env: nil, AutoEtc: true,
Userns: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true, Filesystem: []*fst.FilesystemConfig{
Filesystem: []*fst.FilesystemConfig{ {Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true}, {Src: "/nix/store", Must: true}, {Src: "/run/current-system", 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: "/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},
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
},
Cover: []string{"/var/run/nscd"},
}, },
SystemBus: &dbus.Config{ Cover: []string{"/var/run/nscd"},
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
Filter: true,
},
SessionBus: &dbus.Config{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
},
Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
SystemBus: &dbus.Config{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
Filter: true,
},
SessionBus: &dbus.Config{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
},
DirectWayland: true,
Username: "u0_a1",
Data: "/var/lib/persist/module/fortify/0/1",
Identity: 1, Groups: []string{},
}, },
app.ID{ app.ID{
0x8e, 0x2c, 0x76, 0xb0, 0x8e, 0x2c, 0x76, 0xb0,

View File

@ -14,13 +14,7 @@ import (
var testCasesPd = []sealTestCase{ var testCasesPd = []sealTestCase{
{ {
"nixos permissive defaults no enablements", new(stubNixOS), "nixos permissive defaults no enablements", new(stubNixOS),
&fst.Config{ &fst.Config{Username: "chronos", Data: "/home/chronos"},
Confinement: fst.ConfinementConfig{
AppID: 0,
Username: "chronos",
Outer: "/home/chronos",
},
},
app.ID{ app.ID{
0x4a, 0x45, 0x0b, 0x65, 0x4a, 0x45, 0x0b, 0x65,
0x96, 0xd7, 0xbc, 0x15, 0x96, 0xd7, 0xbc, 0x15,
@ -77,46 +71,44 @@ var testCasesPd = []sealTestCase{
{ {
"nixos permissive defaults chromium", new(stubNixOS), "nixos permissive defaults chromium", new(stubNixOS),
&fst.Config{ &fst.Config{
ID: "org.chromium.Chromium", ID: "org.chromium.Chromium",
Args: []string{"zsh", "-c", "exec chromium "}, Args: []string{"zsh", "-c", "exec chromium "},
Confinement: fst.ConfinementConfig{ Identity: 9,
AppID: 9, Groups: []string{"video"},
Groups: []string{"video"}, Username: "chronos",
Username: "chronos", Data: "/home/chronos",
Outer: "/home/chronos", SessionBus: &dbus.Config{
SessionBus: &dbus.Config{ Talk: []string{
Talk: []string{ "org.freedesktop.Notifications",
"org.freedesktop.Notifications", "org.freedesktop.FileManager1",
"org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.freedesktop.secrets", "org.kde.kwalletd5",
"org.kde.kwalletd5", "org.kde.kwalletd6",
"org.kde.kwalletd6", "org.gnome.SessionManager",
"org.gnome.SessionManager",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{
"org.freedesktop.portal.*": "*",
},
Broadcast: map[string]string{
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*",
},
Filter: true,
}, },
SystemBus: &dbus.Config{ Own: []string{
Talk: []string{ "org.chromium.Chromium.*",
"org.bluez", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.freedesktop.Avahi", "org.mpris.MediaPlayer2.chromium.*",
"org.freedesktop.UPower",
},
Filter: true,
}, },
Enablements: system.EWayland | system.EDBus | system.EPulse, Call: map[string]string{
"org.freedesktop.portal.*": "*",
},
Broadcast: map[string]string{
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*",
},
Filter: true,
}, },
SystemBus: &dbus.Config{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
},
Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
app.ID{ app.ID{
0xeb, 0xf0, 0x83, 0xd1, 0xeb, 0xf0, 0x83, 0xd1,

View File

@ -65,7 +65,7 @@ func (seal *outcome) Run(rs *RunState) error {
// accumulate enablements of remaining launchers // accumulate enablements of remaining launchers
for i, s := range states { for i, s := range states {
if s.Config != nil { if s.Config != nil {
rt |= s.Config.Confinement.Enablements rt |= s.Config.Enablements
} else { } else {
log.Printf("state entry %d does not contain config", i) log.Printf("state entry %d does not contain config", i)
} }

View File

@ -169,16 +169,16 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
// allowed aid range 0 to 9999, this is checked again in fsu // allowed aid range 0 to 9999, this is checked again in fsu
if config.Confinement.AppID < 0 || config.Confinement.AppID > 9999 { if config.Identity < 0 || config.Identity > 9999 {
return fmsg.WrapError(ErrUser, return fmsg.WrapError(ErrUser,
fmt.Sprintf("aid %d out of range", config.Confinement.AppID)) fmt.Sprintf("identity %d out of range", config.Identity))
} }
seal.user = fsuUser{ seal.user = fsuUser{
aid: newInt(config.Confinement.AppID), aid: newInt(config.Identity),
data: config.Confinement.Outer, data: config.Data,
home: config.Confinement.Inner, home: config.Dir,
username: config.Confinement.Username, username: config.Username,
} }
if seal.user.username == "" { if seal.user.username == "" {
seal.user.username = "chronos" seal.user.username = "chronos"
@ -199,8 +199,8 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} else { } else {
seal.user.uid = newInt(u) seal.user.uid = newInt(u)
} }
seal.user.supp = make([]string, len(config.Confinement.Groups)) seal.user.supp = make([]string, len(config.Groups))
for i, name := range config.Confinement.Groups { for i, name := range config.Groups {
if g, err := sys.LookupGroup(name); err != nil { if g, err := sys.LookupGroup(name); err != nil {
return fmsg.WrapError(err, return fmsg.WrapError(err,
fmt.Sprintf("unknown group %q", name)) fmt.Sprintf("unknown group %q", name))
@ -210,17 +210,17 @@ 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 // this also falls back to host path if encountering an invalid path
if !path.IsAbs(config.Confinement.Shell) { if !path.IsAbs(config.Shell) {
config.Confinement.Shell = "/bin/sh" config.Shell = "/bin/sh"
if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) { if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) {
config.Confinement.Shell = s config.Shell = s
} }
} }
// do not use the value of shell before this point // do not use the value of shell before this point
// permissive defaults // permissive defaults
if config.Confinement.Sandbox == nil { if config.Container == nil {
fmsg.Verbose("sandbox configuration not supplied, PROCEED WITH CAUTION") fmsg.Verbose("container configuration not supplied, PROCEED WITH CAUTION")
// fsu clears the environment so resolve paths early // fsu clears the environment so resolve paths early
if !path.IsAbs(config.Path) { if !path.IsAbs(config.Path) {
@ -231,11 +231,11 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
config.Path = p config.Path = p
} }
} else { } else {
config.Path = config.Confinement.Shell config.Path = config.Shell
} }
} }
conf := &fst.SandboxConfig{ conf := &fst.ContainerConfig{
Userns: true, Userns: true,
Net: true, Net: true,
Tty: true, Tty: true,
@ -268,20 +268,20 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
conf.Cover = append(conf.Cover, nscd) conf.Cover = append(conf.Cover, nscd)
} }
// bind GPU stuff // bind GPU stuff
if config.Confinement.Enablements&(system.EX11|system.EWayland) != 0 { if config.Enablements&(system.EX11|system.EWayland) != 0 {
conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true}) conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true})
} }
// opportunistically bind kvm // opportunistically bind kvm
conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/kvm", Device: true}) conf.Filesystem = append(conf.Filesystem, &fst.FilesystemConfig{Src: "/dev/kvm", Device: true})
config.Confinement.Sandbox = conf config.Container = conf
} }
var mapuid, mapgid *stringPair[int] var mapuid, mapgid *stringPair[int]
{ {
var uid, gid int var uid, gid int
var err error var err error
seal.container, seal.env, err = common.NewContainer(config.Confinement.Sandbox, sys, &uid, &gid) seal.container, seal.env, err = common.NewContainer(config.Container, sys, &uid, &gid)
if err != nil { if err != nil {
return fmsg.WrapErrorSuffix(err, return fmsg.WrapErrorSuffix(err,
"cannot initialise container configuration:") "cannot initialise container configuration:")
@ -303,12 +303,12 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
if !config.Confinement.Sandbox.AutoEtc { if !config.Container.AutoEtc {
if config.Confinement.Sandbox.Etc != "" { if config.Container.Etc != "" {
seal.container.Bind(config.Confinement.Sandbox.Etc, "/etc", 0) seal.container.Bind(config.Container.Etc, "/etc", 0)
} }
} else { } else {
etcPath := config.Confinement.Sandbox.Etc etcPath := config.Container.Etc
if etcPath == "" { if etcPath == "" {
etcPath = "/etc" etcPath = "/etc"
} }
@ -352,10 +352,10 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.container.Dir = homeDir seal.container.Dir = homeDir
seal.env["HOME"] = homeDir seal.env["HOME"] = homeDir
seal.env["USER"] = username seal.env["USER"] = username
seal.env[shell] = config.Confinement.Shell seal.env[shell] = config.Shell
seal.container.Place("/etc/passwd", seal.container.Place("/etc/passwd",
[]byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Fortify:"+homeDir+":"+config.Confinement.Shell+"\n")) []byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Fortify:"+homeDir+":"+config.Shell+"\n"))
seal.container.Place("/etc/group", seal.container.Place("/etc/group",
[]byte("fortify:x:"+mapgid.String()+":\n")) []byte("fortify:x:"+mapgid.String()+":\n"))
} }
@ -365,7 +365,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.env[term] = t seal.env[term] = t
} }
if config.Confinement.Enablements&system.EWayland != 0 { if config.Enablements&system.EWayland != 0 {
// outer wayland socket (usually `/run/user/%d/wayland-%d`) // outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath string var socketPath string
if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok { if name, ok := sys.LookupEnv(wl.WaylandDisplay); !ok {
@ -380,7 +380,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
innerPath := path.Join(innerRuntimeDir, wl.FallbackName) innerPath := path.Join(innerRuntimeDir, wl.FallbackName)
seal.env[wl.WaylandDisplay] = wl.FallbackName seal.env[wl.WaylandDisplay] = wl.FallbackName
if !config.Confinement.Sandbox.DirectWayland { // set up security-context-v1 if !config.DirectWayland { // set up security-context-v1
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
@ -398,7 +398,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
if config.Confinement.Enablements&system.EX11 != 0 { if config.Enablements&system.EX11 != 0 {
if d, ok := sys.LookupEnv(display); !ok { if d, ok := sys.LookupEnv(display); !ok {
return fmsg.WrapError(ErrXDisplay, return fmsg.WrapError(ErrXDisplay,
"DISPLAY is not set") "DISPLAY is not set")
@ -409,7 +409,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
if config.Confinement.Enablements&system.EPulse != 0 { if config.Enablements&system.EPulse != 0 {
// PulseAudio runtime directory (usually `/run/user/%d/pulse`) // PulseAudio runtime directory (usually `/run/user/%d/pulse`)
pulseRuntimeDir := path.Join(share.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`)
@ -458,10 +458,10 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
if config.Confinement.Enablements&system.EDBus != 0 { if config.Enablements&system.EDBus != 0 {
// ensure dbus session bus defaults // ensure dbus session bus defaults
if config.Confinement.SessionBus == nil { if config.SessionBus == nil {
config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true) config.SessionBus = dbus.NewConfig(config.ID, true, true)
} }
// downstream socket paths // downstream socket paths
@ -470,7 +470,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
// configure dbus proxy // configure dbus proxy
if f, err := seal.sys.ProxyDBus( if f, err := seal.sys.ProxyDBus(
config.Confinement.SessionBus, config.Confinement.SystemBus, config.SessionBus, config.SystemBus,
sessionPath, systemPath, sessionPath, systemPath,
); err != nil { ); err != nil {
return err return err
@ -483,7 +483,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner
seal.container.Bind(sessionPath, sessionInner, 0) seal.container.Bind(sessionPath, sessionInner, 0)
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write) seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
if config.Confinement.SystemBus != nil { if config.SystemBus != nil {
systemInner := "/run/dbus/system_bus_socket" systemInner := "/run/dbus/system_bus_socket"
seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner
seal.container.Bind(systemPath, systemInner, 0) seal.container.Bind(systemPath, systemInner, 0)
@ -491,12 +491,12 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
} }
} }
for _, dest := range config.Confinement.Sandbox.Cover { for _, dest := range config.Container.Cover {
seal.container.Tmpfs(dest, 1<<13, 0755) seal.container.Tmpfs(dest, 1<<13, 0755)
} }
// append ExtraPerms last // append ExtraPerms last
for _, p := range config.Confinement.ExtraPerms { for _, p := range config.ExtraPerms {
if p == nil { if p == nil {
continue continue
} }
@ -530,7 +530,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
slices.Sort(seal.container.Env) slices.Sort(seal.container.Env)
fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, argv: %s", 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) seal.user.uid, seal.user.username, config.Groups, seal.container.Args)
return nil return nil
} }

26
main.go
View File

@ -154,33 +154,33 @@ func buildCommand(out io.Writer) command.Command {
userName = passwd.Username userName = passwd.Username
} }
config.Confinement.AppID = aid config.Identity = aid
config.Confinement.Groups = groups config.Groups = groups
config.Confinement.Outer = homeDir config.Data = homeDir
config.Confinement.Username = userName config.Username = userName
if wayland { if wayland {
config.Confinement.Enablements |= system.EWayland config.Enablements |= system.EWayland
} }
if x11 { if x11 {
config.Confinement.Enablements |= system.EX11 config.Enablements |= system.EX11
} }
if dBus { if dBus {
config.Confinement.Enablements |= system.EDBus config.Enablements |= system.EDBus
} }
if pulse { if pulse {
config.Confinement.Enablements |= system.EPulse config.Enablements |= system.EPulse
} }
// parse D-Bus config file from flags if applicable // parse D-Bus config file from flags if applicable
if dBus { if dBus {
if dbusConfigSession == "builtin" { if dbusConfigSession == "builtin" {
config.Confinement.SessionBus = dbus.NewConfig(fid, true, mpris) config.SessionBus = dbus.NewConfig(fid, true, mpris)
} else { } else {
if conf, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil { if conf, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
log.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err) log.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
} else { } else {
config.Confinement.SessionBus = conf config.SessionBus = conf
} }
} }
@ -189,14 +189,14 @@ func buildCommand(out io.Writer) command.Command {
if conf, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil { if conf, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
log.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err) log.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
} else { } else {
config.Confinement.SystemBus = conf config.SystemBus = conf
} }
} }
// override log from configuration // override log from configuration
if dbusVerbose { if dbusVerbose {
config.Confinement.SessionBus.Log = true config.SessionBus.Log = true
config.Confinement.SystemBus.Log = true config.SystemBus.Log = true
} }
} }

147
nixos.nix
View File

@ -88,6 +88,7 @@ in
conf = { conf = {
inherit (app) id; inherit (app) id;
path = path =
if app.path == null then if app.path == null then
pkgs.writeScript "${app.name}-start" '' pkgs.writeScript "${app.name}-start" ''
@ -98,88 +99,90 @@ in
app.path; app.path;
args = if app.args == null then [ "${app.name}-start" ] else app.args; args = if app.args == null then [ "${app.name}-start" ] else app.args;
confinement = { inherit enablements;
app_id = aid;
inherit (app) groups;
username = getsubname fid aid;
home = getsubhome fid aid;
sandbox = {
inherit (app)
devel
userns
net
device
tty
multiarch
env
;
map_real_uid = app.mapRealUid;
direct_wayland = app.insecureWayland;
filesystem = inherit (dbusConfig) session_bus system_bus;
let direct_wayland = app.insecureWayland;
bind = src: { inherit src; };
mustBind = src: { username = getsubname fid aid;
inherit src; data = getsubhome fid aid;
require = true;
}; identity = aid;
devBind = src: { inherit (app) groups;
inherit src;
dev = true; container = {
}; inherit (app)
in devel
userns
net
device
tty
multiarch
env
;
map_real_uid = app.mapRealUid;
filesystem =
let
bind = src: { inherit src; };
mustBind = src: {
inherit src;
require = true;
};
devBind = src: {
inherit src;
dev = true;
};
in
[
(mustBind "/bin")
(mustBind "/usr/bin")
(mustBind "/nix/store")
(bind "/sys/block")
(bind "/sys/bus")
(bind "/sys/class")
(bind "/sys/dev")
(bind "/sys/devices")
]
++ optionals app.nix [
(mustBind "/nix/var")
(bind "/var/db/nix-channels")
]
++ optionals isGraphical [
(devBind "/dev/dri")
(devBind "/dev/nvidiactl")
(devBind "/dev/nvidia-modeset")
(devBind "/dev/nvidia-uvm")
(devBind "/dev/nvidia-uvm-tools")
(devBind "/dev/nvidia0")
]
++ app.extraPaths;
auto_etc = true;
cover = [ "/var/run/nscd" ];
symlink =
[
[ [
(mustBind "/bin") "*/run/current-system"
(mustBind "/usr/bin") "/run/current-system"
(mustBind "/nix/store")
(bind "/sys/block")
(bind "/sys/bus")
(bind "/sys/class")
(bind "/sys/dev")
(bind "/sys/devices")
] ]
++ optionals app.nix [ ]
(mustBind "/nix/var") ++ optionals (isGraphical && config.hardware.graphics.enable) (
(bind "/var/db/nix-channels")
]
++ optionals isGraphical [
(devBind "/dev/dri")
(devBind "/dev/nvidiactl")
(devBind "/dev/nvidia-modeset")
(devBind "/dev/nvidia-uvm")
(devBind "/dev/nvidia-uvm-tools")
(devBind "/dev/nvidia0")
]
++ app.extraPaths;
auto_etc = true;
cover = [ "/var/run/nscd" ];
symlink =
[ [
[ [
"*/run/current-system" config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver"."L+".argument
"/run/current-system" "/run/opengl-driver"
] ]
] ]
++ optionals (isGraphical && config.hardware.graphics.enable) ( ++ optionals (app.multiarch && config.hardware.graphics.enable32Bit) [
[ [
[ config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver-32"."L+".argument
config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver"."L+".argument /run/opengl-driver-32
"/run/opengl-driver"
]
] ]
++ optionals (app.multiarch && config.hardware.graphics.enable32Bit) [ ]
[ );
config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver-32"."L+".argument
/run/opengl-driver-32
]
]
);
};
inherit enablements;
inherit (dbusConfig) session_bus system_bus;
}; };
}; };
in in
pkgs.writeShellScriptBin app.name '' pkgs.writeShellScriptBin app.name ''

View File

@ -56,7 +56,7 @@ func printShowInstance(
t := newPrinter(output) t := newPrinter(output)
defer t.MustFlush() defer t.MustFlush()
if config.Confinement.Sandbox == nil { if config.Container == nil {
mustPrint(output, "Warning: this configuration uses permissive defaults!\n\n") mustPrint(output, "Warning: this configuration uses permissive defaults!\n\n")
} }
@ -69,21 +69,21 @@ func printShowInstance(
t.Printf("App\n") t.Printf("App\n")
if config.ID != "" { if config.ID != "" {
t.Printf(" ID:\t%d (%s)\n", config.Confinement.AppID, config.ID) t.Printf(" ID:\t%d (%s)\n", config.Identity, config.ID)
} else { } else {
t.Printf(" ID:\t%d\n", config.Confinement.AppID) t.Printf(" ID:\t%d\n", config.Identity)
} }
t.Printf(" Enablements:\t%s\n", config.Confinement.Enablements.String()) t.Printf(" Enablements:\t%s\n", config.Enablements.String())
if len(config.Confinement.Groups) > 0 { if len(config.Groups) > 0 {
t.Printf(" Groups:\t%q\n", config.Confinement.Groups) t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
} }
if config.Confinement.Outer != "" { if config.Data != "" {
t.Printf(" Directory:\t%s\n", config.Confinement.Outer) t.Printf(" Data:\t%s\n", config.Data)
} }
if config.Confinement.Sandbox != nil { if config.Container != nil {
sandbox := config.Confinement.Sandbox container := config.Container
if sandbox.Hostname != "" { if container.Hostname != "" {
t.Printf(" Hostname:\t%q\n", sandbox.Hostname) t.Printf(" Hostname:\t%s\n", container.Hostname)
} }
flags := make([]string, 0, 7) flags := make([]string, 0, 7)
writeFlag := func(name string, value bool) { writeFlag := func(name string, value bool) {
@ -91,33 +91,29 @@ func printShowInstance(
flags = append(flags, name) flags = append(flags, name)
} }
} }
writeFlag("userns", sandbox.Userns) writeFlag("userns", container.Userns)
writeFlag("devel", sandbox.Devel) writeFlag("devel", container.Devel)
writeFlag("net", sandbox.Net) writeFlag("net", container.Net)
writeFlag("device", sandbox.Device) writeFlag("device", container.Device)
writeFlag("tty", sandbox.Tty) writeFlag("tty", container.Tty)
writeFlag("mapuid", sandbox.MapRealUID) writeFlag("mapuid", container.MapRealUID)
writeFlag("directwl", sandbox.DirectWayland) writeFlag("directwl", config.DirectWayland)
writeFlag("autoetc", sandbox.AutoEtc) writeFlag("autoetc", container.AutoEtc)
if len(flags) == 0 { if len(flags) == 0 {
flags = append(flags, "none") flags = append(flags, "none")
} }
t.Printf(" Flags:\t%s\n", strings.Join(flags, " ")) t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
etc := sandbox.Etc etc := container.Etc
if etc == "" { if etc == "" {
etc = "/etc" etc = "/etc"
} }
t.Printf(" Etc:\t%s\n", etc) t.Printf(" Etc:\t%s\n", etc)
if len(sandbox.Cover) > 0 { if len(container.Cover) > 0 {
t.Printf(" Cover:\t%s\n", strings.Join(sandbox.Cover, " ")) t.Printf(" Cover:\t%s\n", strings.Join(container.Cover, " "))
} }
// Env map[string]string `json:"env"`
// Link [][2]string `json:"symlink"`
}
if config.Confinement.Sandbox != nil {
t.Printf(" Path:\t%s\n", config.Path) t.Printf(" Path:\t%s\n", config.Path)
} }
if len(config.Args) > 0 { if len(config.Args) > 0 {
@ -126,9 +122,9 @@ func printShowInstance(
t.Printf("\n") t.Printf("\n")
if !short { if !short {
if config.Confinement.Sandbox != nil && len(config.Confinement.Sandbox.Filesystem) > 0 { if config.Container != nil && len(config.Container.Filesystem) > 0 {
t.Printf("Filesystem\n") t.Printf("Filesystem\n")
for _, f := range config.Confinement.Sandbox.Filesystem { for _, f := range config.Container.Filesystem {
if f == nil { if f == nil {
continue continue
} }
@ -156,9 +152,9 @@ func printShowInstance(
} }
t.Printf("\n") t.Printf("\n")
} }
if len(config.Confinement.ExtraPerms) > 0 { if len(config.ExtraPerms) > 0 {
t.Printf("Extra ACL\n") t.Printf("Extra ACL\n")
for _, p := range config.Confinement.ExtraPerms { for _, p := range config.ExtraPerms {
if p == nil { if p == nil {
continue continue
} }
@ -186,14 +182,14 @@ func printShowInstance(
t.Printf(" Broadcast:\t%q\n", c.Broadcast) t.Printf(" Broadcast:\t%q\n", c.Broadcast)
} }
} }
if config.Confinement.SessionBus != nil { if config.SessionBus != nil {
t.Printf("Session bus\n") t.Printf("Session bus\n")
printDBus(config.Confinement.SessionBus) printDBus(config.SessionBus)
t.Printf("\n") t.Printf("\n")
} }
if config.Confinement.SystemBus != nil { if config.SystemBus != nil {
t.Printf("System bus\n") t.Printf("System bus\n")
printDBus(config.Confinement.SystemBus) printDBus(config.SystemBus)
t.Printf("\n") t.Printf("\n")
} }
} }
@ -265,7 +261,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
as := "(No configuration information)" as := "(No configuration information)"
if e.Config != nil { if e.Config != nil {
as = strconv.Itoa(e.Config.Confinement.AppID) as = strconv.Itoa(e.Config.Identity)
id := e.Config.ID id := e.Config.ID
if id == "" { if id == "" {
id = "uk.gensokyo.fortify." + e.s[:8] id = "uk.gensokyo.fortify." + e.s[:8]

View File

@ -39,9 +39,9 @@ func Test_printShowInstance(t *testing.T) {
{"config", nil, fst.Template(), false, false, `App {"config", nil, fst.Template(), false, false, `App
ID: 9 (org.chromium.Chromium) ID: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pulseaudio Enablements: wayland, dbus, pulseaudio
Groups: ["video"] Groups: video, dialout, plugdev
Directory: /var/lib/persist/home/org.chromium.Chromium Data: /var/lib/fortify/u0/org.chromium.Chromium
Hostname: "localhost" Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
@ -79,7 +79,7 @@ App
Enablements: (no enablements) Enablements: (no enablements)
`}, `},
{"config flag none", nil, &fst.Config{Confinement: fst.ConfinementConfig{Sandbox: new(fst.SandboxConfig)}}, false, false, `App {"config flag none", nil, &fst.Config{Container: new(fst.ContainerConfig)}, false, false, `App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Flags: none Flags: none
@ -87,7 +87,7 @@ App
Path: 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{Container: &fst.ContainerConfig{Filesystem: make([]*fst.FilesystemConfig, 1)}, ExtraPerms: make([]*fst.ExtraPermConfig, 1)}, false, false, `App
ID: 0 ID: 0
Enablements: (no enablements) Enablements: (no enablements)
Flags: none Flags: none
@ -99,7 +99,7 @@ Filesystem
Extra ACL Extra ACL
`}, `},
{"config pd dbus see", nil, &fst.Config{Confinement: fst.ConfinementConfig{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}}, false, false, `Warning: this configuration uses permissive defaults! {"config pd dbus see", nil, &fst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Warning: this configuration uses permissive defaults!
App App
ID: 0 ID: 0
@ -118,9 +118,9 @@ Session bus
App App
ID: 9 (org.chromium.Chromium) ID: 9 (org.chromium.Chromium)
Enablements: wayland, dbus, pulseaudio Enablements: wayland, dbus, pulseaudio
Groups: ["video"] Groups: video, dialout, plugdev
Directory: /var/lib/persist/home/org.chromium.Chromium Data: /var/lib/fortify/u0/org.chromium.Chromium
Hostname: "localhost" Hostname: localhost
Flags: userns devel net device tty mapuid autoetc Flags: userns devel net device tty mapuid autoetc
Etc: /etc Etc: /etc
Cover: /var/run/nscd Cover: /var/run/nscd
@ -195,141 +195,67 @@ App
"--enable-features=UseOzonePlatform", "--enable-features=UseOzonePlatform",
"--ozone-platform=wayland" "--ozone-platform=wayland"
], ],
"confinement": { "enablements": 13,
"app_id": 9, "session_bus": {
"groups": [ "see": null,
"video" "talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
], ],
"username": "chronos", "own": [
"home_inner": "/var/lib/fortify", "org.chromium.Chromium.*",
"home": "/var/lib/persist/home/org.chromium.Chromium", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"shell": "/run/current-system/sw/bin/zsh", "org.mpris.MediaPlayer2.chromium.*"
"sandbox": {
"hostname": "localhost",
"seccomp": 32,
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{
"src": "/nix/store"
},
{
"src": "/run/current-system"
},
{
"src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
}
],
"symlink": [
[
"/run/user/65534",
"/run/user/150"
]
],
"etc": "/etc",
"auto_etc": true,
"cover": [
"/var/run/nscd"
]
},
"extra_perms": [
{
"ensure": true,
"path": "/var/lib/fortify/u0",
"x": true
},
{
"path": "/var/lib/fortify/u0/org.chromium.Chromium",
"r": true,
"w": true,
"x": true
}
], ],
"system_bus": { "call": {
"see": null, "org.freedesktop.portal.*": "*"
"talk": [
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower"
],
"own": null,
"call": null,
"broadcast": null,
"filter": true
}, },
"session_bus": { "broadcast": {
"see": null, "org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
"talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
],
"own": [
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"
],
"call": {
"org.freedesktop.portal.*": "*"
},
"broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
}, },
"enablements": 13 "filter": true
} },
}, "system_bus": {
"time": "1970-01-01T00:00:00.000000009Z" "see": null,
} "talk": [
`}, "org.bluez",
{"json config", nil, fst.Template(), false, true, `{ "org.freedesktop.Avahi",
"id": "org.chromium.Chromium", "org.freedesktop.UPower"
"path": "/run/current-system/sw/bin/chromium", ],
"args": [ "own": null,
"chromium", "call": null,
"--ignore-gpu-blocklist", "broadcast": null,
"--disable-smooth-scrolling", "filter": true
"--enable-features=UseOzonePlatform", },
"--ozone-platform=wayland"
],
"confinement": {
"app_id": 9,
"groups": [
"video"
],
"username": "chronos", "username": "chronos",
"home_inner": "/var/lib/fortify",
"home": "/var/lib/persist/home/org.chromium.Chromium",
"shell": "/run/current-system/sw/bin/zsh", "shell": "/run/current-system/sw/bin/zsh",
"sandbox": { "data": "/var/lib/fortify/u0/org.chromium.Chromium",
"dir": "/data/data/org.chromium.Chromium",
"extra_perms": [
{
"ensure": true,
"path": "/var/lib/fortify/u0",
"x": true
},
{
"path": "/var/lib/fortify/u0/org.chromium.Chromium",
"r": true,
"w": true,
"x": true
}
],
"identity": 9,
"groups": [
"video",
"dialout",
"plugdev"
],
"container": {
"hostname": "localhost", "hostname": "localhost",
"seccomp": 32, "seccomp": 32,
"devel": true, "devel": true,
@ -379,57 +305,131 @@ App
"cover": [ "cover": [
"/var/run/nscd" "/var/run/nscd"
] ]
}
},
"time": "1970-01-01T00:00:00.000000009Z"
}
`},
{"json config", nil, fst.Template(), false, true, `{
"id": "org.chromium.Chromium",
"path": "/run/current-system/sw/bin/chromium",
"args": [
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
"--enable-features=UseOzonePlatform",
"--ozone-platform=wayland"
],
"enablements": 13,
"session_bus": {
"see": null,
"talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
],
"own": [
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"
],
"call": {
"org.freedesktop.portal.*": "*"
}, },
"extra_perms": [ "broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"system_bus": {
"see": null,
"talk": [
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower"
],
"own": null,
"call": null,
"broadcast": null,
"filter": true
},
"username": "chronos",
"shell": "/run/current-system/sw/bin/zsh",
"data": "/var/lib/fortify/u0/org.chromium.Chromium",
"dir": "/data/data/org.chromium.Chromium",
"extra_perms": [
{
"ensure": true,
"path": "/var/lib/fortify/u0",
"x": true
},
{
"path": "/var/lib/fortify/u0/org.chromium.Chromium",
"r": true,
"w": true,
"x": true
}
],
"identity": 9,
"groups": [
"video",
"dialout",
"plugdev"
],
"container": {
"hostname": "localhost",
"seccomp": 32,
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{ {
"ensure": true, "src": "/nix/store"
"path": "/var/lib/fortify/u0",
"x": true
}, },
{ {
"path": "/var/lib/fortify/u0/org.chromium.Chromium", "src": "/run/current-system"
"r": true, },
"w": true, {
"x": true "src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
} }
], ],
"system_bus": { "symlink": [
"see": null, [
"talk": [ "/run/user/65534",
"org.bluez", "/run/user/150"
"org.freedesktop.Avahi", ]
"org.freedesktop.UPower" ],
], "etc": "/etc",
"own": null, "auto_etc": true,
"call": null, "cover": [
"broadcast": null, "/var/run/nscd"
"filter": true ]
},
"session_bus": {
"see": null,
"talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
],
"own": [
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"
],
"call": {
"org.freedesktop.portal.*": "*"
},
"broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"enablements": 13
} }
} }
`}, `},
@ -499,116 +499,116 @@ func Test_printPs(t *testing.T) {
"--enable-features=UseOzonePlatform", "--enable-features=UseOzonePlatform",
"--ozone-platform=wayland" "--ozone-platform=wayland"
], ],
"confinement": { "enablements": 13,
"app_id": 9, "session_bus": {
"groups": [ "see": null,
"video" "talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
], ],
"username": "chronos", "own": [
"home_inner": "/var/lib/fortify", "org.chromium.Chromium.*",
"home": "/var/lib/persist/home/org.chromium.Chromium", "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"shell": "/run/current-system/sw/bin/zsh", "org.mpris.MediaPlayer2.chromium.*"
"sandbox": { ],
"hostname": "localhost", "call": {
"seccomp": 32, "org.freedesktop.portal.*": "*"
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{
"src": "/nix/store"
},
{
"src": "/run/current-system"
},
{
"src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
}
],
"symlink": [
[
"/run/user/65534",
"/run/user/150"
]
],
"etc": "/etc",
"auto_etc": true,
"cover": [
"/var/run/nscd"
]
}, },
"extra_perms": [ "broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"system_bus": {
"see": null,
"talk": [
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower"
],
"own": null,
"call": null,
"broadcast": null,
"filter": true
},
"username": "chronos",
"shell": "/run/current-system/sw/bin/zsh",
"data": "/var/lib/fortify/u0/org.chromium.Chromium",
"dir": "/data/data/org.chromium.Chromium",
"extra_perms": [
{
"ensure": true,
"path": "/var/lib/fortify/u0",
"x": true
},
{
"path": "/var/lib/fortify/u0/org.chromium.Chromium",
"r": true,
"w": true,
"x": true
}
],
"identity": 9,
"groups": [
"video",
"dialout",
"plugdev"
],
"container": {
"hostname": "localhost",
"seccomp": 32,
"devel": true,
"userns": true,
"net": true,
"tty": true,
"multiarch": true,
"env": {
"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT"
},
"map_real_uid": true,
"device": true,
"filesystem": [
{ {
"ensure": true, "src": "/nix/store"
"path": "/var/lib/fortify/u0",
"x": true
}, },
{ {
"path": "/var/lib/fortify/u0/org.chromium.Chromium", "src": "/run/current-system"
"r": true, },
"w": true, {
"x": true "src": "/run/opengl-driver"
},
{
"src": "/var/db/nix-channels"
},
{
"dst": "/data/data/org.chromium.Chromium",
"src": "/var/lib/fortify/u0/org.chromium.Chromium",
"write": true,
"require": true
},
{
"src": "/dev/dri",
"dev": true
} }
], ],
"system_bus": { "symlink": [
"see": null, [
"talk": [ "/run/user/65534",
"org.bluez", "/run/user/150"
"org.freedesktop.Avahi", ]
"org.freedesktop.UPower" ],
], "etc": "/etc",
"own": null, "auto_etc": true,
"call": null, "cover": [
"broadcast": null, "/var/run/nscd"
"filter": true ]
},
"session_bus": {
"see": null,
"talk": [
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager"
],
"own": [
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*"
],
"call": {
"org.freedesktop.portal.*": "*"
},
"broadcast": {
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"
},
"filter": true
},
"enablements": 13
} }
}, },
"time": "1970-01-01T00:00:00.000000009Z" "time": "1970-01-01T00:00:00.000000009Z"

View File

@ -69,8 +69,8 @@ def check_state(name, enablements):
if len(config['args']) != 1 or config['args'][0] != command: if len(config['args']) != 1 or config['args'][0] != command:
raise Exception(f"unexpected args {config['args']}") raise Exception(f"unexpected args {config['args']}")
if config['confinement']['enablements'] != enablements: if config['enablements'] != enablements:
raise Exception(f"unexpected enablements {instance['config']['confinement']['enablements']}") raise Exception(f"unexpected enablements {instance['config']['enablements']}")
def fortify(command): def fortify(command):