{ lib, pkgs, config, ... }: let inherit (lib) mkIf mkDefault mapAttrs mapAttrsToList mergeAttrsList imap1 foldr foldlAttrs optional optionals ; cfg = config.environment.fortify; in { imports = [ ./options.nix ]; config = mkIf cfg.enable { security.wrappers.fsu = { source = "${cfg.package}/libexec/fsu"; setuid = true; owner = "root"; setgid = true; group = "root"; }; environment.etc = { fsurc = { mode = "0400"; text = foldlAttrs ( acc: username: fid: "${toString config.users.users.${username}.uid} ${toString fid}\n" + acc ) "" cfg.users; }; userdb.source = pkgs.runCommand "fortify-userdb" { } '' ${cfg.package}/libexec/fuserdb -o $out ${ foldlAttrs ( acc: username: fid: acc + " ${username}:${toString fid}" ) "-s /run/current-system/sw/bin/nologin -d ${cfg.stateDir}" cfg.users } ''; }; systemd.services.nix-daemon.unitConfig.RequiresMountsFor = [ "/etc/userdb" ]; services.userdbd.enable = mkDefault true; home-manager = let privPackages = mapAttrs (username: fid: { home.packages = let # aid 0 is reserved wrappers = imap1 ( aid: app: let extendDBusDefault = id: ext: { filter = true; talk = [ "org.freedesktop.Notifications" ] ++ ext.talk; own = (optionals (app.id != null) [ "${id}.*" "org.mpris.MediaPlayer2.${id}.*" ]) ++ ext.own; inherit (ext) call broadcast; }; dbusConfig = let default = { talk = [ ]; own = [ ]; call = { }; broadcast = { }; }; in { session_bus = if app.dbus.session != null then (app.dbus.session (extendDBusDefault app.id)) else (extendDBusDefault app.id default); system_bus = app.dbus.system; }; command = if app.command == null then app.name else app.command; script = if app.script == null then ("exec " + command + " $@") else app.script; enablements = with app.capability; (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 (app) id; command = [ (pkgs.writeScript "${app.name}-start" '' #!${pkgs.zsh}${pkgs.zsh.shellPath} ${script} '') ]; confinement = { app_id = aid; inherit (app) groups; username = "u${toString fid}_a${toString aid}"; home = "${cfg.stateDir}/u${toString fid}/a${toString aid}"; sandbox = { inherit (app) userns net dev env ; map_real_uid = app.mapRealUid; no_new_session = app.tty; 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; } ] ++ optionals app.nix [ { src = "/nix/var"; } { src = "/var/db/nix-channels"; } ] ++ optionals (if app.gpu != null then app.gpu else app.capability.wayland || app.capability.x11) [ { src = "/run/opengl-driver"; } { src = "/dev/dri"; dev = true; } ] ++ app.extraPaths; auto_etc = true; override = [ "/var/run/nscd" ]; }; inherit enablements; inherit (dbusConfig) session_bus system_bus; }; }; in pkgs.writeShellScriptBin app.name '' exec fortify app ${pkgs.writeText "fortify-${app.name}.json" (builtins.toJSON conf)} $@ '' ) cfg.apps; in foldr ( app: acc: let pkg = if app.share != null then app.share else pkgs.${app.name}; copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true"; in optional (app.capability.wayland || app.capability.x11) ( pkgs.runCommand "${app.name}-share" { } '' mkdir -p $out/share ${copy "${pkg}/share/applications"} ${copy "${pkg}/share/pixmaps"} ${copy "${pkg}/share/icons"} ${copy "${pkg}/share/man"} substituteInPlace $out/share/applications/* \ --replace-warn '${pkg}/bin/' "" \ --replace-warn '${pkg}/libexec/' "" '' ) ++ acc ) (wrappers ++ [ cfg.package ]) cfg.apps; }) cfg.users; in { useUserPackages = false; # prevent users.users entries from being added users = foldlAttrs ( acc: _: fid: mergeAttrsList ( # aid 0 is reserved imap1 (aid: app: { "u${toString fid}_a${toString aid}" = app.extraConfig // { home.packages = app.packages; }; }) cfg.apps ) // acc ) privPackages cfg.users; }; }; }