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
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:
@@ -16,9 +16,9 @@ import (
|
||||
"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.
|
||||
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 {
|
||||
return nil, nil, syscall.EBADE
|
||||
}
|
||||
|
||||
@@ -13,41 +13,43 @@ var testCasesNixos = []sealTestCase{
|
||||
{
|
||||
"nixos chromium direct wayland", new(stubNixOS),
|
||||
&fst.Config{
|
||||
ID: "org.chromium.Chromium",
|
||||
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 1, Groups: []string{}, Username: "u0_a1",
|
||||
Outer: "/var/lib/persist/module/fortify/0/1",
|
||||
Sandbox: &fst.SandboxConfig{
|
||||
Userns: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true,
|
||||
Filesystem: []*fst.FilesystemConfig{
|
||||
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
|
||||
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
|
||||
{Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"},
|
||||
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
|
||||
},
|
||||
Cover: []string{"/var/run/nscd"},
|
||||
ID: "org.chromium.Chromium",
|
||||
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
||||
Enablements: system.EWayland | system.EDBus | system.EPulse,
|
||||
|
||||
Container: &fst.ContainerConfig{
|
||||
Userns: true, Net: true, MapRealUID: true, Env: nil, AutoEtc: true,
|
||||
Filesystem: []*fst.FilesystemConfig{
|
||||
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
|
||||
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
|
||||
{Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"},
|
||||
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
|
||||
},
|
||||
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,
|
||||
},
|
||||
Enablements: system.EWayland | system.EDBus | system.EPulse,
|
||||
Cover: []string{"/var/run/nscd"},
|
||||
},
|
||||
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{
|
||||
0x8e, 0x2c, 0x76, 0xb0,
|
||||
|
||||
@@ -14,13 +14,7 @@ import (
|
||||
var testCasesPd = []sealTestCase{
|
||||
{
|
||||
"nixos permissive defaults no enablements", new(stubNixOS),
|
||||
&fst.Config{
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 0,
|
||||
Username: "chronos",
|
||||
Outer: "/home/chronos",
|
||||
},
|
||||
},
|
||||
&fst.Config{Username: "chronos", Data: "/home/chronos"},
|
||||
app.ID{
|
||||
0x4a, 0x45, 0x0b, 0x65,
|
||||
0x96, 0xd7, 0xbc, 0x15,
|
||||
@@ -77,46 +71,44 @@ var testCasesPd = []sealTestCase{
|
||||
{
|
||||
"nixos permissive defaults chromium", new(stubNixOS),
|
||||
&fst.Config{
|
||||
ID: "org.chromium.Chromium",
|
||||
Args: []string{"zsh", "-c", "exec chromium "},
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 9,
|
||||
Groups: []string{"video"},
|
||||
Username: "chronos",
|
||||
Outer: "/home/chronos",
|
||||
SessionBus: &dbus.Config{
|
||||
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/*",
|
||||
},
|
||||
Filter: true,
|
||||
ID: "org.chromium.Chromium",
|
||||
Args: []string{"zsh", "-c", "exec chromium "},
|
||||
Identity: 9,
|
||||
Groups: []string{"video"},
|
||||
Username: "chronos",
|
||||
Data: "/home/chronos",
|
||||
SessionBus: &dbus.Config{
|
||||
Talk: []string{
|
||||
"org.freedesktop.Notifications",
|
||||
"org.freedesktop.FileManager1",
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"org.freedesktop.secrets",
|
||||
"org.kde.kwalletd5",
|
||||
"org.kde.kwalletd6",
|
||||
"org.gnome.SessionManager",
|
||||
},
|
||||
SystemBus: &dbus.Config{
|
||||
Talk: []string{
|
||||
"org.bluez",
|
||||
"org.freedesktop.Avahi",
|
||||
"org.freedesktop.UPower",
|
||||
},
|
||||
Filter: true,
|
||||
Own: []string{
|
||||
"org.chromium.Chromium.*",
|
||||
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
|
||||
"org.mpris.MediaPlayer2.chromium.*",
|
||||
},
|
||||
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{
|
||||
0xeb, 0xf0, 0x83, 0xd1,
|
||||
|
||||
@@ -65,7 +65,7 @@ func (seal *outcome) Run(rs *RunState) error {
|
||||
// accumulate enablements of remaining launchers
|
||||
for i, s := range states {
|
||||
if s.Config != nil {
|
||||
rt |= s.Config.Confinement.Enablements
|
||||
rt |= s.Config.Enablements
|
||||
} else {
|
||||
log.Printf("state entry %d does not contain config", i)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
if config.Confinement.AppID < 0 || config.Confinement.AppID > 9999 {
|
||||
if config.Identity < 0 || config.Identity > 9999 {
|
||||
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{
|
||||
aid: newInt(config.Confinement.AppID),
|
||||
data: config.Confinement.Outer,
|
||||
home: config.Confinement.Inner,
|
||||
username: config.Confinement.Username,
|
||||
aid: newInt(config.Identity),
|
||||
data: config.Data,
|
||||
home: config.Dir,
|
||||
username: config.Username,
|
||||
}
|
||||
if seal.user.username == "" {
|
||||
seal.user.username = "chronos"
|
||||
@@ -199,8 +199,8 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
||||
} else {
|
||||
seal.user.uid = newInt(u)
|
||||
}
|
||||
seal.user.supp = make([]string, len(config.Confinement.Groups))
|
||||
for i, name := range config.Confinement.Groups {
|
||||
seal.user.supp = make([]string, len(config.Groups))
|
||||
for i, name := range config.Groups {
|
||||
if g, err := sys.LookupGroup(name); err != nil {
|
||||
return fmsg.WrapError(err,
|
||||
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
|
||||
if !path.IsAbs(config.Confinement.Shell) {
|
||||
config.Confinement.Shell = "/bin/sh"
|
||||
if !path.IsAbs(config.Shell) {
|
||||
config.Shell = "/bin/sh"
|
||||
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
|
||||
|
||||
// permissive defaults
|
||||
if config.Confinement.Sandbox == nil {
|
||||
fmsg.Verbose("sandbox configuration not supplied, PROCEED WITH CAUTION")
|
||||
if config.Container == nil {
|
||||
fmsg.Verbose("container configuration not supplied, PROCEED WITH CAUTION")
|
||||
|
||||
// fsu clears the environment so resolve paths early
|
||||
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
|
||||
}
|
||||
} else {
|
||||
config.Path = config.Confinement.Shell
|
||||
config.Path = config.Shell
|
||||
}
|
||||
}
|
||||
|
||||
conf := &fst.SandboxConfig{
|
||||
conf := &fst.ContainerConfig{
|
||||
Userns: true,
|
||||
Net: 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)
|
||||
}
|
||||
// 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})
|
||||
}
|
||||
// opportunistically bind kvm
|
||||
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 uid, gid int
|
||||
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 {
|
||||
return fmsg.WrapErrorSuffix(err,
|
||||
"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.Confinement.Sandbox.Etc != "" {
|
||||
seal.container.Bind(config.Confinement.Sandbox.Etc, "/etc", 0)
|
||||
if !config.Container.AutoEtc {
|
||||
if config.Container.Etc != "" {
|
||||
seal.container.Bind(config.Container.Etc, "/etc", 0)
|
||||
}
|
||||
} else {
|
||||
etcPath := config.Confinement.Sandbox.Etc
|
||||
etcPath := config.Container.Etc
|
||||
if etcPath == "" {
|
||||
etcPath = "/etc"
|
||||
}
|
||||
@@ -352,10 +352,10 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
||||
seal.container.Dir = homeDir
|
||||
seal.env["HOME"] = homeDir
|
||||
seal.env["USER"] = username
|
||||
seal.env[shell] = config.Confinement.Shell
|
||||
seal.env[shell] = config.Shell
|
||||
|
||||
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",
|
||||
[]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
|
||||
}
|
||||
|
||||
if config.Confinement.Enablements&system.EWayland != 0 {
|
||||
if config.Enablements&system.EWayland != 0 {
|
||||
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
||||
var socketPath string
|
||||
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)
|
||||
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
|
||||
if appID == "" {
|
||||
// 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 {
|
||||
return fmsg.WrapError(ErrXDisplay,
|
||||
"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`)
|
||||
pulseRuntimeDir := path.Join(share.sc.RuntimePath, "pulse")
|
||||
// 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
|
||||
if config.Confinement.SessionBus == nil {
|
||||
config.Confinement.SessionBus = dbus.NewConfig(config.ID, true, true)
|
||||
if config.SessionBus == nil {
|
||||
config.SessionBus = dbus.NewConfig(config.ID, true, true)
|
||||
}
|
||||
|
||||
// downstream socket paths
|
||||
@@ -470,7 +470,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
||||
|
||||
// configure dbus proxy
|
||||
if f, err := seal.sys.ProxyDBus(
|
||||
config.Confinement.SessionBus, config.Confinement.SystemBus,
|
||||
config.SessionBus, config.SystemBus,
|
||||
sessionPath, systemPath,
|
||||
); err != nil {
|
||||
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.container.Bind(sessionPath, sessionInner, 0)
|
||||
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
|
||||
if config.Confinement.SystemBus != nil {
|
||||
if config.SystemBus != nil {
|
||||
systemInner := "/run/dbus/system_bus_socket"
|
||||
seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner
|
||||
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)
|
||||
}
|
||||
|
||||
// append ExtraPerms last
|
||||
for _, p := range config.Confinement.ExtraPerms {
|
||||
for _, p := range config.ExtraPerms {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
@@ -530,7 +530,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Co
|
||||
slices.Sort(seal.container.Env)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user