Compare commits
10 Commits
cfd05b10f1
...
f831948bca
Author | SHA1 | Date |
---|---|---|
Ophestra Umiker | f831948bca | |
Ophestra Umiker | 2e31b3d3a1 | |
Ophestra Umiker | 4d90e73366 | |
Ophestra Umiker | 3dfc1fcd56 | |
Ophestra Umiker | 89bafd0c22 | |
Ophestra Umiker | 861bb1274f | |
Ophestra Umiker | 714818c8aa | |
Ophestra Umiker | 69cc64ef56 | |
Ophestra Umiker | fc25ac2523 | |
Ophestra Umiker | d909b1190a |
97
README.md
97
README.md
|
@ -2,6 +2,7 @@ Fortify
|
||||||
=======
|
=======
|
||||||
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/git.ophivana.moe/security/fortify.svg)](https://pkg.go.dev/git.ophivana.moe/security/fortify)
|
[![Go Reference](https://pkg.go.dev/badge/git.ophivana.moe/security/fortify.svg)](https://pkg.go.dev/git.ophivana.moe/security/fortify)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/git.ophivana.moe/security/fortify)](https://goreportcard.com/report/git.ophivana.moe/security/fortify)
|
||||||
|
|
||||||
Lets you run graphical applications as another user in a confined environment with a nice NixOS
|
Lets you run graphical applications as another user in a confined environment with a nice NixOS
|
||||||
module to configure target users and provide launchers and desktop files for your privileged user.
|
module to configure target users and provide launchers and desktop files for your privileged user.
|
||||||
|
@ -77,40 +78,54 @@ This adds the `environment.fortify` option:
|
||||||
claws-mail.capability.pulse = false;
|
claws-mail.capability.pulse = false;
|
||||||
|
|
||||||
discord = {
|
discord = {
|
||||||
|
id = "dev.vencord.Vesktop";
|
||||||
command = "vesktop --ozone-platform-hint=wayland";
|
command = "vesktop --ozone-platform-hint=wayland";
|
||||||
|
userns = true;
|
||||||
|
useRealUid = true;
|
||||||
|
dbus = {
|
||||||
|
session =
|
||||||
|
f:
|
||||||
|
f {
|
||||||
|
talk = [ "org.kde.StatusNotifierWatcher" ];
|
||||||
|
own = [ ];
|
||||||
|
call = { };
|
||||||
|
broadcast = { };
|
||||||
|
};
|
||||||
|
system.filter = true;
|
||||||
|
};
|
||||||
share = pkgs.vesktop;
|
share = pkgs.vesktop;
|
||||||
};
|
};
|
||||||
|
|
||||||
chromium.dbus = {
|
chromium = {
|
||||||
configSystem = {
|
id = "org.chromium.Chromium";
|
||||||
filter = true;
|
userns = true;
|
||||||
talk = [
|
useRealUid = true;
|
||||||
"org.bluez"
|
dbus = {
|
||||||
"org.freedesktop.Avahi"
|
system = {
|
||||||
"org.freedesktop.UPower"
|
filter = true;
|
||||||
];
|
talk = [
|
||||||
};
|
"org.bluez"
|
||||||
config = {
|
"org.freedesktop.Avahi"
|
||||||
filter = true;
|
"org.freedesktop.UPower"
|
||||||
talk = [
|
];
|
||||||
"org.freedesktop.DBus"
|
|
||||||
"org.freedesktop.FileManager1"
|
|
||||||
"org.freedesktop.Notifications"
|
|
||||||
"org.freedesktop.ScreenSaver"
|
|
||||||
"org.freedesktop.secrets"
|
|
||||||
"org.kde.kwalletd5"
|
|
||||||
"org.kde.kwalletd6"
|
|
||||||
];
|
|
||||||
own = [
|
|
||||||
"org.chromium.Chromium.*"
|
|
||||||
"org.mpris.MediaPlayer2.org.chromium.Chromium.*"
|
|
||||||
"org.mpris.MediaPlayer2.chromium.*"
|
|
||||||
];
|
|
||||||
call = {
|
|
||||||
"org.freedesktop.portal.*" = "*";
|
|
||||||
};
|
};
|
||||||
broadcast = {
|
session = f: f {
|
||||||
"org.freedesktop.portal.*" = "@/org/freedesktop/portal/*";
|
talk = [
|
||||||
|
"org.freedesktop.DBus"
|
||||||
|
"org.freedesktop.FileManager1"
|
||||||
|
"org.freedesktop.Notifications"
|
||||||
|
"org.freedesktop.ScreenSaver"
|
||||||
|
"org.freedesktop.secrets"
|
||||||
|
"org.kde.kwalletd5"
|
||||||
|
"org.kde.kwalletd6"
|
||||||
|
];
|
||||||
|
own = [
|
||||||
|
"org.chromium.Chromium.*"
|
||||||
|
"org.mpris.MediaPlayer2.org.chromium.Chromium.*"
|
||||||
|
"org.mpris.MediaPlayer2.chromium.*"
|
||||||
|
];
|
||||||
|
call = { };
|
||||||
|
broadcast = { };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -156,15 +171,29 @@ This adds the `environment.fortify` option:
|
||||||
|
|
||||||
The available options are:
|
The available options are:
|
||||||
|
|
||||||
|
* `id`, the freedesktop application ID, primarily used by dbus, null to disable.
|
||||||
|
|
||||||
* `command`, the command to run as the target user. Defaults to launcher name.
|
* `command`, the command to run as the target user. Defaults to launcher name.
|
||||||
|
|
||||||
* `dbus.config`, D-Bus proxy custom configuration.
|
* `dbus.session`, D-Bus session proxy custom configuration.
|
||||||
|
|
||||||
* `dbus.configSystem`, D-Bus system bus custom configuration, null to disable.
|
* `dbus.configSystem`, D-Bus system proxy custom configuration, null to disable.
|
||||||
|
|
||||||
* `dbus.id`, D-Bus application id, has no effect if `dbus.config` is set.
|
* `env`, attrset of environment variables to set for the initial process in the sandbox.
|
||||||
|
|
||||||
* `dbus.mpris`, whether to enable MPRIS defaults, has no effect if `dbus.config` is set.
|
* `nix`, whether to allow nix daemon connections from within the sandbox.
|
||||||
|
|
||||||
|
* `userns`, whether to allow userns within the sandbox.
|
||||||
|
|
||||||
|
* `useRealUid`, whether to map to the real UID within the sandbox.
|
||||||
|
|
||||||
|
* `net`, whether to allow network access within the sandbox.
|
||||||
|
|
||||||
|
* `gpu`, target process GPU and driver access, null to follow Wayland or X capability.
|
||||||
|
|
||||||
|
* `dev`, whether to allow full device access within the sandbox.
|
||||||
|
|
||||||
|
* `extraPaths`, a list of extra paths to make available inside the sandbox.
|
||||||
|
|
||||||
* `capability.wayland`, whether to share the Wayland socket.
|
* `capability.wayland`, whether to share the Wayland socket.
|
||||||
|
|
||||||
|
@ -176,4 +205,4 @@ This adds the `environment.fortify` option:
|
||||||
|
|
||||||
* `share`, package containing desktop/icon files. Defaults to launcher name.
|
* `share`, package containing desktop/icon files. Defaults to launcher name.
|
||||||
|
|
||||||
* `method`, the launch method for the sandboxed program, can be `"fortify"`, `"fortify-sudo"`, `"sudo"`.
|
* `method`, the launch method for the sandboxed program, can be `"sudo"`, `"systemd"`, `"simple"`.
|
||||||
|
|
135
config.go
135
config.go
|
@ -1,135 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/dbus"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/app"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/system"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
printTemplate bool
|
|
||||||
|
|
||||||
confPath string
|
|
||||||
|
|
||||||
dbusConfigSession string
|
|
||||||
dbusConfigSystem string
|
|
||||||
dbusID string
|
|
||||||
mpris bool
|
|
||||||
dbusVerbose bool
|
|
||||||
|
|
||||||
userName string
|
|
||||||
enablements [system.ELen]bool
|
|
||||||
|
|
||||||
launchMethodText string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printTemplate, "template", false, "Print a full config template and exit")
|
|
||||||
|
|
||||||
// config file, disables every other flag here
|
|
||||||
flag.StringVar(&confPath, "c", "nil", "Path to full app configuration, or \"nil\" to configure from flags")
|
|
||||||
|
|
||||||
flag.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults")
|
|
||||||
flag.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable")
|
|
||||||
flag.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available")
|
|
||||||
flag.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available")
|
|
||||||
flag.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy")
|
|
||||||
|
|
||||||
flag.StringVar(&userName, "u", "chronos", "Passwd name of user to run as")
|
|
||||||
flag.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket")
|
|
||||||
flag.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection")
|
|
||||||
flag.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection")
|
|
||||||
flag.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodHelpString := "Method of launching the child process, can be one of \"sudo\""
|
|
||||||
if os.SdBooted() {
|
|
||||||
methodHelpString += ", \"systemd\""
|
|
||||||
}
|
|
||||||
|
|
||||||
flag.StringVar(&launchMethodText, "method", "sudo", methodHelpString)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryTemplate() {
|
|
||||||
if printTemplate {
|
|
||||||
if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil {
|
|
||||||
fmsg.Fatalf("cannot generate template: %v", err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else {
|
|
||||||
fmt.Println(string(s))
|
|
||||||
}
|
|
||||||
fmsg.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfig() *app.Config {
|
|
||||||
if confPath == "nil" {
|
|
||||||
// config from flags
|
|
||||||
return configFromFlags()
|
|
||||||
} else {
|
|
||||||
// config from file
|
|
||||||
c := new(app.Config)
|
|
||||||
if f, err := os.Open(confPath); err != nil {
|
|
||||||
fmsg.Fatalf("cannot access config file %q: %s", confPath, err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else if err = json.NewDecoder(f).Decode(&c); err != nil {
|
|
||||||
fmsg.Fatalf("cannot parse config file %q: %s", confPath, err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configFromFlags() (config *app.Config) {
|
|
||||||
// initialise config from flags
|
|
||||||
config = &app.Config{
|
|
||||||
ID: dbusID,
|
|
||||||
User: userName,
|
|
||||||
Command: flag.Args(),
|
|
||||||
Method: launchMethodText,
|
|
||||||
}
|
|
||||||
|
|
||||||
// enablements from flags
|
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
|
||||||
if enablements[i] {
|
|
||||||
config.Confinement.Enablements.Set(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse D-Bus config file from flags if applicable
|
|
||||||
if enablements[system.EDBus] {
|
|
||||||
if dbusConfigSession == "builtin" {
|
|
||||||
config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris)
|
|
||||||
} else {
|
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
|
||||||
fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
|
||||||
} else {
|
|
||||||
config.Confinement.SessionBus = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// system bus proxy is optional
|
|
||||||
if dbusConfigSystem != "nil" {
|
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
|
||||||
fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
|
||||||
} else {
|
|
||||||
config.Confinement.SystemBus = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override log from configuration
|
|
||||||
if dbusVerbose {
|
|
||||||
config.Confinement.SessionBus.Log = true
|
|
||||||
config.Confinement.SystemBus.Log = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package app_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -54,11 +55,11 @@ var testCasesNixos = []sealTestCase{
|
||||||
DieWithParent: true,
|
DieWithParent: true,
|
||||||
AsInit: true,
|
AsInit: true,
|
||||||
}).SetUID(65534).SetGID(65534).
|
}).SetUID(65534).SetGID(65534).
|
||||||
Procfs("/proc").DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
Procfs("/proc").
|
||||||
Tmpfs("/dev/fortify", 4096).
|
Tmpfs("/fortify", 4096).
|
||||||
|
DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
||||||
Bind("/bin", "/bin", false, true).
|
Bind("/bin", "/bin", false, true).
|
||||||
Bind("/boot", "/boot", false, true).
|
Bind("/boot", "/boot", false, true).
|
||||||
Bind("/etc", "/dev/fortify/etc").
|
|
||||||
Bind("/home", "/home", false, true).
|
Bind("/home", "/home", false, true).
|
||||||
Bind("/lib", "/lib", false, true).
|
Bind("/lib", "/lib", false, true).
|
||||||
Bind("/lib64", "/lib64", false, true).
|
Bind("/lib64", "/lib64", false, true).
|
||||||
|
@ -102,84 +103,85 @@ var testCasesNixos = []sealTestCase{
|
||||||
Bind("/run/wrappers", "/run/wrappers", false, true).
|
Bind("/run/wrappers", "/run/wrappers", false, true).
|
||||||
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
||||||
Bind("/run/zed.state", "/run/zed.state", false, true).
|
Bind("/run/zed.state", "/run/zed.state", false, true).
|
||||||
Symlink("/dev/fortify/etc/alsa", "/etc/alsa").
|
Bind("/etc", "/fortify/etc").
|
||||||
Symlink("/dev/fortify/etc/bashrc", "/etc/bashrc").
|
Symlink("/fortify/etc/alsa", "/etc/alsa").
|
||||||
Symlink("/dev/fortify/etc/binfmt.d", "/etc/binfmt.d").
|
Symlink("/fortify/etc/bashrc", "/etc/bashrc").
|
||||||
Symlink("/dev/fortify/etc/dbus-1", "/etc/dbus-1").
|
Symlink("/fortify/etc/binfmt.d", "/etc/binfmt.d").
|
||||||
Symlink("/dev/fortify/etc/default", "/etc/default").
|
Symlink("/fortify/etc/dbus-1", "/etc/dbus-1").
|
||||||
Symlink("/dev/fortify/etc/ethertypes", "/etc/ethertypes").
|
Symlink("/fortify/etc/default", "/etc/default").
|
||||||
Symlink("/dev/fortify/etc/fonts", "/etc/fonts").
|
Symlink("/fortify/etc/ethertypes", "/etc/ethertypes").
|
||||||
Symlink("/dev/fortify/etc/fstab", "/etc/fstab").
|
Symlink("/fortify/etc/fonts", "/etc/fonts").
|
||||||
Symlink("/dev/fortify/etc/fuse.conf", "/etc/fuse.conf").
|
Symlink("/fortify/etc/fstab", "/etc/fstab").
|
||||||
Symlink("/dev/fortify/etc/host.conf", "/etc/host.conf").
|
Symlink("/fortify/etc/fuse.conf", "/etc/fuse.conf").
|
||||||
Symlink("/dev/fortify/etc/hostid", "/etc/hostid").
|
Symlink("/fortify/etc/host.conf", "/etc/host.conf").
|
||||||
Symlink("/dev/fortify/etc/hostname", "/etc/hostname").
|
Symlink("/fortify/etc/hostid", "/etc/hostid").
|
||||||
Symlink("/dev/fortify/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
Symlink("/fortify/etc/hostname", "/etc/hostname").
|
||||||
Symlink("/dev/fortify/etc/hosts", "/etc/hosts").
|
Symlink("/fortify/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||||
Symlink("/dev/fortify/etc/inputrc", "/etc/inputrc").
|
Symlink("/fortify/etc/hosts", "/etc/hosts").
|
||||||
Symlink("/dev/fortify/etc/ipsec.d", "/etc/ipsec.d").
|
Symlink("/fortify/etc/inputrc", "/etc/inputrc").
|
||||||
Symlink("/dev/fortify/etc/issue", "/etc/issue").
|
Symlink("/fortify/etc/ipsec.d", "/etc/ipsec.d").
|
||||||
Symlink("/dev/fortify/etc/kbd", "/etc/kbd").
|
Symlink("/fortify/etc/issue", "/etc/issue").
|
||||||
Symlink("/dev/fortify/etc/libblockdev", "/etc/libblockdev").
|
Symlink("/fortify/etc/kbd", "/etc/kbd").
|
||||||
Symlink("/dev/fortify/etc/locale.conf", "/etc/locale.conf").
|
Symlink("/fortify/etc/libblockdev", "/etc/libblockdev").
|
||||||
Symlink("/dev/fortify/etc/localtime", "/etc/localtime").
|
Symlink("/fortify/etc/locale.conf", "/etc/locale.conf").
|
||||||
Symlink("/dev/fortify/etc/login.defs", "/etc/login.defs").
|
Symlink("/fortify/etc/localtime", "/etc/localtime").
|
||||||
Symlink("/dev/fortify/etc/lsb-release", "/etc/lsb-release").
|
Symlink("/fortify/etc/login.defs", "/etc/login.defs").
|
||||||
Symlink("/dev/fortify/etc/lvm", "/etc/lvm").
|
Symlink("/fortify/etc/lsb-release", "/etc/lsb-release").
|
||||||
Symlink("/dev/fortify/etc/machine-id", "/etc/machine-id").
|
Symlink("/fortify/etc/lvm", "/etc/lvm").
|
||||||
Symlink("/dev/fortify/etc/man_db.conf", "/etc/man_db.conf").
|
Symlink("/fortify/etc/machine-id", "/etc/machine-id").
|
||||||
Symlink("/dev/fortify/etc/modprobe.d", "/etc/modprobe.d").
|
Symlink("/fortify/etc/man_db.conf", "/etc/man_db.conf").
|
||||||
Symlink("/dev/fortify/etc/modules-load.d", "/etc/modules-load.d").
|
Symlink("/fortify/etc/modprobe.d", "/etc/modprobe.d").
|
||||||
|
Symlink("/fortify/etc/modules-load.d", "/etc/modules-load.d").
|
||||||
Symlink("/proc/mounts", "/etc/mtab").
|
Symlink("/proc/mounts", "/etc/mtab").
|
||||||
Symlink("/dev/fortify/etc/nanorc", "/etc/nanorc").
|
Symlink("/fortify/etc/nanorc", "/etc/nanorc").
|
||||||
Symlink("/dev/fortify/etc/netgroup", "/etc/netgroup").
|
Symlink("/fortify/etc/netgroup", "/etc/netgroup").
|
||||||
Symlink("/dev/fortify/etc/NetworkManager", "/etc/NetworkManager").
|
Symlink("/fortify/etc/NetworkManager", "/etc/NetworkManager").
|
||||||
Symlink("/dev/fortify/etc/nix", "/etc/nix").
|
Symlink("/fortify/etc/nix", "/etc/nix").
|
||||||
Symlink("/dev/fortify/etc/nixos", "/etc/nixos").
|
Symlink("/fortify/etc/nixos", "/etc/nixos").
|
||||||
Symlink("/dev/fortify/etc/NIXOS", "/etc/NIXOS").
|
Symlink("/fortify/etc/NIXOS", "/etc/NIXOS").
|
||||||
Symlink("/dev/fortify/etc/nscd.conf", "/etc/nscd.conf").
|
Symlink("/fortify/etc/nscd.conf", "/etc/nscd.conf").
|
||||||
Symlink("/dev/fortify/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
Symlink("/fortify/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||||
Symlink("/dev/fortify/etc/opensnitchd", "/etc/opensnitchd").
|
Symlink("/fortify/etc/opensnitchd", "/etc/opensnitchd").
|
||||||
Symlink("/dev/fortify/etc/os-release", "/etc/os-release").
|
Symlink("/fortify/etc/os-release", "/etc/os-release").
|
||||||
Symlink("/dev/fortify/etc/pam", "/etc/pam").
|
Symlink("/fortify/etc/pam", "/etc/pam").
|
||||||
Symlink("/dev/fortify/etc/pam.d", "/etc/pam.d").
|
Symlink("/fortify/etc/pam.d", "/etc/pam.d").
|
||||||
Symlink("/dev/fortify/etc/pipewire", "/etc/pipewire").
|
Symlink("/fortify/etc/pipewire", "/etc/pipewire").
|
||||||
Symlink("/dev/fortify/etc/pki", "/etc/pki").
|
Symlink("/fortify/etc/pki", "/etc/pki").
|
||||||
Symlink("/dev/fortify/etc/polkit-1", "/etc/polkit-1").
|
Symlink("/fortify/etc/polkit-1", "/etc/polkit-1").
|
||||||
Symlink("/dev/fortify/etc/profile", "/etc/profile").
|
Symlink("/fortify/etc/profile", "/etc/profile").
|
||||||
Symlink("/dev/fortify/etc/protocols", "/etc/protocols").
|
Symlink("/fortify/etc/protocols", "/etc/protocols").
|
||||||
Symlink("/dev/fortify/etc/qemu", "/etc/qemu").
|
Symlink("/fortify/etc/qemu", "/etc/qemu").
|
||||||
Symlink("/dev/fortify/etc/resolv.conf", "/etc/resolv.conf").
|
Symlink("/fortify/etc/resolv.conf", "/etc/resolv.conf").
|
||||||
Symlink("/dev/fortify/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
Symlink("/fortify/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||||
Symlink("/dev/fortify/etc/rpc", "/etc/rpc").
|
Symlink("/fortify/etc/rpc", "/etc/rpc").
|
||||||
Symlink("/dev/fortify/etc/samba", "/etc/samba").
|
Symlink("/fortify/etc/samba", "/etc/samba").
|
||||||
Symlink("/dev/fortify/etc/sddm.conf", "/etc/sddm.conf").
|
Symlink("/fortify/etc/sddm.conf", "/etc/sddm.conf").
|
||||||
Symlink("/dev/fortify/etc/secureboot", "/etc/secureboot").
|
Symlink("/fortify/etc/secureboot", "/etc/secureboot").
|
||||||
Symlink("/dev/fortify/etc/services", "/etc/services").
|
Symlink("/fortify/etc/services", "/etc/services").
|
||||||
Symlink("/dev/fortify/etc/set-environment", "/etc/set-environment").
|
Symlink("/fortify/etc/set-environment", "/etc/set-environment").
|
||||||
Symlink("/dev/fortify/etc/shadow", "/etc/shadow").
|
Symlink("/fortify/etc/shadow", "/etc/shadow").
|
||||||
Symlink("/dev/fortify/etc/shells", "/etc/shells").
|
Symlink("/fortify/etc/shells", "/etc/shells").
|
||||||
Symlink("/dev/fortify/etc/ssh", "/etc/ssh").
|
Symlink("/fortify/etc/ssh", "/etc/ssh").
|
||||||
Symlink("/dev/fortify/etc/ssl", "/etc/ssl").
|
Symlink("/fortify/etc/ssl", "/etc/ssl").
|
||||||
Symlink("/dev/fortify/etc/static", "/etc/static").
|
Symlink("/fortify/etc/static", "/etc/static").
|
||||||
Symlink("/dev/fortify/etc/subgid", "/etc/subgid").
|
Symlink("/fortify/etc/subgid", "/etc/subgid").
|
||||||
Symlink("/dev/fortify/etc/subuid", "/etc/subuid").
|
Symlink("/fortify/etc/subuid", "/etc/subuid").
|
||||||
Symlink("/dev/fortify/etc/sudoers", "/etc/sudoers").
|
Symlink("/fortify/etc/sudoers", "/etc/sudoers").
|
||||||
Symlink("/dev/fortify/etc/sysctl.d", "/etc/sysctl.d").
|
Symlink("/fortify/etc/sysctl.d", "/etc/sysctl.d").
|
||||||
Symlink("/dev/fortify/etc/systemd", "/etc/systemd").
|
Symlink("/fortify/etc/systemd", "/etc/systemd").
|
||||||
Symlink("/dev/fortify/etc/terminfo", "/etc/terminfo").
|
Symlink("/fortify/etc/terminfo", "/etc/terminfo").
|
||||||
Symlink("/dev/fortify/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
Symlink("/fortify/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||||
Symlink("/dev/fortify/etc/udev", "/etc/udev").
|
Symlink("/fortify/etc/udev", "/etc/udev").
|
||||||
Symlink("/dev/fortify/etc/udisks2", "/etc/udisks2").
|
Symlink("/fortify/etc/udisks2", "/etc/udisks2").
|
||||||
Symlink("/dev/fortify/etc/UPower", "/etc/UPower").
|
Symlink("/fortify/etc/UPower", "/etc/UPower").
|
||||||
Symlink("/dev/fortify/etc/vconsole.conf", "/etc/vconsole.conf").
|
Symlink("/fortify/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||||
Symlink("/dev/fortify/etc/X11", "/etc/X11").
|
Symlink("/fortify/etc/X11", "/etc/X11").
|
||||||
Symlink("/dev/fortify/etc/zfs", "/etc/zfs").
|
Symlink("/fortify/etc/zfs", "/etc/zfs").
|
||||||
Symlink("/dev/fortify/etc/zinputrc", "/etc/zinputrc").
|
Symlink("/fortify/etc/zinputrc", "/etc/zinputrc").
|
||||||
Symlink("/dev/fortify/etc/zoneinfo", "/etc/zoneinfo").
|
Symlink("/fortify/etc/zoneinfo", "/etc/zoneinfo").
|
||||||
Symlink("/dev/fortify/etc/zprofile", "/etc/zprofile").
|
Symlink("/fortify/etc/zprofile", "/etc/zprofile").
|
||||||
Symlink("/dev/fortify/etc/zshenv", "/etc/zshenv").
|
Symlink("/fortify/etc/zshenv", "/etc/zshenv").
|
||||||
Symlink("/dev/fortify/etc/zshrc", "/etc/zshrc").
|
Symlink("/fortify/etc/zshrc", "/etc/zshrc").
|
||||||
Bind("/tmp/fortify.1971/tmpdir/150", "/tmp", false, true).
|
Bind("/tmp/fortify.1971/tmpdir/150", "/tmp", false, true).
|
||||||
Tmpfs("/tmp/fortify.1971", 1048576).
|
Tmpfs("/tmp/fortify.1971", 1048576).
|
||||||
Tmpfs("/run/user", 1048576).
|
Tmpfs("/run/user", 1048576).
|
||||||
|
@ -304,11 +306,11 @@ var testCasesNixos = []sealTestCase{
|
||||||
DieWithParent: true,
|
DieWithParent: true,
|
||||||
AsInit: true,
|
AsInit: true,
|
||||||
}).SetUID(65534).SetGID(65534).
|
}).SetUID(65534).SetGID(65534).
|
||||||
Procfs("/proc").DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
Procfs("/proc").
|
||||||
Tmpfs("/dev/fortify", 4096).
|
Tmpfs("/fortify", 4096).
|
||||||
|
DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
||||||
Bind("/bin", "/bin", false, true).
|
Bind("/bin", "/bin", false, true).
|
||||||
Bind("/boot", "/boot", false, true).
|
Bind("/boot", "/boot", false, true).
|
||||||
Bind("/etc", "/dev/fortify/etc").
|
|
||||||
Bind("/home", "/home", false, true).
|
Bind("/home", "/home", false, true).
|
||||||
Bind("/lib", "/lib", false, true).
|
Bind("/lib", "/lib", false, true).
|
||||||
Bind("/lib64", "/lib64", false, true).
|
Bind("/lib64", "/lib64", false, true).
|
||||||
|
@ -353,84 +355,85 @@ var testCasesNixos = []sealTestCase{
|
||||||
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
Bind("/run/zed.pid", "/run/zed.pid", false, true).
|
||||||
Bind("/run/zed.state", "/run/zed.state", false, true).
|
Bind("/run/zed.state", "/run/zed.state", false, true).
|
||||||
Bind("/dev/dri", "/dev/dri", true, true, true).
|
Bind("/dev/dri", "/dev/dri", true, true, true).
|
||||||
Symlink("/dev/fortify/etc/alsa", "/etc/alsa").
|
Bind("/etc", "/fortify/etc").
|
||||||
Symlink("/dev/fortify/etc/bashrc", "/etc/bashrc").
|
Symlink("/fortify/etc/alsa", "/etc/alsa").
|
||||||
Symlink("/dev/fortify/etc/binfmt.d", "/etc/binfmt.d").
|
Symlink("/fortify/etc/bashrc", "/etc/bashrc").
|
||||||
Symlink("/dev/fortify/etc/dbus-1", "/etc/dbus-1").
|
Symlink("/fortify/etc/binfmt.d", "/etc/binfmt.d").
|
||||||
Symlink("/dev/fortify/etc/default", "/etc/default").
|
Symlink("/fortify/etc/dbus-1", "/etc/dbus-1").
|
||||||
Symlink("/dev/fortify/etc/ethertypes", "/etc/ethertypes").
|
Symlink("/fortify/etc/default", "/etc/default").
|
||||||
Symlink("/dev/fortify/etc/fonts", "/etc/fonts").
|
Symlink("/fortify/etc/ethertypes", "/etc/ethertypes").
|
||||||
Symlink("/dev/fortify/etc/fstab", "/etc/fstab").
|
Symlink("/fortify/etc/fonts", "/etc/fonts").
|
||||||
Symlink("/dev/fortify/etc/fuse.conf", "/etc/fuse.conf").
|
Symlink("/fortify/etc/fstab", "/etc/fstab").
|
||||||
Symlink("/dev/fortify/etc/host.conf", "/etc/host.conf").
|
Symlink("/fortify/etc/fuse.conf", "/etc/fuse.conf").
|
||||||
Symlink("/dev/fortify/etc/hostid", "/etc/hostid").
|
Symlink("/fortify/etc/host.conf", "/etc/host.conf").
|
||||||
Symlink("/dev/fortify/etc/hostname", "/etc/hostname").
|
Symlink("/fortify/etc/hostid", "/etc/hostid").
|
||||||
Symlink("/dev/fortify/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
Symlink("/fortify/etc/hostname", "/etc/hostname").
|
||||||
Symlink("/dev/fortify/etc/hosts", "/etc/hosts").
|
Symlink("/fortify/etc/hostname.CHECKSUM", "/etc/hostname.CHECKSUM").
|
||||||
Symlink("/dev/fortify/etc/inputrc", "/etc/inputrc").
|
Symlink("/fortify/etc/hosts", "/etc/hosts").
|
||||||
Symlink("/dev/fortify/etc/ipsec.d", "/etc/ipsec.d").
|
Symlink("/fortify/etc/inputrc", "/etc/inputrc").
|
||||||
Symlink("/dev/fortify/etc/issue", "/etc/issue").
|
Symlink("/fortify/etc/ipsec.d", "/etc/ipsec.d").
|
||||||
Symlink("/dev/fortify/etc/kbd", "/etc/kbd").
|
Symlink("/fortify/etc/issue", "/etc/issue").
|
||||||
Symlink("/dev/fortify/etc/libblockdev", "/etc/libblockdev").
|
Symlink("/fortify/etc/kbd", "/etc/kbd").
|
||||||
Symlink("/dev/fortify/etc/locale.conf", "/etc/locale.conf").
|
Symlink("/fortify/etc/libblockdev", "/etc/libblockdev").
|
||||||
Symlink("/dev/fortify/etc/localtime", "/etc/localtime").
|
Symlink("/fortify/etc/locale.conf", "/etc/locale.conf").
|
||||||
Symlink("/dev/fortify/etc/login.defs", "/etc/login.defs").
|
Symlink("/fortify/etc/localtime", "/etc/localtime").
|
||||||
Symlink("/dev/fortify/etc/lsb-release", "/etc/lsb-release").
|
Symlink("/fortify/etc/login.defs", "/etc/login.defs").
|
||||||
Symlink("/dev/fortify/etc/lvm", "/etc/lvm").
|
Symlink("/fortify/etc/lsb-release", "/etc/lsb-release").
|
||||||
Symlink("/dev/fortify/etc/machine-id", "/etc/machine-id").
|
Symlink("/fortify/etc/lvm", "/etc/lvm").
|
||||||
Symlink("/dev/fortify/etc/man_db.conf", "/etc/man_db.conf").
|
Symlink("/fortify/etc/machine-id", "/etc/machine-id").
|
||||||
Symlink("/dev/fortify/etc/modprobe.d", "/etc/modprobe.d").
|
Symlink("/fortify/etc/man_db.conf", "/etc/man_db.conf").
|
||||||
Symlink("/dev/fortify/etc/modules-load.d", "/etc/modules-load.d").
|
Symlink("/fortify/etc/modprobe.d", "/etc/modprobe.d").
|
||||||
|
Symlink("/fortify/etc/modules-load.d", "/etc/modules-load.d").
|
||||||
Symlink("/proc/mounts", "/etc/mtab").
|
Symlink("/proc/mounts", "/etc/mtab").
|
||||||
Symlink("/dev/fortify/etc/nanorc", "/etc/nanorc").
|
Symlink("/fortify/etc/nanorc", "/etc/nanorc").
|
||||||
Symlink("/dev/fortify/etc/netgroup", "/etc/netgroup").
|
Symlink("/fortify/etc/netgroup", "/etc/netgroup").
|
||||||
Symlink("/dev/fortify/etc/NetworkManager", "/etc/NetworkManager").
|
Symlink("/fortify/etc/NetworkManager", "/etc/NetworkManager").
|
||||||
Symlink("/dev/fortify/etc/nix", "/etc/nix").
|
Symlink("/fortify/etc/nix", "/etc/nix").
|
||||||
Symlink("/dev/fortify/etc/nixos", "/etc/nixos").
|
Symlink("/fortify/etc/nixos", "/etc/nixos").
|
||||||
Symlink("/dev/fortify/etc/NIXOS", "/etc/NIXOS").
|
Symlink("/fortify/etc/NIXOS", "/etc/NIXOS").
|
||||||
Symlink("/dev/fortify/etc/nscd.conf", "/etc/nscd.conf").
|
Symlink("/fortify/etc/nscd.conf", "/etc/nscd.conf").
|
||||||
Symlink("/dev/fortify/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
Symlink("/fortify/etc/nsswitch.conf", "/etc/nsswitch.conf").
|
||||||
Symlink("/dev/fortify/etc/opensnitchd", "/etc/opensnitchd").
|
Symlink("/fortify/etc/opensnitchd", "/etc/opensnitchd").
|
||||||
Symlink("/dev/fortify/etc/os-release", "/etc/os-release").
|
Symlink("/fortify/etc/os-release", "/etc/os-release").
|
||||||
Symlink("/dev/fortify/etc/pam", "/etc/pam").
|
Symlink("/fortify/etc/pam", "/etc/pam").
|
||||||
Symlink("/dev/fortify/etc/pam.d", "/etc/pam.d").
|
Symlink("/fortify/etc/pam.d", "/etc/pam.d").
|
||||||
Symlink("/dev/fortify/etc/pipewire", "/etc/pipewire").
|
Symlink("/fortify/etc/pipewire", "/etc/pipewire").
|
||||||
Symlink("/dev/fortify/etc/pki", "/etc/pki").
|
Symlink("/fortify/etc/pki", "/etc/pki").
|
||||||
Symlink("/dev/fortify/etc/polkit-1", "/etc/polkit-1").
|
Symlink("/fortify/etc/polkit-1", "/etc/polkit-1").
|
||||||
Symlink("/dev/fortify/etc/profile", "/etc/profile").
|
Symlink("/fortify/etc/profile", "/etc/profile").
|
||||||
Symlink("/dev/fortify/etc/protocols", "/etc/protocols").
|
Symlink("/fortify/etc/protocols", "/etc/protocols").
|
||||||
Symlink("/dev/fortify/etc/qemu", "/etc/qemu").
|
Symlink("/fortify/etc/qemu", "/etc/qemu").
|
||||||
Symlink("/dev/fortify/etc/resolv.conf", "/etc/resolv.conf").
|
Symlink("/fortify/etc/resolv.conf", "/etc/resolv.conf").
|
||||||
Symlink("/dev/fortify/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
Symlink("/fortify/etc/resolvconf.conf", "/etc/resolvconf.conf").
|
||||||
Symlink("/dev/fortify/etc/rpc", "/etc/rpc").
|
Symlink("/fortify/etc/rpc", "/etc/rpc").
|
||||||
Symlink("/dev/fortify/etc/samba", "/etc/samba").
|
Symlink("/fortify/etc/samba", "/etc/samba").
|
||||||
Symlink("/dev/fortify/etc/sddm.conf", "/etc/sddm.conf").
|
Symlink("/fortify/etc/sddm.conf", "/etc/sddm.conf").
|
||||||
Symlink("/dev/fortify/etc/secureboot", "/etc/secureboot").
|
Symlink("/fortify/etc/secureboot", "/etc/secureboot").
|
||||||
Symlink("/dev/fortify/etc/services", "/etc/services").
|
Symlink("/fortify/etc/services", "/etc/services").
|
||||||
Symlink("/dev/fortify/etc/set-environment", "/etc/set-environment").
|
Symlink("/fortify/etc/set-environment", "/etc/set-environment").
|
||||||
Symlink("/dev/fortify/etc/shadow", "/etc/shadow").
|
Symlink("/fortify/etc/shadow", "/etc/shadow").
|
||||||
Symlink("/dev/fortify/etc/shells", "/etc/shells").
|
Symlink("/fortify/etc/shells", "/etc/shells").
|
||||||
Symlink("/dev/fortify/etc/ssh", "/etc/ssh").
|
Symlink("/fortify/etc/ssh", "/etc/ssh").
|
||||||
Symlink("/dev/fortify/etc/ssl", "/etc/ssl").
|
Symlink("/fortify/etc/ssl", "/etc/ssl").
|
||||||
Symlink("/dev/fortify/etc/static", "/etc/static").
|
Symlink("/fortify/etc/static", "/etc/static").
|
||||||
Symlink("/dev/fortify/etc/subgid", "/etc/subgid").
|
Symlink("/fortify/etc/subgid", "/etc/subgid").
|
||||||
Symlink("/dev/fortify/etc/subuid", "/etc/subuid").
|
Symlink("/fortify/etc/subuid", "/etc/subuid").
|
||||||
Symlink("/dev/fortify/etc/sudoers", "/etc/sudoers").
|
Symlink("/fortify/etc/sudoers", "/etc/sudoers").
|
||||||
Symlink("/dev/fortify/etc/sysctl.d", "/etc/sysctl.d").
|
Symlink("/fortify/etc/sysctl.d", "/etc/sysctl.d").
|
||||||
Symlink("/dev/fortify/etc/systemd", "/etc/systemd").
|
Symlink("/fortify/etc/systemd", "/etc/systemd").
|
||||||
Symlink("/dev/fortify/etc/terminfo", "/etc/terminfo").
|
Symlink("/fortify/etc/terminfo", "/etc/terminfo").
|
||||||
Symlink("/dev/fortify/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
Symlink("/fortify/etc/tmpfiles.d", "/etc/tmpfiles.d").
|
||||||
Symlink("/dev/fortify/etc/udev", "/etc/udev").
|
Symlink("/fortify/etc/udev", "/etc/udev").
|
||||||
Symlink("/dev/fortify/etc/udisks2", "/etc/udisks2").
|
Symlink("/fortify/etc/udisks2", "/etc/udisks2").
|
||||||
Symlink("/dev/fortify/etc/UPower", "/etc/UPower").
|
Symlink("/fortify/etc/UPower", "/etc/UPower").
|
||||||
Symlink("/dev/fortify/etc/vconsole.conf", "/etc/vconsole.conf").
|
Symlink("/fortify/etc/vconsole.conf", "/etc/vconsole.conf").
|
||||||
Symlink("/dev/fortify/etc/X11", "/etc/X11").
|
Symlink("/fortify/etc/X11", "/etc/X11").
|
||||||
Symlink("/dev/fortify/etc/zfs", "/etc/zfs").
|
Symlink("/fortify/etc/zfs", "/etc/zfs").
|
||||||
Symlink("/dev/fortify/etc/zinputrc", "/etc/zinputrc").
|
Symlink("/fortify/etc/zinputrc", "/etc/zinputrc").
|
||||||
Symlink("/dev/fortify/etc/zoneinfo", "/etc/zoneinfo").
|
Symlink("/fortify/etc/zoneinfo", "/etc/zoneinfo").
|
||||||
Symlink("/dev/fortify/etc/zprofile", "/etc/zprofile").
|
Symlink("/fortify/etc/zprofile", "/etc/zprofile").
|
||||||
Symlink("/dev/fortify/etc/zshenv", "/etc/zshenv").
|
Symlink("/fortify/etc/zshenv", "/etc/zshenv").
|
||||||
Symlink("/dev/fortify/etc/zshrc", "/etc/zshrc").
|
Symlink("/fortify/etc/zshrc", "/etc/zshrc").
|
||||||
Bind("/tmp/fortify.1971/tmpdir/150", "/tmp", false, true).
|
Bind("/tmp/fortify.1971/tmpdir/150", "/tmp", false, true).
|
||||||
Tmpfs("/tmp/fortify.1971", 1048576).
|
Tmpfs("/tmp/fortify.1971", 1048576).
|
||||||
Tmpfs("/run/user", 1048576).
|
Tmpfs("/run/user", 1048576).
|
||||||
|
@ -579,6 +582,10 @@ func (s *stubNixOS) Exit(code int) {
|
||||||
panic("called exit on stub with code " + strconv.Itoa(code))
|
panic("called exit on stub with code " + strconv.Itoa(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubNixOS) Stdout() io.Writer {
|
||||||
|
panic("requested stdout")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stubNixOS) FshimPath() string {
|
func (s *stubNixOS) FshimPath() string {
|
||||||
return "/nix/store/00000000000000000000000000000000-fortify-0.0.10/bin/.fshim"
|
return "/nix/store/00000000000000000000000000000000-fortify-0.0.10/bin/.fshim"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"errors"
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/dbus"
|
"git.ophivana.moe/security/fortify/dbus"
|
||||||
"git.ophivana.moe/security/fortify/helper/bwrap"
|
"git.ophivana.moe/security/fortify/helper/bwrap"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/linux"
|
||||||
"git.ophivana.moe/security/fortify/internal/system"
|
"git.ophivana.moe/security/fortify/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fTmp = "/fortify"
|
||||||
|
|
||||||
// Config is used to seal an *App
|
// Config is used to seal an *App
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// D-Bus application ID
|
// D-Bus application ID
|
||||||
|
@ -47,6 +50,8 @@ type SandboxConfig struct {
|
||||||
UserNS bool `json:"userns,omitempty"`
|
UserNS bool `json:"userns,omitempty"`
|
||||||
// share net namespace
|
// share net namespace
|
||||||
Net bool `json:"net,omitempty"`
|
Net bool `json:"net,omitempty"`
|
||||||
|
// share all devices
|
||||||
|
Dev bool `json:"dev,omitempty"`
|
||||||
// do not run in new session
|
// do not run in new session
|
||||||
NoNewSession bool `json:"no_new_session,omitempty"`
|
NoNewSession bool `json:"no_new_session,omitempty"`
|
||||||
// map target user uid to privileged user uid in the user namespace
|
// map target user uid to privileged user uid in the user namespace
|
||||||
|
@ -60,6 +65,8 @@ type SandboxConfig struct {
|
||||||
Filesystem []*FilesystemConfig `json:"filesystem"`
|
Filesystem []*FilesystemConfig `json:"filesystem"`
|
||||||
// symlinks created inside the sandbox
|
// symlinks created inside the sandbox
|
||||||
Link [][2]string `json:"symlink"`
|
Link [][2]string `json:"symlink"`
|
||||||
|
// automatically set up /etc symlinks
|
||||||
|
AutoEtc bool `json:"auto_etc"`
|
||||||
// paths to override by mounting tmpfs over them
|
// paths to override by mounting tmpfs over them
|
||||||
Override []string `json:"override"`
|
Override []string `json:"override"`
|
||||||
}
|
}
|
||||||
|
@ -79,13 +86,16 @@ type FilesystemConfig struct {
|
||||||
|
|
||||||
// Bwrap returns the address of the corresponding bwrap.Config to s.
|
// Bwrap returns the address of the corresponding bwrap.Config to s.
|
||||||
// Note that remaining tmpfs entries must be queued by the caller prior to launch.
|
// Note that remaining tmpfs entries must be queued by the caller prior to launch.
|
||||||
func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config {
|
func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return nil
|
return nil, errors.New("nil sandbox config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var uid int
|
||||||
if !s.UseRealUID {
|
if !s.UseRealUID {
|
||||||
uid = 65534
|
uid = 65534
|
||||||
|
} else {
|
||||||
|
uid = os.Geteuid()
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := (&bwrap.Config{
|
conf := (&bwrap.Config{
|
||||||
|
@ -99,11 +109,21 @@ func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config {
|
||||||
AsInit: true,
|
AsInit: true,
|
||||||
|
|
||||||
// initialise map
|
// initialise map
|
||||||
Chmod: make(map[string]os.FileMode),
|
Chmod: make(bwrap.ChmodConfig),
|
||||||
}).
|
}).
|
||||||
SetUID(uid).SetGID(uid).
|
SetUID(uid).SetGID(uid).
|
||||||
Procfs("/proc").DevTmpfs("/dev").Mqueue("/dev/mqueue").
|
Procfs("/proc").
|
||||||
Tmpfs("/dev/fortify", 4*1024)
|
Tmpfs(fTmp, 4*1024)
|
||||||
|
|
||||||
|
if !s.Dev {
|
||||||
|
conf.DevTmpfs("/dev").Mqueue("/dev/mqueue")
|
||||||
|
} else {
|
||||||
|
conf.Bind("/dev", "/dev", false, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.AutoEtc {
|
||||||
|
conf.Dir("/etc")
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range s.Filesystem {
|
for _, c := range s.Filesystem {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
|
@ -121,7 +141,29 @@ func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config {
|
||||||
conf.Symlink(l[0], l[1])
|
conf.Symlink(l[0], l[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf
|
if s.AutoEtc {
|
||||||
|
conf.Bind("/etc", fTmp+"/etc")
|
||||||
|
|
||||||
|
// link host /etc contents to prevent passwd/group from being overwritten
|
||||||
|
if d, err := os.ReadDir("/etc"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
for _, ent := range d {
|
||||||
|
name := ent.Name()
|
||||||
|
switch name {
|
||||||
|
case "passwd":
|
||||||
|
case "group":
|
||||||
|
|
||||||
|
case "mtab":
|
||||||
|
conf.Symlink("/proc/mounts", "/etc/"+name)
|
||||||
|
default:
|
||||||
|
conf.Symlink(fTmp+"/etc/"+name, "/etc/"+name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template returns a fully populated instance of Config.
|
// Template returns a fully populated instance of Config.
|
||||||
|
@ -143,6 +185,8 @@ func Template() *Config {
|
||||||
UserNS: true,
|
UserNS: true,
|
||||||
Net: true,
|
Net: true,
|
||||||
NoNewSession: true,
|
NoNewSession: true,
|
||||||
|
UseRealUID: true,
|
||||||
|
Dev: true,
|
||||||
Wayland: false,
|
Wayland: false,
|
||||||
// example API credentials pulled from Google Chrome
|
// example API credentials pulled from Google Chrome
|
||||||
// DO NOT USE THESE IN A REAL BROWSER
|
// DO NOT USE THESE IN A REAL BROWSER
|
||||||
|
@ -152,12 +196,15 @@ func Template() *Config {
|
||||||
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT",
|
"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT",
|
||||||
},
|
},
|
||||||
Filesystem: []*FilesystemConfig{
|
Filesystem: []*FilesystemConfig{
|
||||||
{Src: "/nix"},
|
{Src: "/nix/store"},
|
||||||
{Src: "/storage/emulated/0", Write: true, Must: true},
|
{Src: "/run/current-system"},
|
||||||
{Src: "/data/user/0", Dst: "/data/data", Write: true, Must: true},
|
{Src: "/run/opengl-driver"},
|
||||||
{Src: "/var/tmp", Write: true},
|
{Src: "/var/db/nix-channels"},
|
||||||
|
{Src: "/home/chronos", Write: true, Must: true},
|
||||||
|
{Src: "/dev/dri", Device: true},
|
||||||
},
|
},
|
||||||
Link: [][2]string{{"/dev/fortify/etc", "/etc"}},
|
Link: [][2]string{{"/run/user/65534", "/run/user/150"}},
|
||||||
|
AutoEtc: true,
|
||||||
Override: []string{"/var/run/nscd"},
|
Override: []string{"/var/run/nscd"},
|
||||||
},
|
},
|
||||||
SystemBus: &dbus.Config{
|
SystemBus: &dbus.Config{
|
||||||
|
|
|
@ -159,6 +159,7 @@ func (a *app) Seal(config *Config) error {
|
||||||
UserNS: true,
|
UserNS: true,
|
||||||
Net: true,
|
Net: true,
|
||||||
NoNewSession: true,
|
NoNewSession: true,
|
||||||
|
AutoEtc: true,
|
||||||
}
|
}
|
||||||
// bind entries in /
|
// bind entries in /
|
||||||
if d, err := a.os.ReadDir("/"); err != nil {
|
if d, err := a.os.ReadDir("/"); err != nil {
|
||||||
|
@ -173,9 +174,8 @@ func (a *app) Seal(config *Config) error {
|
||||||
case "/run":
|
case "/run":
|
||||||
case "/tmp":
|
case "/tmp":
|
||||||
case "/mnt":
|
case "/mnt":
|
||||||
|
|
||||||
case "/etc":
|
case "/etc":
|
||||||
b = append(b, &FilesystemConfig{Src: p, Dst: "/dev/fortify/etc", Write: false, Must: true})
|
|
||||||
default:
|
default:
|
||||||
b = append(b, &FilesystemConfig{Src: p, Write: true, Must: true})
|
b = append(b, &FilesystemConfig{Src: p, Write: true, Must: true})
|
||||||
}
|
}
|
||||||
|
@ -208,35 +208,14 @@ func (a *app) Seal(config *Config) error {
|
||||||
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) {
|
if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) {
|
||||||
conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true})
|
conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true})
|
||||||
}
|
}
|
||||||
// link host /etc to prevent passwd/group from being overwritten
|
|
||||||
if d, err := a.os.ReadDir("/etc"); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
b := make([][2]string, 0, len(d))
|
|
||||||
for _, ent := range d {
|
|
||||||
name := ent.Name()
|
|
||||||
switch name {
|
|
||||||
case "passwd":
|
|
||||||
case "group":
|
|
||||||
|
|
||||||
case "mtab":
|
|
||||||
b = append(b, [2]string{
|
|
||||||
"/proc/mounts",
|
|
||||||
"/etc/" + name,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
b = append(b, [2]string{
|
|
||||||
"/dev/fortify/etc/" + name,
|
|
||||||
"/etc/" + name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf.Link = append(conf.Link, b...)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.Confinement.Sandbox = conf
|
config.Confinement.Sandbox = conf
|
||||||
}
|
}
|
||||||
seal.sys.bwrap = config.Confinement.Sandbox.Bwrap(a.os.Geteuid())
|
if b, err := config.Confinement.Sandbox.Bwrap(a.os); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
seal.sys.bwrap = b
|
||||||
|
}
|
||||||
seal.sys.override = config.Confinement.Sandbox.Override
|
seal.sys.override = config.Confinement.Sandbox.Override
|
||||||
if seal.sys.bwrap.SetEnv == nil {
|
if seal.sys.bwrap.SetEnv == nil {
|
||||||
seal.sys.bwrap.SetEnv = make(map[string]string)
|
seal.sys.bwrap.SetEnv = make(map[string]string)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package linux
|
package linux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
@ -31,6 +32,8 @@ type System interface {
|
||||||
Open(name string) (fs.File, error)
|
Open(name string) (fs.File, error)
|
||||||
// Exit provides [os.Exit].
|
// Exit provides [os.Exit].
|
||||||
Exit(code int)
|
Exit(code int)
|
||||||
|
// Stdout provides [os.Stdout].
|
||||||
|
Stdout() io.Writer
|
||||||
|
|
||||||
// FshimPath returns an absolute path to the fshim binary.
|
// FshimPath returns an absolute path to the fshim binary.
|
||||||
FshimPath() string
|
FshimPath() string
|
||||||
|
|
|
@ -2,6 +2,7 @@ package linux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -34,6 +35,7 @@ func (s *Std) ReadDir(name string) ([]os.DirEntry, error) { return os.ReadDir(na
|
||||||
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
func (s *Std) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) }
|
||||||
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
func (s *Std) Open(name string) (fs.File, error) { return os.Open(name) }
|
||||||
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
func (s *Std) Exit(code int) { fmsg.Exit(code) }
|
||||||
|
func (s *Std) Stdout() io.Writer { return os.Stdout }
|
||||||
|
|
||||||
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
|
|
25
license.go
25
license.go
|
@ -1,25 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed LICENSE
|
|
||||||
license string
|
|
||||||
|
|
||||||
printLicense bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printLicense, "license", false, "Print license")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryLicense() {
|
|
||||||
if printLicense {
|
|
||||||
fmt.Println(license)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
205
main.go
205
main.go
|
@ -1,16 +1,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"git.ophivana.moe/security/fortify/dbus"
|
||||||
"git.ophivana.moe/security/fortify/internal"
|
"git.ophivana.moe/security/fortify/internal"
|
||||||
"git.ophivana.moe/security/fortify/internal/app"
|
"git.ophivana.moe/security/fortify/internal/app"
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
"git.ophivana.moe/security/fortify/internal/fmsg"
|
||||||
"git.ophivana.moe/security/fortify/internal/linux"
|
"git.ophivana.moe/security/fortify/internal/linux"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/state"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagVerbose bool
|
flagVerbose bool
|
||||||
|
|
||||||
|
//go:embed LICENSE
|
||||||
|
license string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -25,32 +35,194 @@ func main() {
|
||||||
// not fatal: this program runs as the privileged user
|
// not fatal: this program runs as the privileged user
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
fmsg.SetVerbose(flagVerbose)
|
|
||||||
|
|
||||||
if os.SdBooted() {
|
|
||||||
fmsg.VPrintln("system booted with systemd as init system")
|
|
||||||
}
|
|
||||||
|
|
||||||
// root check
|
|
||||||
if os.Geteuid() == 0 {
|
if os.Geteuid() == 0 {
|
||||||
fmsg.Fatal("this program must not run as root")
|
fmsg.Fatal("this program must not run as root")
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// version/license/template command early exit
|
flag.CommandLine.Usage = func() {
|
||||||
tryVersion()
|
fmt.Println()
|
||||||
tryLicense()
|
fmt.Println("Usage:\tfortify [-v] COMMAND [OPTIONS]")
|
||||||
tryTemplate()
|
fmt.Println()
|
||||||
|
fmt.Println("Commands:")
|
||||||
|
w := tabwriter.NewWriter(os.Stdout(), 0, 1, 4, ' ', 0)
|
||||||
|
commands := [][2]string{
|
||||||
|
{"app", "Launch app defined by the specified config file"},
|
||||||
|
{"run", "Configure and start a permissive default sandbox"},
|
||||||
|
{"ps", "List active apps and their state"},
|
||||||
|
{"version", "Show fortify version"},
|
||||||
|
{"license", "Show full license text"},
|
||||||
|
{"template", "Produce a config template"},
|
||||||
|
{"help", "Show this help message"},
|
||||||
|
}
|
||||||
|
for _, c := range commands {
|
||||||
|
_, _ = fmt.Fprintf(w, "\t%s\t%s\n", c[0], c[1])
|
||||||
|
}
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
fmt.Printf("fortify: cannot write command list: %v\n", err)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
fmsg.SetVerbose(flagVerbose)
|
||||||
|
|
||||||
// state query command early exit
|
args := flag.Args()
|
||||||
tryState()
|
if len(args) == 0 {
|
||||||
|
flag.CommandLine.Usage()
|
||||||
|
fmsg.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "version": // print version string
|
||||||
|
if v, ok := internal.Check(internal.Version); ok {
|
||||||
|
fmt.Println(v)
|
||||||
|
} else {
|
||||||
|
fmt.Println("impure")
|
||||||
|
}
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "license": // print embedded license
|
||||||
|
fmt.Println(license)
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "template": // print full template configuration
|
||||||
|
if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil {
|
||||||
|
fmsg.Fatalf("cannot generate template: %v", err)
|
||||||
|
panic("unreachable")
|
||||||
|
} else {
|
||||||
|
fmt.Println(string(s))
|
||||||
|
}
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "help": // print help message
|
||||||
|
flag.CommandLine.Usage()
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "ps": // print all state info
|
||||||
|
var w *tabwriter.Writer
|
||||||
|
state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath)
|
||||||
|
if w != nil {
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
fmsg.Println("cannot format output:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("No information available")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "app": // launch app from configuration file
|
||||||
|
if len(args) < 2 {
|
||||||
|
fmsg.Fatal("app requires at least 1 argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := new(app.Config)
|
||||||
|
if f, err := os.Open(args[1]); err != nil {
|
||||||
|
fmsg.Fatalf("cannot access config file %q: %s", args[1], err)
|
||||||
|
panic("unreachable")
|
||||||
|
} else if err = json.NewDecoder(f).Decode(&config); err != nil {
|
||||||
|
fmsg.Fatalf("cannot parse config file %q: %s", args[1], err)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// append extra args
|
||||||
|
config.Command = append(config.Command, args[2:]...)
|
||||||
|
|
||||||
|
// invoke app
|
||||||
|
runApp(config)
|
||||||
|
case "run": // run app in permissive defaults usage pattern
|
||||||
|
set := flag.NewFlagSet("run", flag.ExitOnError)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbusConfigSession string
|
||||||
|
dbusConfigSystem string
|
||||||
|
dbusID string
|
||||||
|
mpris bool
|
||||||
|
dbusVerbose bool
|
||||||
|
|
||||||
|
userName string
|
||||||
|
enablements [system.ELen]bool
|
||||||
|
|
||||||
|
launchMethodText string
|
||||||
|
)
|
||||||
|
|
||||||
|
set.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults")
|
||||||
|
set.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable")
|
||||||
|
set.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available")
|
||||||
|
set.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available")
|
||||||
|
set.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy")
|
||||||
|
|
||||||
|
set.StringVar(&userName, "u", "chronos", "Passwd name of user to run as")
|
||||||
|
set.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket")
|
||||||
|
set.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection")
|
||||||
|
set.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection")
|
||||||
|
set.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie")
|
||||||
|
|
||||||
|
methodHelpString := "Method of launching the child process, can be one of \"sudo\""
|
||||||
|
if os.SdBooted() {
|
||||||
|
methodHelpString += ", \"systemd\""
|
||||||
|
}
|
||||||
|
set.StringVar(&launchMethodText, "method", "sudo", methodHelpString)
|
||||||
|
|
||||||
|
// Ignore errors; set is set for ExitOnError.
|
||||||
|
_ = set.Parse(args[1:])
|
||||||
|
|
||||||
|
// initialise config from flags
|
||||||
|
config := &app.Config{
|
||||||
|
ID: dbusID,
|
||||||
|
User: userName,
|
||||||
|
Command: set.Args(),
|
||||||
|
Method: launchMethodText,
|
||||||
|
}
|
||||||
|
|
||||||
|
// enablements from flags
|
||||||
|
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
||||||
|
if enablements[i] {
|
||||||
|
config.Confinement.Enablements.Set(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse D-Bus config file from flags if applicable
|
||||||
|
if enablements[system.EDBus] {
|
||||||
|
if dbusConfigSession == "builtin" {
|
||||||
|
config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris)
|
||||||
|
} else {
|
||||||
|
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
||||||
|
fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SessionBus = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// system bus proxy is optional
|
||||||
|
if dbusConfigSystem != "nil" {
|
||||||
|
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
||||||
|
fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SystemBus = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override log from configuration
|
||||||
|
if dbusVerbose {
|
||||||
|
config.Confinement.SessionBus.Log = true
|
||||||
|
config.Confinement.SystemBus.Log = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoke app
|
||||||
|
runApp(config)
|
||||||
|
default:
|
||||||
|
fmsg.Fatalf("%q is not a valid command", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runApp(config *app.Config) {
|
||||||
|
if os.SdBooted() {
|
||||||
|
fmsg.VPrintln("system booted with systemd as init system")
|
||||||
|
}
|
||||||
|
|
||||||
// invoke app
|
|
||||||
a, err := app.New(os)
|
a, err := app.New(os)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmsg.Fatalf("cannot create app: %s\n", err)
|
fmsg.Fatalf("cannot create app: %s\n", err)
|
||||||
} else if err = a.Seal(loadConfig()); err != nil {
|
} else if err = a.Seal(config); err != nil {
|
||||||
logBaseError(err, "cannot seal app:")
|
logBaseError(err, "cannot seal app:")
|
||||||
fmsg.Exit(1)
|
fmsg.Exit(1)
|
||||||
} else if err = a.Start(); err != nil {
|
} else if err = a.Start(); err != nil {
|
||||||
|
@ -69,4 +241,5 @@ func main() {
|
||||||
fmsg.Println("inner wait failed:", err)
|
fmsg.Println("inner wait failed:", err)
|
||||||
}
|
}
|
||||||
fmsg.Exit(r)
|
fmsg.Exit(r)
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
230
nixos.nix
230
nixos.nix
|
@ -15,6 +15,7 @@ let
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
foldlAttrs
|
foldlAttrs
|
||||||
optional
|
optional
|
||||||
|
optionals
|
||||||
;
|
;
|
||||||
|
|
||||||
cfg = config.environment.fortify;
|
cfg = config.environment.fortify;
|
||||||
|
@ -39,6 +40,7 @@ in
|
||||||
listOf
|
listOf
|
||||||
attrsOf
|
attrsOf
|
||||||
nullOr
|
nullOr
|
||||||
|
functionTo
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
attrsOf (submodule {
|
attrsOf (submodule {
|
||||||
|
@ -54,6 +56,14 @@ in
|
||||||
launchers = mkOption {
|
launchers = mkOption {
|
||||||
type = attrsOf (submodule {
|
type = attrsOf (submodule {
|
||||||
options = {
|
options = {
|
||||||
|
id = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Freedesktop application ID.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
command = mkOption {
|
command = mkOption {
|
||||||
type = nullOr str;
|
type = nullOr str;
|
||||||
default = null;
|
default = null;
|
||||||
|
@ -63,17 +73,29 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
method = mkOption {
|
||||||
|
type = enum [
|
||||||
|
"simple"
|
||||||
|
"sudo"
|
||||||
|
"systemd"
|
||||||
|
];
|
||||||
|
default = "systemd";
|
||||||
|
description = ''
|
||||||
|
Launch method for the sandboxed program.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
dbus = {
|
dbus = {
|
||||||
config = mkOption {
|
session = mkOption {
|
||||||
type = nullOr anything;
|
type = nullOr (functionTo anything);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
D-Bus custom configuration.
|
D-Bus session bus custom configuration.
|
||||||
Setting this to null will enable built-in defaults.
|
Setting this to null will enable built-in defaults.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
configSystem = mkOption {
|
system = mkOption {
|
||||||
type = nullOr anything;
|
type = nullOr anything;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -81,24 +103,55 @@ in
|
||||||
Setting this to null will disable the system bus proxy.
|
Setting this to null will disable the system bus proxy.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
id = mkOption {
|
env = mkOption {
|
||||||
type = nullOr str;
|
type = nullOr (attrsOf str);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
D-Bus application id.
|
Environment variables to set for the initial process in the sandbox.
|
||||||
Setting this to null will disable own path in defaults.
|
'';
|
||||||
Has no effect if custom configuration is set.
|
};
|
||||||
'';
|
|
||||||
|
nix = mkEnableOption ''
|
||||||
|
Whether to allow nix daemon connections from within sandbox.
|
||||||
|
'';
|
||||||
|
|
||||||
|
userns = mkEnableOption ''
|
||||||
|
Whether to allow userns within sandbox.
|
||||||
|
'';
|
||||||
|
|
||||||
|
useRealUid = mkEnableOption ''
|
||||||
|
Whether to map to fortify's real UID within the sandbox.
|
||||||
|
'';
|
||||||
|
|
||||||
|
net =
|
||||||
|
mkEnableOption ''
|
||||||
|
Whether to allow network access within sandbox.
|
||||||
|
''
|
||||||
|
// {
|
||||||
|
default = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
mpris = mkOption {
|
gpu = mkOption {
|
||||||
type = bool;
|
type = nullOr bool;
|
||||||
default = false;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
Whether to enable MPRIS in D-Bus defaults.
|
Target process GPU and driver access.
|
||||||
'';
|
Setting this to null will enable GPU whenever X or Wayland is enabled.
|
||||||
};
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
dev = mkEnableOption ''
|
||||||
|
Whether to allow access to all devices within sandbox.
|
||||||
|
'';
|
||||||
|
|
||||||
|
extraPaths = mkOption {
|
||||||
|
type = listOf anything;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Extra paths to make available inside the sandbox.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
capability = {
|
capability = {
|
||||||
|
@ -143,18 +196,6 @@ in
|
||||||
Setting this to null will default package name to wrapper name.
|
Setting this to null will default package name to wrapper name.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
method = mkOption {
|
|
||||||
type = enum [
|
|
||||||
"simple"
|
|
||||||
"sudo"
|
|
||||||
"systemd"
|
|
||||||
];
|
|
||||||
default = "systemd";
|
|
||||||
description = ''
|
|
||||||
Launch method for the sandboxed program.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
default = { };
|
default = { };
|
||||||
|
@ -222,26 +263,113 @@ in
|
||||||
name: launcher:
|
name: launcher:
|
||||||
with launcher.capability;
|
with launcher.capability;
|
||||||
let
|
let
|
||||||
command = if launcher.command == null then name else launcher.command;
|
extendDBusDefault = id: ext: {
|
||||||
|
filter = true;
|
||||||
|
|
||||||
|
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
||||||
|
own =
|
||||||
|
(optionals (launcher.id != null) [
|
||||||
|
"${id}.*"
|
||||||
|
"org.mpris.MediaPlayer2.${id}.*"
|
||||||
|
])
|
||||||
|
++ ext.own;
|
||||||
|
call = {
|
||||||
|
"org.freedesktop.portal.*" = "*";
|
||||||
|
} // ext.call;
|
||||||
|
broadcast = {
|
||||||
|
"org.freedesktop.portal.*" = "@/org/freedesktop/portal/*";
|
||||||
|
} // ext.broadcast;
|
||||||
|
};
|
||||||
dbusConfig =
|
dbusConfig =
|
||||||
if launcher.dbus.config != null then
|
let
|
||||||
pkgs.writeText "${name}-dbus.json" (builtins.toJSON launcher.dbus.config)
|
default = {
|
||||||
else
|
talk = [ ];
|
||||||
null;
|
own = [ ];
|
||||||
dbusSystem =
|
call = { };
|
||||||
if launcher.dbus.configSystem != null then
|
broadcast = { };
|
||||||
pkgs.writeText "${name}-dbus-system.json" (builtins.toJSON launcher.dbus.configSystem)
|
};
|
||||||
else
|
in
|
||||||
null;
|
{
|
||||||
capArgs =
|
session_bus =
|
||||||
(if wayland then " --wayland" else "")
|
if launcher.dbus.session != null then
|
||||||
+ (if x11 then " -X" else "")
|
(launcher.dbus.session (extendDBusDefault launcher.id))
|
||||||
+ (if dbus then " --dbus" else "")
|
else
|
||||||
+ (if pulse then " --pulse" else "")
|
(extendDBusDefault launcher.id default);
|
||||||
+ (if launcher.dbus.mpris then " --mpris" else "")
|
system_bus = launcher.dbus.system;
|
||||||
+ (if launcher.dbus.id != null then " --dbus-id ${launcher.dbus.id}" else "")
|
};
|
||||||
+ (if dbusConfig != null then " --dbus-config ${dbusConfig}" else "")
|
command = if launcher.command == null then name else launcher.command;
|
||||||
+ (if dbusSystem != null then " --dbus-system ${dbusSystem}" else "");
|
enablements =
|
||||||
|
(if wayland then 1 else 0)
|
||||||
|
+ (if x11 then 2 else 0)
|
||||||
|
+ (if dbus then 4 else 0)
|
||||||
|
+ (if pulse then 8 else 0);
|
||||||
|
conf = {
|
||||||
|
inherit (launcher) id method;
|
||||||
|
inherit user;
|
||||||
|
command = [
|
||||||
|
"/run/current-system/sw/bin/zsh"
|
||||||
|
(pkgs.writeShellScript "${name}-start" ("exec " + command + " $@"))
|
||||||
|
];
|
||||||
|
confinement = {
|
||||||
|
sandbox = {
|
||||||
|
inherit (launcher)
|
||||||
|
userns
|
||||||
|
net
|
||||||
|
dev
|
||||||
|
env
|
||||||
|
;
|
||||||
|
use_real_uid = launcher.useRealUid;
|
||||||
|
filesystem =
|
||||||
|
[
|
||||||
|
{ src = "/bin"; }
|
||||||
|
{ src = "/usr/bin"; }
|
||||||
|
{ src = "/nix/store"; }
|
||||||
|
{ src = "/run/current-system"; }
|
||||||
|
{
|
||||||
|
src = "/sys/block";
|
||||||
|
require = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
src = "/sys/bus";
|
||||||
|
require = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
src = "/sys/class";
|
||||||
|
require = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
src = "/sys/dev";
|
||||||
|
require = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
src = "/sys/devices";
|
||||||
|
require = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
src = "/home/${user}";
|
||||||
|
write = true;
|
||||||
|
require = true;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ optionals launcher.nix [
|
||||||
|
{ src = "/nix/var"; }
|
||||||
|
{ src = "/var/db/nix-channels"; }
|
||||||
|
]
|
||||||
|
++ optionals (if launcher.gpu != null then launcher.gpu else wayland || x11) [
|
||||||
|
{ src = "/run/opengl-driver"; }
|
||||||
|
{
|
||||||
|
src = "/dev/dri";
|
||||||
|
dev = true;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ launcher.extraPaths;
|
||||||
|
auto_etc = true;
|
||||||
|
override = [ "/var/run/nscd" ];
|
||||||
|
};
|
||||||
|
inherit enablements;
|
||||||
|
inherit (dbusConfig) session_bus system_bus;
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
pkgs.writeShellScriptBin name (
|
pkgs.writeShellScriptBin name (
|
||||||
if launcher.method == "simple" then
|
if launcher.method == "simple" then
|
||||||
|
@ -250,7 +378,7 @@ in
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
''
|
''
|
||||||
exec fortify${capArgs} --method ${launcher.method} -u ${user} $SHELL -c "exec ${command} $@"
|
exec fortify app ${pkgs.writeText "fortify-${name}.json" (builtins.toJSON conf)} $@
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
) launchers;
|
) launchers;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
buildGoModule rec {
|
buildGoModule rec {
|
||||||
pname = "fortify";
|
pname = "fortify";
|
||||||
version = "0.0.11";
|
version = "0.1.0";
|
||||||
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
vendorHash = null;
|
vendorHash = null;
|
||||||
|
|
35
state.go
35
state.go
|
@ -1,35 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
stateActionEarly bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&stateActionEarly, "state", false, "print state information of active launchers")
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryState is called after app initialisation
|
|
||||||
func tryState() {
|
|
||||||
if stateActionEarly {
|
|
||||||
var w *tabwriter.Writer
|
|
||||||
state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath)
|
|
||||||
if w != nil {
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
fmsg.Println("cannot format output:", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Println("No information available")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmsg.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
27
version.go
27
version.go
|
@ -1,27 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
printVersion bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printVersion, "V", false, "Print version")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryVersion() {
|
|
||||||
if printVersion {
|
|
||||||
if v, ok := internal.Check(internal.Version); ok {
|
|
||||||
fmt.Println(v)
|
|
||||||
} else {
|
|
||||||
fmt.Println("impure")
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue