programs: allow running binaries in a netns-style firejail

This commit is contained in:
Colin 2024-01-20 11:11:12 +00:00
parent 03fbf42680
commit 59187a0ec0
42 changed files with 114 additions and 53 deletions

View File

@ -15,7 +15,7 @@ in
};
};
package = pkgs.abaddon.overrideAttrs (upstream: {
packageUnwrapped = pkgs.abaddon.overrideAttrs (upstream: {
patches = (upstream.patches or []) ++ [
(pkgs.fetchpatch {
url = "https://git.uninsane.org/colin/abaddon/commit/eb551f188d34679f75adcbc83cb8d5beb4d19fd6.patch";

View File

@ -2,7 +2,7 @@
let
declPackageSet = pkgs: {
package = null;
packageUnwrapped = null;
suggestedPrograms = pkgs;
};
in
@ -239,7 +239,7 @@ in
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
font-manager.package = pkgs.font-manager.override {
font-manager.packageUnwrapped = pkgs.font-manager.override {
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
withWebkit = false;
};
@ -262,7 +262,7 @@ in
# settings (electron app)
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
python3-repl.package = pkgs.python3.withPackages (ps: with ps; [
python3-repl.packageUnwrapped = pkgs.python3.withPackages (ps: with ps; [
requests
]);

View File

@ -1,7 +1,7 @@
{ pkgs, ... }:
{
sane.programs.audacity = {
package = pkgs.audacity.override {
packageUnwrapped = pkgs.audacity.override {
# wxGTK32 uses webkitgtk-4.0.
# audacity doesn't actually need webkit though, so diable to reduce closure
wxGTK32 = pkgs.wxGTK32.override {

View File

@ -87,7 +87,7 @@ let
in
{
sane.programs.bemenu = {
package = pkgs.bemenu.overrideAttrs (upstream: {
packageUnwrapped = pkgs.bemenu.overrideAttrs (upstream: {
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
pkgs.makeWrapper
];

View File

@ -1,8 +1,8 @@
{ pkgs, ... }:
{
sane.programs.chatty = {
# package = chattyNoOauth;
package = pkgs.chatty-latest;
# packageUnwrapped = chattyNoOauth;
packageUnwrapped = pkgs.chatty-latest;
suggestedPrograms = [ "gnome-keyring" ];
persist.byStore.private = [
".local/share/chatty" # matrix avatars and files

View File

@ -56,6 +56,7 @@
./neovim.nix
./newsflash.nix
./nheko.nix
./nicotine-plus.nix
./nix-index.nix
./notejot.nix
./ntfy-sh.nix

View File

@ -1,7 +1,7 @@
{ pkgs, ... }:
{
sane.programs.dialect = {
package = pkgs.dialect.overrideAttrs (upstream: {
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {
# TODO: send upstream
# TODO: figure out how to get audio working
# TODO: move to runtimeDependencies?

View File

@ -7,7 +7,7 @@
{ pkgs, ... }:
{
sane.programs.element-desktop = {
package = pkgs.element-desktop.override {
packageUnwrapped = pkgs.element-desktop.override {
# use pre-build electron because otherwise it takes 4 hrs to build from source.
electron = pkgs.electron-bin;
};

View File

@ -22,7 +22,7 @@
#
# TODO: consider `WEBKIT_USE_SINGLE_WEB_PROCESS=1` for better perf
# - this runs all tabs in 1 process. which is fine, if i'm not a heavy multi-tabber
package = pkgs.epiphany.overrideAttrs (upstream: {
packageUnwrapped = pkgs.epiphany.overrideAttrs (upstream: {
preFixup = ''
gappsWrapperArgs+=(
--set WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS "1"

View File

@ -7,7 +7,7 @@ let
in
{
sane.programs.feedbackd = {
package = pkgs.rmDbusServices pkgs.feedbackd;
packageUnwrapped = pkgs.rmDbusServices pkgs.feedbackd;
configOption = with lib; mkOption {
type = types.submodule {

View File

@ -38,7 +38,7 @@ let
# defaultSettings = firefoxSettings;
defaultSettings = librewolfSettings;
package = (pkgs.wrapFirefox cfg.browser.browser {
packageUnwrapped = (pkgs.wrapFirefox cfg.browser.browser {
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
inherit (cfg.browser) extraPrefsFiles libName;
@ -212,7 +212,7 @@ in
})
({
sane.programs.firefox = {
inherit package;
inherit packageUnwrapped;
suggestedPrograms = [
"open-in-mpv"

View File

@ -62,8 +62,8 @@
{ pkgs, ... }:
{
sane.programs.flare-signal = {
package = pkgs.flare-signal-nixified;
# package = pkgs.flare-signal;
packageUnwrapped = pkgs.flare-signal-nixified;
# packageUnwrapped = pkgs.flare-signal;
persist.byStore.private = [
# everything: conf, state, files, all opaque
".local/share/flare"

View File

@ -23,9 +23,9 @@ let
in
{
sane.programs.fractal = {
package = pkgs.fractal-nixified.optimized;
# package = pkgs.fractal-latest;
# package = pkgs.fractal-next;
packageUnwrapped = pkgs.fractal-nixified.optimized;
# packageUnwrapped = pkgs.fractal-latest;
# packageUnwrapped = pkgs.fractal-next;
configOption = with lib; mkOption {
default = {};

View File

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }:
{
sane.programs.gnome-keyring = {
package = pkgs.gnome.gnome-keyring;
packageUnwrapped = pkgs.gnome.gnome-keyring;
};
# adds gnome-keyring as a xdg-data-portal (xdg.portal)
services.gnome.gnome-keyring = lib.mkIf config.sane.programs.gnome-keyring.enabled {

View File

@ -8,7 +8,7 @@ let
wanted-feeds = feeds.filterByFormat [ "podcast" "video" ] all-feeds;
in {
sane.programs.gpodder = {
package = pkgs.gpodder-adaptive-configured.overrideAttrs (base: {
packageUnwrapped = pkgs.gpodder-adaptive-configured.overrideAttrs (base: {
# environment variables:
# - GPODDER_HOME (defaults to "~/gPodder")
# - GPODDER_DOWNLOAD_DIR (defaults to "$GPODDER_HOME/Downloads")
@ -19,7 +19,7 @@ in {
"--set" "GPODDER_HOME" "~/.local/share/gPodder"
];
});
# package = pkgs.gpodder-configured;
# packageUnwrapped = pkgs.gpodder-configured;
fs.".config/gpodderFeeds.opml".symlink.text = feeds.feedsToOpml wanted-feeds;
# XXX: we preserve the whole thing because if we only preserve gPodder/Downloads

View File

@ -2,7 +2,7 @@
{
sane.programs.gthumb = {
# compile without webservices to avoid the expensive webkitgtk dependency
package = pkgs.gthumb.override { withWebservices = false; };
packageUnwrapped = pkgs.gthumb.override { withWebservices = false; };
mime.priority = 200; # gthumb is kinda bloated image/gallery viewer
mime.associations = {
"image/gif" = "org.gnome.gThumb.desktop";

View File

@ -17,7 +17,7 @@ in
};
};
package = pkgs.gtkcord4.overrideAttrs (upstream: {
packageUnwrapped = pkgs.gtkcord4.overrideAttrs (upstream: {
postConfigure = (upstream.postConfigure or "") + ''
# gtkcord4 uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
# go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`.

View File

@ -1,7 +1,7 @@
{ pkgs, ... }:
{
sane.programs.imagemagick = {
package = pkgs.imagemagick.override {
packageUnwrapped = pkgs.imagemagick.override {
ghostscriptSupport = true;
};
suggestedPrograms = [ "ghostscript" ];

View File

@ -2,10 +2,10 @@
{
sane.programs.jellyfin-media-player = {
# package = pkgs.jellyfin-media-player;
# packageUnwrapped = pkgs.jellyfin-media-player;
# qt6 version is slightly buggy, but also most qtwebengine apps (e.g. zeal) are on qt5
# so using qt6 would force yet *another* qtwebengine compile.
# package = pkgs.jellyfin-media-player-qt6;
# packageUnwrapped = pkgs.jellyfin-media-player-qt6;
# jellyfin stores things in a bunch of directories: this one persists auth info.
# it *might* be possible to populate this externally (it's Qt stuff), but likely to

View File

@ -35,7 +35,7 @@ let
) wantedFeeds;
in {
sane.programs.koreader = {
package = pkgs.koreader-from-src;
packageUnwrapped = pkgs.koreader-from-src;
# koreader applies these lua "patches" at boot:
# - <https://github.com/koreader/koreader/wiki/User-patches>
# the naming is IMPORTANT. these must start with a `2-` in order to be invoked during the right initialization phase

View File

@ -2,9 +2,9 @@
{
sane.programs.libreoffice = {
# package = pkgs.libreoffice-bin;
# package = pkgs.libreoffice-still;
package = pkgs.libreoffice-fresh;
# packageUnwrapped = pkgs.libreoffice-bin;
# packageUnwrapped = pkgs.libreoffice-still;
packageUnwrapped = pkgs.libreoffice-fresh;
slowToBuild = true;

View File

@ -4,7 +4,7 @@
{
sane.programs.mako = {
# we control mako as a systemd service, so have dbus not automatically activate it.
package = pkgs.rmDbusServices pkgs.mako;
packageUnwrapped = pkgs.rmDbusServices pkgs.mako;
fs.".config/mako/config".symlink.text = ''
# notification interaction mapping
# "on-touch" defaults to "dismiss", which isn't nice for touchscreens.

View File

@ -45,7 +45,7 @@ let
in
{
sane.programs.mimeo = {
package = pkgs.mimeo.overridePythonAttrs (upstream: {
packageUnwrapped = pkgs.mimeo.overridePythonAttrs (upstream: {
nativeBuildInputs = (upstream.nativeBuildInputs or []) ++ [
pkgs.copyDesktopItems
];

View File

@ -29,7 +29,7 @@ let
in
{
sane.programs.mopidy = {
package = mopidyWithExtensions (with pkgs; [
packageUnwrapped = mopidyWithExtensions (with pkgs; [
mopidy-iris # web client: <https://github.com/jaedb/Iris>
mopidy-jellyfin
mopidy-local

View File

@ -20,7 +20,7 @@ in
};
};
};
package = pkgs.wrapMpv pkgs.mpv-unwrapped {
packageUnwrapped = pkgs.wrapMpv pkgs.mpv-unwrapped {
scripts = with pkgs.mpvScripts; [
mpris
uosc

View File

@ -1,7 +1,7 @@
{ pkgs, ... }:
{
sane.programs."gnome.nautilus" = {
package = pkgs.gnome.nautilus.overrideAttrs (orig: {
packageUnwrapped = pkgs.gnome.nautilus.overrideAttrs (orig: {
# enable the "Audio and Video Properties" pane. see: <https://nixos.wiki/wiki/Nautilus>
buildInputs = orig.buildInputs ++ (with pkgs.gst_all_1; [
gst-plugins-good

View File

@ -86,8 +86,8 @@ let
in
{
sane.programs.neovim = {
# package = config.programs.neovim.finalPackage;
package = pkgs.wrapNeovimUnstable pkgs.neovim-unwrapped (pkgs.neovimUtils.makeNeovimConfig {
# packageUnwrapped = config.programs.neovim.finalPackage;
packageUnwrapped = pkgs.wrapNeovimUnstable pkgs.neovim-unwrapped (pkgs.neovimUtils.makeNeovimConfig {
withRuby = false; #< doesn't cross-compile w/o binfmt
viAlias = true;
vimAlias = true;

View File

@ -0,0 +1,10 @@
# soulseek filesharing GUI app
{ ... }:
{
sane.programs.nicotine-plus = {
net = "vpn";
# ".config/nicotine": contains the config file, with plaintext creds.
# TODO: define this as a secret instead of persisting it.
persist.byStore.private = [ ".config/nicotine" ];
};
}

View File

@ -21,7 +21,7 @@ in
};
};
package = pkgs.signal-desktop-from-src;
packageUnwrapped = pkgs.signal-desktop-from-src;
# creds, media
persist.byStore.private = [

View File

@ -2,7 +2,7 @@
{
sane.programs.sublime-music = {
package = pkgs.sublime-music-mobile;
packageUnwrapped = pkgs.sublime-music-mobile;
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json

View File

@ -23,7 +23,7 @@ in
};
};
package = pkgs.static-nix-shell.mkBash {
packageUnwrapped = pkgs.static-nix-shell.mkBash {
pname = "sway-autoscaler";
pkgs = [ "jq" "sway" "util-linux" ];
src = ./.;

View File

@ -116,7 +116,7 @@ in
default = {};
};
# prevent dbus from automatically activating swaync so i can manage it as a systemd service instead
package = pkgs.rmDbusServices (pkgs.swaynotificationcenter.overrideAttrs (upstream: {
packageUnwrapped = pkgs.rmDbusServices (pkgs.swaynotificationcenter.overrideAttrs (upstream: {
# allow toggle buttons:
patches = (upstream.patches or []) ++ [
# (pkgs.fetchpatch {

View File

@ -18,7 +18,7 @@ in
# XXX(2023/07/08): running on moby without disabling the webkit sandbox fails, with:
# - `bwrap: Can't make symlink at /var/run: File exists`
# see epiphany.nix for more info
package = pkgs.tangram.overrideAttrs (upstream: {
packageUnwrapped = pkgs.tangram.overrideAttrs (upstream: {
preFixup = ''
gappsWrapperArgs+=(
--set WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS "1"

View File

@ -1,7 +1,7 @@
{ pkgs, ... }:
{
sane.programs.tor-browser-bundle-bin = {
package = pkgs.tor-browser-bundle-bin.override {
packageUnwrapped = pkgs.tor-browser-bundle-bin.override {
# hardenedMalloc solves an "unable to connect to Tor" error when pressing the "connect" button
# - still required as of 2023/07/14
useHardenedMalloc = false;

View File

@ -1,6 +1,6 @@
{ pkgs, ... }:
{
sane.programs.xarchiver.package = pkgs.xarchiver.override {
sane.programs.xarchiver.packageUnwrapped = pkgs.xarchiver.override {
# unar doesn't cross compile well, so disable support for it
unar = null;
};

View File

@ -13,8 +13,8 @@ let
};
in {
sane.programs.zeal = {
# package = pkgs.zeal-qt6; #< TODO: upgrade system to qt6 versions of everything (i.e. jellyfin-media-player, nheko)
package = pkgs.zeal-qt5;
# packageUnwrapped = pkgs.zeal-qt6; #< TODO: upgrade system to qt6 versions of everything (i.e. jellyfin-media-player, nheko)
packageUnwrapped = pkgs.zeal-qt5;
slowToBuild = true;
persist.byStore.plaintext = [
".cache/Zeal"
@ -29,7 +29,7 @@ in {
type = configOpts;
default = {};
};
package = pkgs.symlinkJoin {
packageUnwrapped = pkgs.symlinkJoin {
name = "docsets";
# build each package with rust docs
paths = map (name:

View File

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }:
let
declPackageSet = pkgs: {
package = null;
packageUnwrapped = null;
suggestedPrograms = pkgs;
};
in
@ -162,6 +162,7 @@ in
"monero-gui" # x86-only
"mumble"
# "nheko" # Matrix chat client
"nicotine-plus" # soulseek client
# "obsidian"
"openscad" # 3d modeling
# "rhythmbox" # local music player

View File

@ -23,7 +23,7 @@ in
config = mkMerge [
{
sane.programs.phoshApps = {
package = null;
packageUnwrapped = null;
suggestedPrograms = [
"guiApps"
# TODO: see about removing gnome-bluetooth if the in-built gnome-settings bluetooth manager can work

View File

@ -169,7 +169,7 @@ in
config = lib.mkMerge [
{
sane.programs.swayApps = {
package = null;
packageUnwrapped = null;
suggestedPrograms = [
"guiApps"
"conky" # for a nice background

View File

@ -262,7 +262,7 @@ in
config = lib.mkMerge [
{
sane.programs.sxmoApps = {
package = null;
packageUnwrapped = null;
suggestedPrograms = [
"guiApps"
"bemenu" # specifically to import its theming

View File

@ -31,9 +31,33 @@ let
) (mkDefaultEnables pkgSpecs) pkgSpecs;
mkDefaultEnables = lib.mapAttrs (_pname: _pval: { system = false; user = {}; });
defaultEnables = solveDefaultEnableFor cfg;
# wrap a package so that its binaries (maybe) run in a sandbox
wrapPkg = { net }: package: (
if net == "clearnet" then
package
else if net == "vpn" then
# TODO: update the package's `.desktop` files to ensure they exec the sandboxed app.
pkgs.symlinkJoin {
inherit (package) name;
paths = [ package ];
postBuild = ''
for p in $(ls "$out/bin/"); do
unlink "$out/bin/$p"
cat <<EOF >> "$out/bin/$p"
#!/bin/sh
exec ${pkgs.sane-scripts.vpn}/bin/sane-vpn do default "${package}/bin/$p" "\$@"
EOF
chmod +x "$out/bin/$p"
done
'';
}
else
throw "unknown net type '${net}'"
);
pkgSpec = with lib; types.submodule ({ config, name, ... }: {
options = {
package = mkOption {
packageUnwrapped = mkOption {
type = types.nullOr types.package;
description = ''
package, or `null` if the program is some sort of meta set (in which case it much EXPLICITLY be set null).
@ -48,6 +72,13 @@ let
# a valid source explicitly.
lib.getAttrFromPath pkgPath pkgs;
};
package = mkOption {
type = types.nullOr types.package;
description = ''
assigned internally.
this is `packageUnwrapped`, but with the binaries possibly wrapped in sandboxing measures.
'';
};
enableFor.system = mkOption {
type = types.bool;
default = defaultEnables."${name}".system;
@ -161,6 +192,15 @@ let
marking packages like this can be used to achieve faster, but limited, rebuilds/deploys (by omitting the package).
'';
};
net = mkOption {
type = types.enum [ "clearnet" "vpn" ];
default = "clearnet";
description = ''
how this app should have its network traffic routed.
- "clearnet" for unsandboxed network.
- "vpn" to route all traffic over the default VPN.
'';
};
configOption = mkOption {
type = types.raw;
default = mkOption {
@ -181,6 +221,11 @@ let
passesSlowTest = saneCfg.enableSlowPrograms || !config.slowToBuild;
in {
enabled = (config.enableFor.system || enabledForUser) && passesSlowTest;
package = if config.packageUnwrapped == null then
null
else
wrapPkg { inherit (config) net; } config.packageUnwrapped
;
};
});
toPkgSpec = with lib; types.coercedTo types.package (p: { package = p; }) pkgSpec;

View File

@ -17,6 +17,10 @@ get_vpns() {
}
canonicalize_region() {
if [ "$region" = "default" ]; then
# TODO: don't special-case this, but e.g. grab whichever VPN has the lowest `ip rule` priority.
region="us"
fi
if networkctl list "br-$region"; then
bridge="br-$region"
elif networkctl list "br-ovpnd-$region"; then