@@ -1,6 +1,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
@@ -10,9 +11,10 @@ import (
|
||||
"git.gensokyo.uk/security/fortify/internal/sys"
|
||||
)
|
||||
|
||||
func New(os sys.State) (fst.App, error) {
|
||||
func New(ctx context.Context, os sys.State) (fst.App, error) {
|
||||
a := new(app)
|
||||
a.sys = os
|
||||
a.ctx = ctx
|
||||
|
||||
id := new(fst.ID)
|
||||
err := fst.NewAppID(id)
|
||||
@@ -21,8 +23,8 @@ func New(os sys.State) (fst.App, error) {
|
||||
return a, err
|
||||
}
|
||||
|
||||
func MustNew(os sys.State) fst.App {
|
||||
a, err := New(os)
|
||||
func MustNew(ctx context.Context, os sys.State) fst.App {
|
||||
a, err := New(ctx, os)
|
||||
if err != nil {
|
||||
log.Fatalf("cannot create app: %v", err)
|
||||
}
|
||||
@@ -32,6 +34,7 @@ func MustNew(os sys.State) fst.App {
|
||||
type app struct {
|
||||
id *stringPair[fst.ID]
|
||||
sys sys.State
|
||||
ctx context.Context
|
||||
|
||||
*outcome
|
||||
mu sync.RWMutex
|
||||
@@ -71,7 +74,7 @@ func (a *app) Seal(config *fst.Config) (fst.SealedApp, error) {
|
||||
|
||||
seal := new(outcome)
|
||||
seal.id = a.id
|
||||
err := seal.finalise(a.sys, config)
|
||||
err := seal.finalise(a.ctx, a.sys, config)
|
||||
if err == nil {
|
||||
a.outcome = seal
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"git.gensokyo.uk/security/fortify/acl"
|
||||
"git.gensokyo.uk/security/fortify/dbus"
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
"git.gensokyo.uk/security/fortify/system"
|
||||
)
|
||||
|
||||
@@ -12,20 +12,20 @@ var testCasesNixos = []sealTestCase{
|
||||
{
|
||||
"nixos chromium direct wayland", new(stubNixOS),
|
||||
&fst.Config{
|
||||
ID: "org.chromium.Chromium",
|
||||
Command: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
|
||||
ID: "org.chromium.Chromium",
|
||||
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 1, Groups: []string{}, Username: "u0_a1",
|
||||
Outer: "/var/lib/persist/module/fortify/0/1",
|
||||
Sandbox: &fst.SandboxConfig{
|
||||
UserNS: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true,
|
||||
Userns: true, Net: true, MapRealUID: true, DirectWayland: true, Env: nil, AutoEtc: true,
|
||||
Filesystem: []*fst.FilesystemConfig{
|
||||
{Src: "/bin", Must: true}, {Src: "/usr/bin", Must: true},
|
||||
{Src: "/nix/store", Must: true}, {Src: "/run/current-system", Must: true},
|
||||
{Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"},
|
||||
{Src: "/run/opengl-driver", Must: true}, {Src: "/dev/dri", Device: true},
|
||||
},
|
||||
Override: []string{"/var/run/nscd"},
|
||||
Cover: []string{"/var/run/nscd"},
|
||||
},
|
||||
SystemBus: &dbus.Config{
|
||||
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
|
||||
@@ -88,136 +88,133 @@ var testCasesNixos = []sealTestCase{
|
||||
}).
|
||||
UpdatePerm("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", acl.Read, acl.Write).
|
||||
UpdatePerm("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", acl.Read, acl.Write),
|
||||
(&bwrap.Config{
|
||||
Net: true,
|
||||
UserNS: true,
|
||||
Chdir: "/var/lib/persist/module/fortify/0/1",
|
||||
Clearenv: true,
|
||||
SetEnv: map[string]string{
|
||||
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1971/bus",
|
||||
"DBUS_SYSTEM_BUS_ADDRESS": "unix:path=/run/dbus/system_bus_socket",
|
||||
"HOME": "/var/lib/persist/module/fortify/0/1",
|
||||
"PULSE_COOKIE": fst.Tmp + "/pulse-cookie",
|
||||
"PULSE_SERVER": "unix:/run/user/1971/pulse/native",
|
||||
"SHELL": "/run/current-system/sw/bin/zsh",
|
||||
"TERM": "xterm-256color",
|
||||
"USER": "u0_a1",
|
||||
"WAYLAND_DISPLAY": "wayland-0",
|
||||
"XDG_RUNTIME_DIR": "/run/user/1971",
|
||||
"XDG_SESSION_CLASS": "user",
|
||||
"XDG_SESSION_TYPE": "tty",
|
||||
&sandbox.Params{
|
||||
Uid: 1971,
|
||||
Gid: 100,
|
||||
Flags: sandbox.FAllowNet | sandbox.FAllowUserns,
|
||||
Dir: "/var/lib/persist/module/fortify/0/1",
|
||||
Path: "/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start",
|
||||
Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
|
||||
Env: []string{
|
||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
|
||||
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket",
|
||||
"HOME=/var/lib/persist/module/fortify/0/1",
|
||||
"PULSE_COOKIE=" + fst.Tmp + "/pulse-cookie",
|
||||
"PULSE_SERVER=unix:/run/user/1971/pulse/native",
|
||||
"TERM=xterm-256color",
|
||||
"USER=u0_a1",
|
||||
"WAYLAND_DISPLAY=wayland-0",
|
||||
"XDG_RUNTIME_DIR=/run/user/1971",
|
||||
"XDG_SESSION_CLASS=user",
|
||||
"XDG_SESSION_TYPE=tty",
|
||||
},
|
||||
Chmod: make(bwrap.ChmodConfig),
|
||||
NewSession: true,
|
||||
DieWithParent: true,
|
||||
AsInit: true,
|
||||
}).SetUID(1971).SetGID(1971).
|
||||
Procfs("/proc").
|
||||
Tmpfs(fst.Tmp, 4096).
|
||||
DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin").
|
||||
Bind("/usr/bin", "/usr/bin").
|
||||
Bind("/nix/store", "/nix/store").
|
||||
Bind("/run/current-system", "/run/current-system").
|
||||
Bind("/sys/block", "/sys/block", true).
|
||||
Bind("/sys/bus", "/sys/bus", true).
|
||||
Bind("/sys/class", "/sys/class", true).
|
||||
Bind("/sys/dev", "/sys/dev", true).
|
||||
Bind("/sys/devices", "/sys/devices", true).
|
||||
Bind("/run/opengl-driver", "/run/opengl-driver").
|
||||
Bind("/dev/dri", "/dev/dri", true, true, true).
|
||||
Bind("/etc", fst.Tmp+"/etc").
|
||||
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Symlink(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Symlink("/proc/mounts", "/etc/mtab").
|
||||
Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Symlink(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Symlink(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 1048576).
|
||||
Tmpfs("/run/user/1971", 8388608).
|
||||
Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", false, true).
|
||||
Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", false, true).
|
||||
CopyBind("/etc/passwd", []byte("u0_a1:x:1971:1971:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).
|
||||
CopyBind("/etc/group", []byte("fortify:x:1971:\n")).
|
||||
Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0").
|
||||
Bind("/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse", "/run/user/1971/pulse/native").
|
||||
CopyBind(fst.Tmp+"/pulse-cookie", nil).
|
||||
Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus").
|
||||
Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket").
|
||||
Tmpfs("/var/run/nscd", 8192).
|
||||
Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
|
||||
Symlink("fortify", "/.fortify/sbin/init0"),
|
||||
Ops: new(sandbox.Ops).
|
||||
Proc("/proc").
|
||||
Tmpfs(fst.Tmp, 4096, 0755).
|
||||
Dev("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin", 0).
|
||||
Bind("/usr/bin", "/usr/bin", 0).
|
||||
Bind("/nix/store", "/nix/store", 0).
|
||||
Bind("/run/current-system", "/run/current-system", 0).
|
||||
Bind("/sys/block", "/sys/block", sandbox.BindOptional).
|
||||
Bind("/sys/bus", "/sys/bus", sandbox.BindOptional).
|
||||
Bind("/sys/class", "/sys/class", sandbox.BindOptional).
|
||||
Bind("/sys/dev", "/sys/dev", sandbox.BindOptional).
|
||||
Bind("/sys/devices", "/sys/devices", sandbox.BindOptional).
|
||||
Bind("/run/opengl-driver", "/run/opengl-driver", 0).
|
||||
Bind("/dev/dri", "/dev/dri", sandbox.BindDevice|sandbox.BindWritable|sandbox.BindOptional).
|
||||
Bind("/etc", fst.Tmp+"/etc", 0).
|
||||
Mkdir("/etc", 0700).
|
||||
Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Link(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Link(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Link("/proc/mounts", "/etc/mtab").
|
||||
Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Link(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Link(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Link(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Link(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Link(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Link(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Link(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Link(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Link(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Link(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/1971", 8388608, 0755).
|
||||
Bind("/tmp/fortify.1971/tmpdir/1", "/tmp", sandbox.BindWritable).
|
||||
Bind("/var/lib/persist/module/fortify/0/1", "/var/lib/persist/module/fortify/0/1", sandbox.BindWritable).
|
||||
Place("/etc/passwd", []byte("u0_a1:x:1971:100:Fortify:/var/lib/persist/module/fortify/0/1:/run/current-system/sw/bin/zsh\n")).
|
||||
Place("/etc/group", []byte("fortify:x:100:\n")).
|
||||
Bind("/run/user/1971/wayland-0", "/run/user/1971/wayland-0", 0).
|
||||
Bind("/run/user/1971/fortify/8e2c76b066dabe574cf073bdb46eb5c1/pulse", "/run/user/1971/pulse/native", 0).
|
||||
Place(fst.Tmp+"/pulse-cookie", nil).
|
||||
Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/bus", "/run/user/1971/bus", 0).
|
||||
Bind("/tmp/fortify.1971/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"git.gensokyo.uk/security/fortify/acl"
|
||||
"git.gensokyo.uk/security/fortify/dbus"
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
"git.gensokyo.uk/security/fortify/system"
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@ var testCasesPd = []sealTestCase{
|
||||
{
|
||||
"nixos permissive defaults no enablements", new(stubNixOS),
|
||||
&fst.Config{
|
||||
Command: make([]string, 0),
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 0,
|
||||
Username: "chronos",
|
||||
@@ -35,136 +34,132 @@ var testCasesPd = []sealTestCase{
|
||||
Ephemeral(system.Process, "/run/user/1971/fortify/4a450b6596d7bc15bd01780eb9a607ac", 0700).UpdatePermType(system.Process, "/run/user/1971/fortify/4a450b6596d7bc15bd01780eb9a607ac", acl.Execute).
|
||||
Ensure("/tmp/fortify.1971/tmpdir", 0700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir", acl.Execute).
|
||||
Ensure("/tmp/fortify.1971/tmpdir/0", 01700).UpdatePermType(system.User, "/tmp/fortify.1971/tmpdir/0", acl.Read, acl.Write, acl.Execute),
|
||||
(&bwrap.Config{
|
||||
Net: true,
|
||||
UserNS: true,
|
||||
Clearenv: true,
|
||||
Syscall: new(bwrap.SyscallPolicy),
|
||||
Chdir: "/home/chronos",
|
||||
SetEnv: map[string]string{
|
||||
"HOME": "/home/chronos",
|
||||
"SHELL": "/run/current-system/sw/bin/zsh",
|
||||
"TERM": "xterm-256color",
|
||||
"USER": "chronos",
|
||||
"XDG_RUNTIME_DIR": "/run/user/65534",
|
||||
"XDG_SESSION_CLASS": "user",
|
||||
"XDG_SESSION_TYPE": "tty"},
|
||||
Chmod: make(bwrap.ChmodConfig),
|
||||
DieWithParent: true,
|
||||
AsInit: true,
|
||||
}).SetUID(65534).SetGID(65534).
|
||||
Procfs("/proc").
|
||||
Tmpfs(fst.Tmp, 4096).
|
||||
DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin", false, true).
|
||||
Bind("/boot", "/boot", false, true).
|
||||
Bind("/home", "/home", false, true).
|
||||
Bind("/lib", "/lib", false, true).
|
||||
Bind("/lib64", "/lib64", false, true).
|
||||
Bind("/nix", "/nix", false, true).
|
||||
Bind("/root", "/root", false, true).
|
||||
Bind("/run", "/run", false, true).
|
||||
Bind("/srv", "/srv", false, true).
|
||||
Bind("/sys", "/sys", false, true).
|
||||
Bind("/usr", "/usr", false, true).
|
||||
Bind("/var", "/var", false, true).
|
||||
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
||||
Tmpfs("/run/user/1971", 8192).
|
||||
Tmpfs("/run/dbus", 8192).
|
||||
Bind("/etc", fst.Tmp+"/etc").
|
||||
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Symlink(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Symlink("/proc/mounts", "/etc/mtab").
|
||||
Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Symlink(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Symlink(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 1048576).
|
||||
Tmpfs("/run/user/65534", 8388608).
|
||||
Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", false, true).
|
||||
Bind("/home/chronos", "/home/chronos", false, true).
|
||||
CopyBind("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||
CopyBind("/etc/group", []byte("fortify:x:65534:\n")).
|
||||
Tmpfs("/var/run/nscd", 8192).
|
||||
Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
|
||||
Symlink("fortify", "/.fortify/sbin/init0"),
|
||||
&sandbox.Params{
|
||||
Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
|
||||
Dir: "/home/chronos",
|
||||
Path: "/run/current-system/sw/bin/zsh",
|
||||
Args: []string{"/run/current-system/sw/bin/zsh"},
|
||||
Env: []string{
|
||||
"HOME=/home/chronos",
|
||||
"TERM=xterm-256color",
|
||||
"USER=chronos",
|
||||
"XDG_RUNTIME_DIR=/run/user/65534",
|
||||
"XDG_SESSION_CLASS=user",
|
||||
"XDG_SESSION_TYPE=tty",
|
||||
},
|
||||
Ops: new(sandbox.Ops).
|
||||
Proc("/proc").
|
||||
Tmpfs(fst.Tmp, 4096, 0755).
|
||||
Dev("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin", sandbox.BindWritable).
|
||||
Bind("/boot", "/boot", sandbox.BindWritable).
|
||||
Bind("/home", "/home", sandbox.BindWritable).
|
||||
Bind("/lib", "/lib", sandbox.BindWritable).
|
||||
Bind("/lib64", "/lib64", sandbox.BindWritable).
|
||||
Bind("/nix", "/nix", sandbox.BindWritable).
|
||||
Bind("/root", "/root", sandbox.BindWritable).
|
||||
Bind("/run", "/run", sandbox.BindWritable).
|
||||
Bind("/srv", "/srv", sandbox.BindWritable).
|
||||
Bind("/sys", "/sys", sandbox.BindWritable).
|
||||
Bind("/usr", "/usr", sandbox.BindWritable).
|
||||
Bind("/var", "/var", sandbox.BindWritable).
|
||||
Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||
Tmpfs("/run/user/1971", 8192, 0755).
|
||||
Tmpfs("/run/dbus", 8192, 0755).
|
||||
Bind("/etc", fst.Tmp+"/etc", 0).
|
||||
Mkdir("/etc", 0700).
|
||||
Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Link(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Link(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Link("/proc/mounts", "/etc/mtab").
|
||||
Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Link(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Link(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Link(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Link(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Link(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Link(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Link(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Link(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Link(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Link(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0755).
|
||||
Bind("/tmp/fortify.1971/tmpdir/0", "/tmp", sandbox.BindWritable).
|
||||
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
|
||||
Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||
Place("/etc/group", []byte("fortify:x:65534:\n")).
|
||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||
},
|
||||
},
|
||||
{
|
||||
"nixos permissive defaults chromium", new(stubNixOS),
|
||||
&fst.Config{
|
||||
ID: "org.chromium.Chromium",
|
||||
Command: []string{"/run/current-system/sw/bin/zsh", "-c", "exec chromium "},
|
||||
ID: "org.chromium.Chromium",
|
||||
Args: []string{"zsh", "-c", "exec chromium "},
|
||||
Confinement: fst.ConfinementConfig{
|
||||
AppID: 9,
|
||||
Groups: []string{"video"},
|
||||
@@ -254,141 +249,136 @@ var testCasesPd = []sealTestCase{
|
||||
}).
|
||||
UpdatePerm("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", acl.Read, acl.Write).
|
||||
UpdatePerm("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", acl.Read, acl.Write),
|
||||
(&bwrap.Config{
|
||||
Net: true,
|
||||
UserNS: true,
|
||||
Chdir: "/home/chronos",
|
||||
Clearenv: true,
|
||||
Syscall: new(bwrap.SyscallPolicy),
|
||||
SetEnv: map[string]string{
|
||||
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/65534/bus",
|
||||
"DBUS_SYSTEM_BUS_ADDRESS": "unix:path=/run/dbus/system_bus_socket",
|
||||
"HOME": "/home/chronos",
|
||||
"PULSE_COOKIE": fst.Tmp + "/pulse-cookie",
|
||||
"PULSE_SERVER": "unix:/run/user/65534/pulse/native",
|
||||
"SHELL": "/run/current-system/sw/bin/zsh",
|
||||
"TERM": "xterm-256color",
|
||||
"USER": "chronos",
|
||||
"WAYLAND_DISPLAY": "wayland-0",
|
||||
"XDG_RUNTIME_DIR": "/run/user/65534",
|
||||
"XDG_SESSION_CLASS": "user",
|
||||
"XDG_SESSION_TYPE": "tty",
|
||||
&sandbox.Params{
|
||||
Flags: sandbox.FAllowNet | sandbox.FAllowUserns | sandbox.FAllowTTY,
|
||||
Dir: "/home/chronos",
|
||||
Path: "/run/current-system/sw/bin/zsh",
|
||||
Args: []string{"zsh", "-c", "exec chromium "},
|
||||
Env: []string{
|
||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus",
|
||||
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket",
|
||||
"HOME=/home/chronos",
|
||||
"PULSE_COOKIE=" + fst.Tmp + "/pulse-cookie",
|
||||
"PULSE_SERVER=unix:/run/user/65534/pulse/native",
|
||||
"TERM=xterm-256color",
|
||||
"USER=chronos",
|
||||
"WAYLAND_DISPLAY=wayland-0",
|
||||
"XDG_RUNTIME_DIR=/run/user/65534",
|
||||
"XDG_SESSION_CLASS=user",
|
||||
"XDG_SESSION_TYPE=tty",
|
||||
},
|
||||
Chmod: make(bwrap.ChmodConfig),
|
||||
DieWithParent: true,
|
||||
AsInit: true,
|
||||
}).SetUID(65534).SetGID(65534).
|
||||
Procfs("/proc").
|
||||
Tmpfs(fst.Tmp, 4096).
|
||||
DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin", false, true).
|
||||
Bind("/boot", "/boot", false, true).
|
||||
Bind("/home", "/home", false, true).
|
||||
Bind("/lib", "/lib", false, true).
|
||||
Bind("/lib64", "/lib64", false, true).
|
||||
Bind("/nix", "/nix", false, true).
|
||||
Bind("/root", "/root", false, true).
|
||||
Bind("/run", "/run", false, true).
|
||||
Bind("/srv", "/srv", false, true).
|
||||
Bind("/sys", "/sys", false, true).
|
||||
Bind("/usr", "/usr", false, true).
|
||||
Bind("/var", "/var", false, true).
|
||||
Bind("/dev/dri", "/dev/dri", true, true, true).
|
||||
Bind("/dev/kvm", "/dev/kvm", true, true, true).
|
||||
Tmpfs("/run/user/1971", 8192).
|
||||
Tmpfs("/run/dbus", 8192).
|
||||
Bind("/etc", fst.Tmp+"/etc").
|
||||
Symlink(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Symlink(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Symlink(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Symlink(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Symlink(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Symlink(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Symlink(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Symlink(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Symlink(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Symlink(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Symlink(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Symlink(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Symlink(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Symlink(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Symlink(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Symlink(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Symlink(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Symlink(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Symlink(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Symlink(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Symlink(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Symlink(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Symlink(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Symlink(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Symlink(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Symlink(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Symlink(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Symlink(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Symlink("/proc/mounts", "/etc/mtab").
|
||||
Symlink(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Symlink(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Symlink(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Symlink(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Symlink(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Symlink(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Symlink(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Symlink(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Symlink(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Symlink(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Symlink(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Symlink(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Symlink(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Symlink(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Symlink(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Symlink(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Symlink(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Symlink(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Symlink(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Symlink(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Symlink(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Symlink(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Symlink(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Symlink(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Symlink(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Symlink(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Symlink(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Symlink(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Symlink(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Symlink(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Symlink(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Symlink(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Symlink(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Symlink(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Symlink(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Symlink(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Symlink(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Symlink(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Symlink(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Symlink(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Symlink(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Symlink(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Symlink(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Symlink(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Symlink(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Symlink(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Symlink(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Symlink(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Symlink(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 1048576).
|
||||
Tmpfs("/run/user/65534", 8388608).
|
||||
Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", false, true).
|
||||
Bind("/home/chronos", "/home/chronos", false, true).
|
||||
CopyBind("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||
CopyBind("/etc/group", []byte("fortify:x:65534:\n")).
|
||||
Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0").
|
||||
Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native").
|
||||
CopyBind(fst.Tmp+"/pulse-cookie", nil).
|
||||
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus").
|
||||
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket").
|
||||
Tmpfs("/var/run/nscd", 8192).
|
||||
Bind("/run/wrappers/bin/fortify", "/.fortify/sbin/fortify").
|
||||
Symlink("fortify", "/.fortify/sbin/init0"),
|
||||
Ops: new(sandbox.Ops).
|
||||
Proc("/proc").
|
||||
Tmpfs(fst.Tmp, 4096, 0755).
|
||||
Dev("/dev").Mqueue("/dev/mqueue").
|
||||
Bind("/bin", "/bin", sandbox.BindWritable).
|
||||
Bind("/boot", "/boot", sandbox.BindWritable).
|
||||
Bind("/home", "/home", sandbox.BindWritable).
|
||||
Bind("/lib", "/lib", sandbox.BindWritable).
|
||||
Bind("/lib64", "/lib64", sandbox.BindWritable).
|
||||
Bind("/nix", "/nix", sandbox.BindWritable).
|
||||
Bind("/root", "/root", sandbox.BindWritable).
|
||||
Bind("/run", "/run", sandbox.BindWritable).
|
||||
Bind("/srv", "/srv", sandbox.BindWritable).
|
||||
Bind("/sys", "/sys", sandbox.BindWritable).
|
||||
Bind("/usr", "/usr", sandbox.BindWritable).
|
||||
Bind("/var", "/var", sandbox.BindWritable).
|
||||
Bind("/dev/dri", "/dev/dri", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||
Bind("/dev/kvm", "/dev/kvm", sandbox.BindWritable|sandbox.BindDevice|sandbox.BindOptional).
|
||||
Tmpfs("/run/user/1971", 8192, 0755).
|
||||
Tmpfs("/run/dbus", 8192, 0755).
|
||||
Bind("/etc", fst.Tmp+"/etc", 0).
|
||||
Mkdir("/etc", 0700).
|
||||
Link(fst.Tmp+"/etc/alsa", "/etc/alsa").
|
||||
Link(fst.Tmp+"/etc/bashrc", "/etc/bashrc").
|
||||
Link(fst.Tmp+"/etc/binfmt.d", "/etc/binfmt.d").
|
||||
Link(fst.Tmp+"/etc/dbus-1", "/etc/dbus-1").
|
||||
Link(fst.Tmp+"/etc/default", "/etc/default").
|
||||
Link(fst.Tmp+"/etc/ethertypes", "/etc/ethertypes").
|
||||
Link(fst.Tmp+"/etc/fonts", "/etc/fonts").
|
||||
Link(fst.Tmp+"/etc/fstab", "/etc/fstab").
|
||||
Link(fst.Tmp+"/etc/fuse.conf", "/etc/fuse.conf").
|
||||
Link(fst.Tmp+"/etc/host.conf", "/etc/host.conf").
|
||||
Link(fst.Tmp+"/etc/hostid", "/etc/hostid").
|
||||
Link(fst.Tmp+"/etc/hostname", "/etc/hostname").
|
||||
Link(fst.Tmp+"/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||
Link(fst.Tmp+"/etc/hosts", "/etc/hosts").
|
||||
Link(fst.Tmp+"/etc/inputrc", "/etc/inputrc").
|
||||
Link(fst.Tmp+"/etc/ipsec.d", "/etc/ipsec.d").
|
||||
Link(fst.Tmp+"/etc/issue", "/etc/issue").
|
||||
Link(fst.Tmp+"/etc/kbd", "/etc/kbd").
|
||||
Link(fst.Tmp+"/etc/libblockdev", "/etc/libblockdev").
|
||||
Link(fst.Tmp+"/etc/locale.conf", "/etc/locale.conf").
|
||||
Link(fst.Tmp+"/etc/localtime", "/etc/localtime").
|
||||
Link(fst.Tmp+"/etc/login.defs", "/etc/login.defs").
|
||||
Link(fst.Tmp+"/etc/lsb-release", "/etc/lsb-release").
|
||||
Link(fst.Tmp+"/etc/lvm", "/etc/lvm").
|
||||
Link(fst.Tmp+"/etc/machine-id", "/etc/machine-id").
|
||||
Link(fst.Tmp+"/etc/man_db.conf", "/etc/man_db.conf").
|
||||
Link(fst.Tmp+"/etc/modprobe.d", "/etc/modprobe.d").
|
||||
Link(fst.Tmp+"/etc/modules-load.d", "/etc/modules-load.d").
|
||||
Link("/proc/mounts", "/etc/mtab").
|
||||
Link(fst.Tmp+"/etc/nanorc", "/etc/nanorc").
|
||||
Link(fst.Tmp+"/etc/netgroup", "/etc/netgroup").
|
||||
Link(fst.Tmp+"/etc/NetworkManager", "/etc/NetworkManager").
|
||||
Link(fst.Tmp+"/etc/nix", "/etc/nix").
|
||||
Link(fst.Tmp+"/etc/nixos", "/etc/nixos").
|
||||
Link(fst.Tmp+"/etc/NIXOS", "/etc/NIXOS").
|
||||
Link(fst.Tmp+"/etc/nscd.conf", "/etc/nscd.conf").
|
||||
Link(fst.Tmp+"/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||
Link(fst.Tmp+"/etc/opensnitchd", "/etc/opensnitchd").
|
||||
Link(fst.Tmp+"/etc/os-release", "/etc/os-release").
|
||||
Link(fst.Tmp+"/etc/pam", "/etc/pam").
|
||||
Link(fst.Tmp+"/etc/pam.d", "/etc/pam.d").
|
||||
Link(fst.Tmp+"/etc/pipewire", "/etc/pipewire").
|
||||
Link(fst.Tmp+"/etc/pki", "/etc/pki").
|
||||
Link(fst.Tmp+"/etc/polkit-1", "/etc/polkit-1").
|
||||
Link(fst.Tmp+"/etc/profile", "/etc/profile").
|
||||
Link(fst.Tmp+"/etc/protocols", "/etc/protocols").
|
||||
Link(fst.Tmp+"/etc/qemu", "/etc/qemu").
|
||||
Link(fst.Tmp+"/etc/resolv.conf", "/etc/resolv.conf").
|
||||
Link(fst.Tmp+"/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||
Link(fst.Tmp+"/etc/rpc", "/etc/rpc").
|
||||
Link(fst.Tmp+"/etc/samba", "/etc/samba").
|
||||
Link(fst.Tmp+"/etc/sddm.conf", "/etc/sddm.conf").
|
||||
Link(fst.Tmp+"/etc/secureboot", "/etc/secureboot").
|
||||
Link(fst.Tmp+"/etc/services", "/etc/services").
|
||||
Link(fst.Tmp+"/etc/set-environment", "/etc/set-environment").
|
||||
Link(fst.Tmp+"/etc/shadow", "/etc/shadow").
|
||||
Link(fst.Tmp+"/etc/shells", "/etc/shells").
|
||||
Link(fst.Tmp+"/etc/ssh", "/etc/ssh").
|
||||
Link(fst.Tmp+"/etc/ssl", "/etc/ssl").
|
||||
Link(fst.Tmp+"/etc/static", "/etc/static").
|
||||
Link(fst.Tmp+"/etc/subgid", "/etc/subgid").
|
||||
Link(fst.Tmp+"/etc/subuid", "/etc/subuid").
|
||||
Link(fst.Tmp+"/etc/sudoers", "/etc/sudoers").
|
||||
Link(fst.Tmp+"/etc/sysctl.d", "/etc/sysctl.d").
|
||||
Link(fst.Tmp+"/etc/systemd", "/etc/systemd").
|
||||
Link(fst.Tmp+"/etc/terminfo", "/etc/terminfo").
|
||||
Link(fst.Tmp+"/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||
Link(fst.Tmp+"/etc/udev", "/etc/udev").
|
||||
Link(fst.Tmp+"/etc/udisks2", "/etc/udisks2").
|
||||
Link(fst.Tmp+"/etc/UPower", "/etc/UPower").
|
||||
Link(fst.Tmp+"/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||
Link(fst.Tmp+"/etc/X11", "/etc/X11").
|
||||
Link(fst.Tmp+"/etc/zfs", "/etc/zfs").
|
||||
Link(fst.Tmp+"/etc/zinputrc", "/etc/zinputrc").
|
||||
Link(fst.Tmp+"/etc/zoneinfo", "/etc/zoneinfo").
|
||||
Link(fst.Tmp+"/etc/zprofile", "/etc/zprofile").
|
||||
Link(fst.Tmp+"/etc/zshenv", "/etc/zshenv").
|
||||
Link(fst.Tmp+"/etc/zshrc", "/etc/zshrc").
|
||||
Tmpfs("/run/user", 4096, 0755).
|
||||
Tmpfs("/run/user/65534", 8388608, 0755).
|
||||
Bind("/tmp/fortify.1971/tmpdir/9", "/tmp", sandbox.BindWritable).
|
||||
Bind("/home/chronos", "/home/chronos", sandbox.BindWritable).
|
||||
Place("/etc/passwd", []byte("chronos:x:65534:65534:Fortify:/home/chronos:/run/current-system/sw/bin/zsh\n")).
|
||||
Place("/etc/group", []byte("fortify:x:65534:\n")).
|
||||
Bind("/tmp/fortify.1971/wayland/ebf083d1b175911782d413369b64ce7c", "/run/user/65534/wayland-0", 0).
|
||||
Bind("/run/user/1971/fortify/ebf083d1b175911782d413369b64ce7c/pulse", "/run/user/65534/pulse/native", 0).
|
||||
Place(fst.Tmp+"/pulse-cookie", nil).
|
||||
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/bus", "/run/user/65534/bus", 0).
|
||||
Bind("/tmp/fortify.1971/ebf083d1b175911782d413369b64ce7c/system_bus_socket", "/run/dbus/system_bus_socket", 0).
|
||||
Tmpfs("/var/run/nscd", 8192, 0755),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -55,10 +55,8 @@ func (s *stubNixOS) LookPath(file string) (string, error) {
|
||||
}
|
||||
|
||||
switch file {
|
||||
case "sudo":
|
||||
return "/run/wrappers/bin/sudo", nil
|
||||
case "machinectl":
|
||||
return "/home/ophestra/.nix-profile/bin/machinectl", nil
|
||||
case "zsh":
|
||||
return "/run/current-system/sw/bin/zsh", nil
|
||||
default:
|
||||
panic(fmt.Sprintf("attempted to look up unexpected executable %q", file))
|
||||
}
|
||||
|
||||
@@ -8,19 +8,19 @@ import (
|
||||
"time"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/internal/app"
|
||||
"git.gensokyo.uk/security/fortify/internal/sys"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
"git.gensokyo.uk/security/fortify/system"
|
||||
)
|
||||
|
||||
type sealTestCase struct {
|
||||
name string
|
||||
os sys.State
|
||||
config *fst.Config
|
||||
id fst.ID
|
||||
wantSys *system.I
|
||||
wantBwrap *bwrap.Config
|
||||
name string
|
||||
os sys.State
|
||||
config *fst.Config
|
||||
id fst.ID
|
||||
wantSys *system.I
|
||||
wantContainer *sandbox.Params
|
||||
}
|
||||
|
||||
func TestApp(t *testing.T) {
|
||||
@@ -30,15 +30,15 @@ func TestApp(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
a := app.NewWithID(tc.id, tc.os)
|
||||
var (
|
||||
gotSys *system.I
|
||||
gotBwrap *bwrap.Config
|
||||
gotSys *system.I
|
||||
gotContainer *sandbox.Params
|
||||
)
|
||||
if !t.Run("seal", func(t *testing.T) {
|
||||
if sa, err := a.Seal(tc.config); err != nil {
|
||||
t.Errorf("Seal: error = %v", err)
|
||||
return
|
||||
} else {
|
||||
gotSys, gotBwrap = app.AppSystemBwrap(a, sa)
|
||||
gotSys, gotContainer = app.AppIParams(a, sa)
|
||||
}
|
||||
}) {
|
||||
return
|
||||
@@ -51,10 +51,10 @@ func TestApp(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("compare bwrap", func(t *testing.T) {
|
||||
if !reflect.DeepEqual(gotBwrap, tc.wantBwrap) {
|
||||
t.Errorf("seal: bwrap =\n%s\n, want\n%s",
|
||||
mustMarshal(gotBwrap), mustMarshal(tc.wantBwrap))
|
||||
t.Run("compare params", func(t *testing.T) {
|
||||
if !reflect.DeepEqual(gotContainer, tc.wantContainer) {
|
||||
t.Errorf("seal: params =\n%s\n, want\n%s",
|
||||
mustMarshal(gotContainer), mustMarshal(tc.wantContainer))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,8 +2,8 @@ package app
|
||||
|
||||
import (
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/internal/sys"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
"git.gensokyo.uk/security/fortify/system"
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ func NewWithID(id fst.ID, os sys.State) fst.App {
|
||||
return a
|
||||
}
|
||||
|
||||
func AppSystemBwrap(a fst.App, sa fst.SealedApp) (*system.I, *bwrap.Config) {
|
||||
func AppIParams(a fst.App, sa fst.SealedApp) (*system.I, *sandbox.Params) {
|
||||
v := a.(*app)
|
||||
seal := sa.(*outcome)
|
||||
if v.outcome != seal || v.id != seal.id {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package init0
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
)
|
||||
|
||||
// used by the parent process
|
||||
|
||||
// TryArgv0 calls [Main] if the last element of argv0 is "init0".
|
||||
func TryArgv0() {
|
||||
if len(os.Args) > 0 && path.Base(os.Args[0]) == "init0" {
|
||||
Main()
|
||||
internal.Exit(0)
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package init0
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
)
|
||||
|
||||
const (
|
||||
// time to wait for linger processes after death of initial process
|
||||
residualProcessTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// everything beyond this point runs within pid namespace
|
||||
// proceed with caution!
|
||||
|
||||
func Main() {
|
||||
// sharing stdout with shim
|
||||
// USE WITH CAUTION
|
||||
fmsg.Prepare("init0")
|
||||
|
||||
// setting this prevents ptrace
|
||||
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
}
|
||||
|
||||
if os.Getpid() != 1 {
|
||||
log.Fatal("this process must run as pid 1")
|
||||
}
|
||||
|
||||
// receive setup payload
|
||||
var (
|
||||
payload Payload
|
||||
closeSetup func() error
|
||||
)
|
||||
if f, err := sandbox.Receive(Env, &payload, nil); err != nil {
|
||||
if errors.Is(err, sandbox.ErrInvalid) {
|
||||
log.Fatal("invalid config descriptor")
|
||||
}
|
||||
if errors.Is(err, sandbox.ErrNotSet) {
|
||||
log.Fatal("FORTIFY_INIT not set")
|
||||
}
|
||||
|
||||
log.Fatalf("cannot decode init setup payload: %v", err)
|
||||
} else {
|
||||
fmsg.Store(payload.Verbose)
|
||||
closeSetup = f
|
||||
|
||||
// child does not need to see this
|
||||
if err = os.Unsetenv(Env); err != nil {
|
||||
log.Printf("cannot unset %s: %v", Env, err)
|
||||
// not fatal
|
||||
} else {
|
||||
fmsg.Verbose("received configuration")
|
||||
}
|
||||
}
|
||||
|
||||
// die with parent
|
||||
if err := sandbox.SetPdeathsig(syscall.SIGKILL); err != nil {
|
||||
log.Fatalf("prctl(PR_SET_PDEATHSIG, SIGKILL): %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(payload.Argv0)
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
cmd.Args = payload.Argv
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("cannot start %q: %v", payload.Argv0, err)
|
||||
}
|
||||
fmsg.Suspend()
|
||||
|
||||
// close setup pipe as setup is now complete
|
||||
if err := closeSetup(); err != nil {
|
||||
log.Println("cannot close setup pipe:", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
sig := make(chan os.Signal, 2)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
type winfo struct {
|
||||
wpid int
|
||||
wstatus syscall.WaitStatus
|
||||
}
|
||||
info := make(chan winfo, 1)
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
var (
|
||||
err error
|
||||
wpid = -2
|
||||
wstatus syscall.WaitStatus
|
||||
)
|
||||
|
||||
// keep going until no child process is left
|
||||
for wpid != -1 {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if wpid != -2 {
|
||||
info <- winfo{wpid, wstatus}
|
||||
}
|
||||
|
||||
err = syscall.EINTR
|
||||
for errors.Is(err, syscall.EINTR) {
|
||||
wpid, err = syscall.Wait4(-1, &wstatus, 0, nil)
|
||||
}
|
||||
}
|
||||
if !errors.Is(err, syscall.ECHILD) {
|
||||
log.Println("unexpected wait4 response:", err)
|
||||
}
|
||||
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// closed after residualProcessTimeout has elapsed after initial process death
|
||||
timeout := make(chan struct{})
|
||||
|
||||
r := 2
|
||||
for {
|
||||
select {
|
||||
case s := <-sig:
|
||||
if fmsg.Resume() {
|
||||
fmsg.Verbosef("terminating on %s after process start", s.String())
|
||||
} else {
|
||||
fmsg.Verbosef("terminating on %s", s.String())
|
||||
}
|
||||
internal.Exit(0)
|
||||
case w := <-info:
|
||||
if w.wpid == cmd.Process.Pid {
|
||||
// initial process exited, output is most likely available again
|
||||
fmsg.Resume()
|
||||
|
||||
switch {
|
||||
case w.wstatus.Exited():
|
||||
r = w.wstatus.ExitStatus()
|
||||
case w.wstatus.Signaled():
|
||||
r = 128 + int(w.wstatus.Signal())
|
||||
default:
|
||||
r = 255
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(residualProcessTimeout)
|
||||
close(timeout)
|
||||
}()
|
||||
}
|
||||
case <-done:
|
||||
internal.Exit(r)
|
||||
case <-timeout:
|
||||
log.Println("timeout exceeded waiting for lingering processes")
|
||||
internal.Exit(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package init0
|
||||
|
||||
const Env = "FORTIFY_INIT"
|
||||
|
||||
type Payload struct {
|
||||
// target full exec path
|
||||
Argv0 string
|
||||
// child full argv
|
||||
Argv []string
|
||||
|
||||
// verbosity pass through
|
||||
Verbose bool
|
||||
}
|
||||
@@ -3,15 +3,12 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/app/shim"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
@@ -21,7 +18,7 @@ import (
|
||||
|
||||
const shimSetupTimeout = 5 * time.Second
|
||||
|
||||
func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
func (seal *outcome) Run(rs *fst.RunState) error {
|
||||
if !seal.f.CompareAndSwap(false, true) {
|
||||
// run does much more than just starting a process; calling it twice, even if the first call fails, will result
|
||||
// in inconsistent state that is impossible to clean up; return here to limit damage and hopefully give the
|
||||
@@ -37,33 +34,11 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
fmsg.Verbosef("version %s", internal.Version())
|
||||
fmsg.Verbosef("setuid helper at %s", internal.MustFsuPath())
|
||||
|
||||
/*
|
||||
resolve exec paths
|
||||
*/
|
||||
|
||||
shimExec := [2]string{helper.BubblewrapName}
|
||||
if len(seal.command) > 0 {
|
||||
shimExec[1] = seal.command[0]
|
||||
}
|
||||
for i, n := range shimExec {
|
||||
if len(n) == 0 {
|
||||
continue
|
||||
}
|
||||
if filepath.Base(n) == n {
|
||||
if s, err := exec.LookPath(n); err == nil {
|
||||
shimExec[i] = s
|
||||
} else {
|
||||
return fmsg.WrapError(err,
|
||||
fmt.Sprintf("executable file %q not found in $PATH", n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
prepare/revert os state
|
||||
*/
|
||||
|
||||
if err := seal.sys.Commit(ctx); err != nil {
|
||||
if err := seal.sys.Commit(seal.ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
store := state.NewMulti(seal.runDirPath)
|
||||
@@ -137,7 +112,6 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
if startTime, err := cmd.Start(
|
||||
seal.user.aid.String(),
|
||||
seal.user.supp,
|
||||
seal.bwrapSync,
|
||||
); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -145,7 +119,7 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
rs.Time = startTime
|
||||
}
|
||||
|
||||
c, cancel := context.WithTimeout(ctx, shimSetupTimeout)
|
||||
ctx, cancel := context.WithTimeout(seal.ctx, shimSetupTimeout)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
@@ -154,11 +128,9 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
if err := cmd.Serve(c, &shim.Payload{
|
||||
Argv: seal.command,
|
||||
Exec: shimExec,
|
||||
Bwrap: seal.container,
|
||||
Home: seal.user.data,
|
||||
if err := cmd.Serve(ctx, &shim.Params{
|
||||
Container: seal.container,
|
||||
Home: seal.user.data,
|
||||
|
||||
Verbose: fmsg.Load(),
|
||||
}); err != nil {
|
||||
@@ -199,18 +171,22 @@ func (seal *outcome) Run(ctx context.Context, rs *fst.RunState) error {
|
||||
// this is reached when a fault makes an already running shim impossible to continue execution
|
||||
// however a kill signal could not be delivered (should actually always happen like that since fsu)
|
||||
// the effects of this is similar to the alternative exit path and ensures shim death
|
||||
case err := <-cmd.WaitFallback():
|
||||
case err := <-cmd.Fallback():
|
||||
rs.ExitCode = 255
|
||||
log.Printf("cannot terminate shim on faulted setup: %v", err)
|
||||
|
||||
// alternative exit path relying on shim behaviour on monitor process exit
|
||||
case <-ctx.Done():
|
||||
case <-seal.ctx.Done():
|
||||
fmsg.Verbose("alternative exit path selected")
|
||||
}
|
||||
|
||||
fmsg.Resume()
|
||||
if seal.sync != nil {
|
||||
if err := seal.sync.Close(); err != nil {
|
||||
log.Printf("cannot close wayland security context: %v", err)
|
||||
}
|
||||
}
|
||||
if seal.dbusMsg != nil {
|
||||
// dump dbus message buffer
|
||||
seal.dbusMsg()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,24 +2,28 @@ package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/acl"
|
||||
"git.gensokyo.uk/security/fortify/dbus"
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/internal/sys"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
"git.gensokyo.uk/security/fortify/system"
|
||||
"git.gensokyo.uk/security/fortify/wl"
|
||||
)
|
||||
@@ -65,19 +69,19 @@ type outcome struct {
|
||||
// copied from [sys.State] response
|
||||
runDirPath string
|
||||
|
||||
// passed through from [fst.Config]
|
||||
command []string
|
||||
|
||||
// initial [fst.Config] gob stream for state data;
|
||||
// this is prepared ahead of time as config is mutated during seal creation
|
||||
// this is prepared ahead of time as config is clobbered during seal creation
|
||||
ct io.WriterTo
|
||||
// dump dbus proxy message buffer
|
||||
dbusMsg func()
|
||||
|
||||
user fsuUser
|
||||
sys *system.I
|
||||
container *bwrap.Config
|
||||
bwrapSync *os.File
|
||||
user fsuUser
|
||||
sys *system.I
|
||||
ctx context.Context
|
||||
|
||||
container *sandbox.Params
|
||||
env map[string]string
|
||||
sync *os.File
|
||||
|
||||
f atomic.Bool
|
||||
}
|
||||
@@ -100,7 +104,17 @@ type fsuUser struct {
|
||||
username string
|
||||
}
|
||||
|
||||
func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *fst.Config) error {
|
||||
if seal.ctx != nil {
|
||||
panic("finalise called twice")
|
||||
}
|
||||
seal.ctx = ctx
|
||||
|
||||
shellPath := "/bin/sh"
|
||||
if s, ok := sys.LookupEnv(shell); ok && path.IsAbs(s) {
|
||||
shellPath = s
|
||||
}
|
||||
|
||||
{
|
||||
// encode initial configuration for state tracking
|
||||
ct := new(bytes.Buffer)
|
||||
@@ -111,9 +125,6 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
seal.ct = ct
|
||||
}
|
||||
|
||||
// pass through command slice; this value is never touched in the main process
|
||||
seal.command = config.Command
|
||||
|
||||
// allowed aid range 0 to 9999, this is checked again in fsu
|
||||
if config.Confinement.AppID < 0 || config.Confinement.AppID > 9999 {
|
||||
return fmsg.WrapError(ErrUser,
|
||||
@@ -167,12 +178,24 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
if config.Confinement.Sandbox == nil {
|
||||
fmsg.Verbose("sandbox configuration not supplied, PROCEED WITH CAUTION")
|
||||
|
||||
// fsu clears the environment so resolve paths early
|
||||
if !path.IsAbs(config.Path) {
|
||||
if len(config.Args) > 0 {
|
||||
if p, err := sys.LookPath(config.Args[0]); err != nil {
|
||||
return fmsg.WrapError(err, err.Error())
|
||||
} else {
|
||||
config.Path = p
|
||||
}
|
||||
} else {
|
||||
config.Path = shellPath
|
||||
}
|
||||
}
|
||||
|
||||
conf := &fst.SandboxConfig{
|
||||
UserNS: true,
|
||||
Net: true,
|
||||
Syscall: new(bwrap.SyscallPolicy),
|
||||
NoNewSession: true,
|
||||
AutoEtc: true,
|
||||
Userns: true,
|
||||
Net: true,
|
||||
Tty: true,
|
||||
AutoEtc: true,
|
||||
}
|
||||
// bind entries in /
|
||||
if d, err := sys.ReadDir("/"); err != nil {
|
||||
@@ -198,7 +221,7 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
// hide nscd from sandbox if present
|
||||
nscd := "/var/run/nscd"
|
||||
if _, err := sys.Stat(nscd); !errors.Is(err, fs.ErrNotExist) {
|
||||
conf.Override = append(conf.Override, nscd)
|
||||
conf.Cover = append(conf.Cover, nscd)
|
||||
}
|
||||
// bind GPU stuff
|
||||
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) {
|
||||
@@ -210,17 +233,29 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
config.Confinement.Sandbox = conf
|
||||
}
|
||||
|
||||
var mapuid *stringPair[int]
|
||||
var mapuid, mapgid *stringPair[int]
|
||||
{
|
||||
var uid int
|
||||
var uid, gid int
|
||||
var err error
|
||||
seal.container, err = config.Confinement.Sandbox.Bwrap(sys, &uid)
|
||||
seal.container, seal.env, err = config.Confinement.Sandbox.ToContainer(sys, &uid, &gid)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmsg.WrapErrorSuffix(err,
|
||||
"cannot initialise container configuration:")
|
||||
}
|
||||
if !path.IsAbs(config.Path) {
|
||||
return fmsg.WrapError(syscall.EINVAL,
|
||||
"invalid program path")
|
||||
}
|
||||
if len(config.Args) == 0 {
|
||||
config.Args = []string{config.Path}
|
||||
}
|
||||
seal.container.Path = config.Path
|
||||
seal.container.Args = config.Args
|
||||
|
||||
mapuid = newInt(uid)
|
||||
if seal.container.SetEnv == nil {
|
||||
seal.container.SetEnv = make(map[string]string)
|
||||
mapgid = newInt(gid)
|
||||
if seal.env == nil {
|
||||
seal.env = make(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,35 +290,27 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
|
||||
// inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` as post-fsu user
|
||||
innerRuntimeDir := path.Join("/run/user", mapuid.String())
|
||||
seal.container.Tmpfs("/run/user", 1*1024*1024)
|
||||
seal.container.Tmpfs(innerRuntimeDir, 8*1024*1024)
|
||||
seal.container.SetEnv[xdgRuntimeDir] = innerRuntimeDir
|
||||
seal.container.SetEnv[xdgSessionClass] = "user"
|
||||
seal.container.SetEnv[xdgSessionType] = "tty"
|
||||
seal.container.Tmpfs("/run/user", 1<<12, 0755)
|
||||
seal.container.Tmpfs(innerRuntimeDir, 1<<23, 0755)
|
||||
seal.env[xdgRuntimeDir] = innerRuntimeDir
|
||||
seal.env[xdgSessionClass] = "user"
|
||||
seal.env[xdgSessionType] = "tty"
|
||||
|
||||
// outer path for inner /tmp
|
||||
{
|
||||
tmpdir := path.Join(sc.SharePath, "tmpdir")
|
||||
seal.sys.Ensure(tmpdir, 0700)
|
||||
seal.sys.UpdatePermType(system.User, tmpdir, acl.Execute)
|
||||
tmpdirProc := path.Join(tmpdir, seal.user.aid.String())
|
||||
seal.sys.Ensure(tmpdirProc, 01700)
|
||||
seal.sys.UpdatePermType(system.User, tmpdirProc, acl.Read, acl.Write, acl.Execute)
|
||||
seal.container.Bind(tmpdirProc, "/tmp", false, true)
|
||||
tmpdirInst := path.Join(tmpdir, seal.user.aid.String())
|
||||
seal.sys.Ensure(tmpdirInst, 01700)
|
||||
seal.sys.UpdatePermType(system.User, tmpdirInst, acl.Read, acl.Write, acl.Execute)
|
||||
seal.container.Bind(tmpdirInst, "/tmp", sandbox.BindWritable)
|
||||
}
|
||||
|
||||
/*
|
||||
Passwd database
|
||||
*/
|
||||
|
||||
// look up shell
|
||||
sh := "/bin/sh"
|
||||
if s, ok := sys.LookupEnv(shell); ok {
|
||||
seal.container.SetEnv[shell] = s
|
||||
sh = s
|
||||
}
|
||||
|
||||
// bind home directory
|
||||
homeDir := "/var/empty"
|
||||
if seal.user.home != "" {
|
||||
homeDir = seal.user.home
|
||||
@@ -292,27 +319,25 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
if seal.user.username != "" {
|
||||
username = seal.user.username
|
||||
}
|
||||
seal.container.Bind(seal.user.data, homeDir, false, true)
|
||||
seal.container.Chdir = homeDir
|
||||
seal.container.SetEnv["HOME"] = homeDir
|
||||
seal.container.SetEnv["USER"] = username
|
||||
seal.container.Bind(seal.user.data, homeDir, sandbox.BindWritable)
|
||||
seal.container.Dir = homeDir
|
||||
seal.env["HOME"] = homeDir
|
||||
seal.env["USER"] = username
|
||||
|
||||
// generate /etc/passwd and /etc/group
|
||||
seal.container.CopyBind("/etc/passwd",
|
||||
[]byte(username+":x:"+mapuid.String()+":"+mapuid.String()+":Fortify:"+homeDir+":"+sh+"\n"))
|
||||
seal.container.CopyBind("/etc/group",
|
||||
[]byte("fortify:x:"+mapuid.String()+":\n"))
|
||||
seal.container.Place("/etc/passwd",
|
||||
[]byte(username+":x:"+mapuid.String()+":"+mapgid.String()+":Fortify:"+homeDir+":"+shellPath+"\n"))
|
||||
seal.container.Place("/etc/group",
|
||||
[]byte("fortify:x:"+mapgid.String()+":\n"))
|
||||
|
||||
/*
|
||||
Display servers
|
||||
*/
|
||||
|
||||
// pass $TERM to launcher
|
||||
// pass $TERM for proper terminal I/O in shell
|
||||
if t, ok := sys.LookupEnv(term); ok {
|
||||
seal.container.SetEnv[term] = t
|
||||
seal.env[term] = t
|
||||
}
|
||||
|
||||
// set up wayland
|
||||
if config.Confinement.Enablements.Has(system.EWayland) {
|
||||
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
|
||||
var socketPath string
|
||||
@@ -326,7 +351,7 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
}
|
||||
|
||||
innerPath := path.Join(innerRuntimeDir, wl.FallbackName)
|
||||
seal.container.SetEnv[wl.WaylandDisplay] = wl.FallbackName
|
||||
seal.env[wl.WaylandDisplay] = wl.FallbackName
|
||||
|
||||
if !config.Confinement.Sandbox.DirectWayland { // set up security-context-v1
|
||||
socketDir := path.Join(sc.SharePath, "wayland")
|
||||
@@ -337,25 +362,23 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
// use instance ID in case app id is not set
|
||||
appID = "uk.gensokyo.fortify." + seal.id.String()
|
||||
}
|
||||
seal.sys.Wayland(&seal.bwrapSync, outerPath, socketPath, appID, seal.id.String())
|
||||
seal.container.Bind(outerPath, innerPath)
|
||||
seal.sys.Wayland(&seal.sync, outerPath, socketPath, appID, seal.id.String())
|
||||
seal.container.Bind(outerPath, innerPath, 0)
|
||||
} else { // bind mount wayland socket (insecure)
|
||||
fmsg.Verbose("direct wayland access, PROCEED WITH CAUTION")
|
||||
seal.container.Bind(socketPath, innerPath)
|
||||
seal.container.Bind(socketPath, innerPath, 0)
|
||||
seal.sys.UpdatePermType(system.EWayland, socketPath, acl.Read, acl.Write, acl.Execute)
|
||||
}
|
||||
}
|
||||
|
||||
// set up X11
|
||||
if config.Confinement.Enablements.Has(system.EX11) {
|
||||
// discover X11 and grant user permission via the `ChangeHosts` command
|
||||
if d, ok := sys.LookupEnv(display); !ok {
|
||||
return fmsg.WrapError(ErrXDisplay,
|
||||
"DISPLAY is not set")
|
||||
} else {
|
||||
seal.sys.ChangeHosts("#" + seal.user.uid.String())
|
||||
seal.container.SetEnv[display] = d
|
||||
seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix")
|
||||
seal.env[display] = d
|
||||
seal.container.Bind("/tmp/.X11-unix", "/tmp/.X11-unix", 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,8 +419,8 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
innerPulseRuntimeDir := path.Join(sharePathLocal, "pulse")
|
||||
innerPulseSocket := path.Join(innerRuntimeDir, "pulse", "native")
|
||||
seal.sys.Link(pulseSocket, innerPulseRuntimeDir)
|
||||
seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket)
|
||||
seal.container.SetEnv[pulseServer] = "unix:" + innerPulseSocket
|
||||
seal.container.Bind(innerPulseRuntimeDir, innerPulseSocket, 0)
|
||||
seal.env[pulseServer] = "unix:" + innerPulseSocket
|
||||
|
||||
// publish current user's pulse cookie for target user
|
||||
if src, err := discoverPulseCookie(sys); err != nil {
|
||||
@@ -405,9 +428,9 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
fmsg.Verbose(strings.TrimSpace(err.(*fmsg.BaseError).Message()))
|
||||
} else {
|
||||
innerDst := fst.Tmp + "/pulse-cookie"
|
||||
seal.container.SetEnv[pulseCookie] = innerDst
|
||||
payload := new([]byte)
|
||||
seal.container.CopyBindRef(innerDst, &payload)
|
||||
seal.env[pulseCookie] = innerDst
|
||||
var payload *[]byte
|
||||
seal.container.PlaceP(innerDst, &payload)
|
||||
seal.sys.CopyFile(payload, src, 256, 256)
|
||||
}
|
||||
}
|
||||
@@ -437,13 +460,13 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
|
||||
// share proxy sockets
|
||||
sessionInner := path.Join(innerRuntimeDir, "bus")
|
||||
seal.container.SetEnv[dbusSessionBusAddress] = "unix:path=" + sessionInner
|
||||
seal.container.Bind(sessionPath, sessionInner)
|
||||
seal.env[dbusSessionBusAddress] = "unix:path=" + sessionInner
|
||||
seal.container.Bind(sessionPath, sessionInner, 0)
|
||||
seal.sys.UpdatePerm(sessionPath, acl.Read, acl.Write)
|
||||
if config.Confinement.SystemBus != nil {
|
||||
systemInner := "/run/dbus/system_bus_socket"
|
||||
seal.container.SetEnv[dbusSystemBusAddress] = "unix:path=" + systemInner
|
||||
seal.container.Bind(systemPath, systemInner)
|
||||
seal.env[dbusSystemBusAddress] = "unix:path=" + systemInner
|
||||
seal.container.Bind(systemPath, systemInner, 0)
|
||||
seal.sys.UpdatePerm(systemPath, acl.Read, acl.Write)
|
||||
}
|
||||
}
|
||||
@@ -452,9 +475,8 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
Miscellaneous
|
||||
*/
|
||||
|
||||
// queue overriding tmpfs at the end of seal.container.Filesystem
|
||||
for _, dest := range config.Confinement.Sandbox.Override {
|
||||
seal.container.Tmpfs(dest, 8*1024)
|
||||
for _, dest := range config.Confinement.Sandbox.Cover {
|
||||
seal.container.Tmpfs(dest, 1<<13, 0755)
|
||||
}
|
||||
|
||||
// append ExtraPerms last
|
||||
@@ -480,12 +502,13 @@ func (seal *outcome) finalise(sys sys.State, config *fst.Config) error {
|
||||
seal.sys.UpdatePermType(system.User, p.Path, perms...)
|
||||
}
|
||||
|
||||
// mount fortify in sandbox for init
|
||||
seal.container.Bind(sys.MustExecutable(), path.Join(fst.Tmp, "sbin/fortify"))
|
||||
seal.container.Symlink("fortify", path.Join(fst.Tmp, "sbin/init0"))
|
||||
// flatten and sort env for deterministic behaviour
|
||||
seal.container.Env = make([]string, 0, len(seal.env))
|
||||
maps.All(seal.env)(func(k string, v string) bool { seal.container.Env = append(seal.container.Env, k+"="+v); return true })
|
||||
slices.Sort(seal.container.Env)
|
||||
|
||||
fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, command: %s",
|
||||
seal.user.uid, seal.user.username, config.Confinement.Groups, config.Command)
|
||||
fmsg.Verbosef("created application seal for uid %s (%s) groups: %v, argv: %s",
|
||||
seal.user.uid, seal.user.username, config.Confinement.Groups, seal.container.Args)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,18 +7,26 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/fst"
|
||||
"git.gensokyo.uk/security/fortify/helper"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/app/init0"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
)
|
||||
|
||||
const Env = "FORTIFY_SHIM"
|
||||
|
||||
type Params struct {
|
||||
// finalised container params
|
||||
Container *sandbox.Params
|
||||
// path to outer home directory
|
||||
Home string
|
||||
|
||||
// verbosity pass through
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// everything beyond this point runs as unconstrained target user
|
||||
// proceed with caution!
|
||||
|
||||
@@ -27,17 +35,15 @@ func Main() {
|
||||
// USE WITH CAUTION
|
||||
fmsg.Prepare("shim")
|
||||
|
||||
// setting this prevents ptrace
|
||||
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
}
|
||||
|
||||
// receive setup payload
|
||||
var (
|
||||
payload Payload
|
||||
params Params
|
||||
closeSetup func() error
|
||||
)
|
||||
if f, err := sandbox.Receive(Env, &payload, nil); err != nil {
|
||||
if f, err := sandbox.Receive(Env, ¶ms, nil); err != nil {
|
||||
if errors.Is(err, sandbox.ErrInvalid) {
|
||||
log.Fatal("invalid config descriptor")
|
||||
}
|
||||
@@ -45,32 +51,26 @@ func Main() {
|
||||
log.Fatal("FORTIFY_SHIM not set")
|
||||
}
|
||||
|
||||
log.Fatalf("cannot decode shim setup payload: %v", err)
|
||||
log.Fatalf("cannot receive shim setup params: %v", err)
|
||||
} else {
|
||||
internal.InstallFmsg(payload.Verbose)
|
||||
internal.InstallFmsg(params.Verbose)
|
||||
closeSetup = f
|
||||
}
|
||||
|
||||
if payload.Bwrap == nil {
|
||||
log.Fatal("bwrap config not supplied")
|
||||
}
|
||||
|
||||
// restore bwrap sync fd
|
||||
var syncFd *os.File
|
||||
if payload.Sync != nil {
|
||||
syncFd = os.NewFile(*payload.Sync, "sync")
|
||||
if params.Container == nil || params.Container.Ops == nil {
|
||||
log.Fatal("invalid container params")
|
||||
}
|
||||
|
||||
// close setup socket
|
||||
if err := closeSetup(); err != nil {
|
||||
log.Println("cannot close setup pipe:", err)
|
||||
log.Printf("cannot close setup pipe: %v", err)
|
||||
// not fatal
|
||||
}
|
||||
|
||||
// ensure home directory as target user
|
||||
if s, err := os.Stat(payload.Home); err != nil {
|
||||
if s, err := os.Stat(params.Home); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.Mkdir(payload.Home, 0700); err != nil {
|
||||
if err = os.Mkdir(params.Home, 0700); err != nil {
|
||||
log.Fatalf("cannot create home directory: %v", err)
|
||||
}
|
||||
} else {
|
||||
@@ -79,72 +79,37 @@ func Main() {
|
||||
|
||||
// home directory is created, proceed
|
||||
} else if !s.IsDir() {
|
||||
log.Fatalf("data path %q is not a directory", payload.Home)
|
||||
log.Fatalf("path %q is not a directory", params.Home)
|
||||
}
|
||||
|
||||
var ic init0.Payload
|
||||
|
||||
// resolve argv0
|
||||
ic.Argv = payload.Argv
|
||||
if len(ic.Argv) > 0 {
|
||||
// looked up from $PATH by parent
|
||||
ic.Argv0 = payload.Exec[1]
|
||||
} else {
|
||||
// no argv, look up shell instead
|
||||
var ok bool
|
||||
if payload.Bwrap.SetEnv == nil {
|
||||
log.Fatal("no command was specified and environment is unset")
|
||||
}
|
||||
if ic.Argv0, ok = payload.Bwrap.SetEnv["SHELL"]; !ok {
|
||||
log.Fatal("no command was specified and $SHELL was unset")
|
||||
}
|
||||
|
||||
ic.Argv = []string{ic.Argv0}
|
||||
var name string
|
||||
if len(params.Container.Args) > 0 {
|
||||
name = params.Container.Args[0]
|
||||
}
|
||||
|
||||
conf := payload.Bwrap
|
||||
|
||||
var extraFiles []*os.File
|
||||
|
||||
// serve setup payload
|
||||
if fd, encoder, err := sandbox.Setup(&extraFiles); err != nil {
|
||||
log.Fatalf("cannot pipe: %v", err)
|
||||
} else {
|
||||
conf.SetEnv[init0.Env] = strconv.Itoa(fd)
|
||||
go func() {
|
||||
fmsg.Verbose("transmitting config to init")
|
||||
if err = encoder.Encode(&ic); err != nil {
|
||||
log.Fatalf("cannot transmit init config: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop() // unreachable
|
||||
if b, err := helper.NewBwrap(
|
||||
ctx, path.Join(fst.Tmp, "sbin/init0"),
|
||||
nil, false,
|
||||
func(int, int) []string { return make([]string, 0) },
|
||||
func(cmd *exec.Cmd) { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr },
|
||||
extraFiles,
|
||||
conf, syncFd,
|
||||
); err != nil {
|
||||
log.Fatalf("malformed sandbox config: %v", err)
|
||||
} else {
|
||||
// run and pass through exit code
|
||||
if err = b.Start(); err != nil {
|
||||
log.Fatalf("cannot start target process: %v", err)
|
||||
} else if err = b.Wait(); err != nil {
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) {
|
||||
log.Printf("wait: %v", err)
|
||||
internal.Exit(127)
|
||||
panic("unreachable")
|
||||
container := sandbox.New(ctx, name)
|
||||
container.Params = *params.Container
|
||||
container.Stdin, container.Stdout, container.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
container.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
|
||||
container.WaitDelay = 2 * time.Second
|
||||
|
||||
if err := container.Start(); err != nil {
|
||||
fmsg.PrintBaseError(err, "cannot start container:")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := container.Serve(); err != nil {
|
||||
fmsg.PrintBaseError(err, "cannot configure container:")
|
||||
}
|
||||
if err := container.Wait(); err != nil {
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
os.Exit(2)
|
||||
}
|
||||
internal.Exit(exitError.ExitCode())
|
||||
panic("unreachable")
|
||||
log.Printf("wait: %v", err)
|
||||
os.Exit(127)
|
||||
}
|
||||
os.Exit(exitError.ExitCode())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package shim
|
||||
|
||||
import (
|
||||
"git.gensokyo.uk/security/fortify/helper/bwrap"
|
||||
)
|
||||
|
||||
const Env = "FORTIFY_SHIM"
|
||||
|
||||
type Payload struct {
|
||||
// child full argv
|
||||
Argv []string
|
||||
// bwrap, target full exec path
|
||||
Exec [2]string
|
||||
// bwrap config
|
||||
Bwrap *bwrap.Config
|
||||
// path to outer home directory
|
||||
Home string
|
||||
// sync fd
|
||||
Sync *uintptr
|
||||
|
||||
// verbosity pass through
|
||||
Verbose bool
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.gensokyo.uk/security/fortify/helper/proc"
|
||||
"git.gensokyo.uk/security/fortify/internal"
|
||||
"git.gensokyo.uk/security/fortify/internal/fmsg"
|
||||
"git.gensokyo.uk/security/fortify/sandbox"
|
||||
@@ -25,10 +25,11 @@ type Shim struct {
|
||||
killFallback chan error
|
||||
// monitor to shim encoder
|
||||
encoder *gob.Encoder
|
||||
// bwrap --sync-fd value
|
||||
sync *uintptr
|
||||
}
|
||||
|
||||
func (s *Shim) Unwrap() *exec.Cmd { return s.cmd }
|
||||
func (s *Shim) Fallback() chan error { return s.killFallback }
|
||||
|
||||
func (s *Shim) String() string {
|
||||
if s.cmd == nil {
|
||||
return "(unused shim manager)"
|
||||
@@ -36,21 +37,9 @@ func (s *Shim) String() string {
|
||||
return s.cmd.String()
|
||||
}
|
||||
|
||||
func (s *Shim) Unwrap() *exec.Cmd {
|
||||
return s.cmd
|
||||
}
|
||||
|
||||
func (s *Shim) WaitFallback() chan error {
|
||||
return s.killFallback
|
||||
}
|
||||
|
||||
func (s *Shim) Start(
|
||||
// string representation of application id
|
||||
aid string,
|
||||
// string representation of supplementary group ids
|
||||
supp []string,
|
||||
// bwrap --sync-fd
|
||||
syncFd *os.File,
|
||||
) (*time.Time, error) {
|
||||
// prepare user switcher invocation
|
||||
fsuPath := internal.MustFsuPath()
|
||||
@@ -76,12 +65,6 @@ func (s *Shim) Start(
|
||||
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
s.cmd.Dir = "/"
|
||||
|
||||
// pass sync fd if set
|
||||
if syncFd != nil {
|
||||
fd := proc.ExtraFile(s.cmd, syncFd)
|
||||
s.sync = &fd
|
||||
}
|
||||
|
||||
fmsg.Verbose("starting shim via fsu:", s.cmd)
|
||||
// withhold messages to stderr
|
||||
fmsg.Suspend()
|
||||
@@ -90,10 +73,11 @@ func (s *Shim) Start(
|
||||
"cannot start fsu:")
|
||||
}
|
||||
startTime := time.Now().UTC()
|
||||
|
||||
return &startTime, nil
|
||||
}
|
||||
|
||||
func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
|
||||
func (s *Shim) Serve(ctx context.Context, params *Params) error {
|
||||
// kill shim if something goes wrong and an error is returned
|
||||
s.killFallback = make(chan error, 1)
|
||||
killShim := func() {
|
||||
@@ -103,9 +87,8 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
|
||||
}
|
||||
defer func() { killShim() }()
|
||||
|
||||
payload.Sync = s.sync
|
||||
encodeErr := make(chan error)
|
||||
go func() { encodeErr <- s.encoder.Encode(payload) }()
|
||||
go func() { encodeErr <- s.encoder.Encode(params) }()
|
||||
|
||||
select {
|
||||
// encode return indicates setup completion
|
||||
@@ -121,11 +104,11 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return fmsg.WrapError(errors.New("shim setup canceled"),
|
||||
return fmsg.WrapError(syscall.ECANCELED,
|
||||
"shim setup canceled")
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return fmsg.WrapError(errors.New("deadline exceeded waiting for shim"),
|
||||
return fmsg.WrapError(syscall.ETIMEDOUT,
|
||||
"deadline exceeded waiting for shim")
|
||||
}
|
||||
// unreachable
|
||||
@@ -96,7 +96,7 @@ func testStore(t *testing.T, s state.Store) {
|
||||
} else {
|
||||
slices.Sort(aids)
|
||||
want := []int{0, 1}
|
||||
if slices.Compare(aids, want) != 0 {
|
||||
if !slices.Equal(aids, want) {
|
||||
t.Fatalf("List() = %#v, want %#v", aids, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user