nix: integrate dynamic users
test / test (push) Successful in 21s
Details
test / test (push) Successful in 21s
Details
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
05b7dbf066
commit
8f3f0c7bbf
254
nixos.nix
254
nixos.nix
|
@ -11,8 +11,12 @@ let
|
||||||
mkOption
|
mkOption
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
mkIf
|
mkIf
|
||||||
|
mkDefault
|
||||||
mapAttrs
|
mapAttrs
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
|
mergeAttrsList
|
||||||
|
imap1
|
||||||
|
foldr
|
||||||
foldlAttrs
|
foldlAttrs
|
||||||
optional
|
optional
|
||||||
optionals
|
optionals
|
||||||
|
@ -26,8 +30,24 @@ in
|
||||||
environment.fortify = {
|
environment.fortify = {
|
||||||
enable = mkEnableOption "fortify";
|
enable = mkEnableOption "fortify";
|
||||||
|
|
||||||
target = mkOption {
|
package = mkOption {
|
||||||
default = { };
|
type = types.package;
|
||||||
|
default = pkgs.callPackage ./package.nix { };
|
||||||
|
description = "Package providing fortify.";
|
||||||
|
};
|
||||||
|
|
||||||
|
users = mkOption {
|
||||||
|
type =
|
||||||
|
let
|
||||||
|
inherit (types) attrsOf ints;
|
||||||
|
in
|
||||||
|
attrsOf (ints.between 0 99);
|
||||||
|
description = ''
|
||||||
|
Users allowed to spawn fortify apps, as well as their fortify ID value.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
apps = mkOption {
|
||||||
type =
|
type =
|
||||||
let
|
let
|
||||||
inherit (types)
|
inherit (types)
|
||||||
|
@ -43,8 +63,23 @@ in
|
||||||
functionTo
|
functionTo
|
||||||
;
|
;
|
||||||
in
|
in
|
||||||
attrsOf (submodule {
|
listOf (submodule {
|
||||||
options = {
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
App name, typically command.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
id = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Freedesktop application ID.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
packages = mkOption {
|
packages = mkOption {
|
||||||
type = listOf package;
|
type = listOf package;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
|
@ -53,15 +88,10 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
launchers = mkOption {
|
extraConfig = mkOption {
|
||||||
type = attrsOf (submodule {
|
type = anything;
|
||||||
options = {
|
default = { };
|
||||||
id = mkOption {
|
description = "Extra home-manager configuration.";
|
||||||
type = nullOr str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Freedesktop application ID.
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
script = mkOption {
|
script = mkOption {
|
||||||
|
@ -82,15 +112,11 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
method = mkOption {
|
groups = mkOption {
|
||||||
type = enum [
|
type = listOf str;
|
||||||
"simple"
|
default = [ ];
|
||||||
"sudo"
|
|
||||||
"systemd"
|
|
||||||
];
|
|
||||||
default = "systemd";
|
|
||||||
description = ''
|
description = ''
|
||||||
Launch method for the sandboxed program.
|
List of groups to inherit from the privileged user.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -207,46 +233,8 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
default = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
persistence = mkOption {
|
|
||||||
type = submodule {
|
|
||||||
options = {
|
|
||||||
directories = mkOption {
|
|
||||||
type = listOf anything;
|
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
description = "Applications managed by fortify.";
|
||||||
|
|
||||||
files = mkOption {
|
|
||||||
type = listOf anything;
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
description = ''
|
|
||||||
Per-user state passed to github:nix-community/impermanence.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraConfig = mkOption {
|
|
||||||
type = anything;
|
|
||||||
default = { };
|
|
||||||
description = "Extra home-manager configuration.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
package = mkOption {
|
|
||||||
type = types.package;
|
|
||||||
default = pkgs.callPackage ./package.nix { };
|
|
||||||
description = "Package providing fortify.";
|
|
||||||
};
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "Privileged user account.";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
stateDir = mkOption {
|
stateDir = mkOption {
|
||||||
|
@ -259,25 +247,50 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
environment.persistence.${cfg.stateDir}.users = mapAttrs (_: target: target.persistence) cfg.target;
|
security.wrappers.fsu = {
|
||||||
|
source = "${cfg.package}/libexec/fsu";
|
||||||
|
setuid = true;
|
||||||
|
owner = "root";
|
||||||
|
setgid = true;
|
||||||
|
group = "root";
|
||||||
|
};
|
||||||
|
|
||||||
home-manager.users =
|
environment.etc = {
|
||||||
mapAttrs (_: target: target.extraConfig // { home.packages = target.packages; }) cfg.target
|
fsurc = {
|
||||||
// {
|
mode = "0400";
|
||||||
${cfg.user}.home.packages =
|
text = foldlAttrs (
|
||||||
|
acc: username: fid:
|
||||||
|
"${toString config.users.users.${username}.uid} ${toString fid}\n" + acc
|
||||||
|
) "" cfg.users;
|
||||||
|
};
|
||||||
|
|
||||||
|
userdb.source = pkgs.runCommand "generate-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
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.userdbd.enable = mkDefault true;
|
||||||
|
|
||||||
|
home-manager =
|
||||||
let
|
let
|
||||||
wrap =
|
privPackages = mapAttrs (username: fid: {
|
||||||
user: launchers:
|
home.packages =
|
||||||
mapAttrsToList (
|
let
|
||||||
name: launcher:
|
# aid 0 is reserved
|
||||||
with launcher.capability;
|
wrappers = imap1 (
|
||||||
|
aid: app:
|
||||||
let
|
let
|
||||||
extendDBusDefault = id: ext: {
|
extendDBusDefault = id: ext: {
|
||||||
filter = true;
|
filter = true;
|
||||||
|
|
||||||
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
||||||
own =
|
own =
|
||||||
(optionals (launcher.id != null) [
|
(optionals (app.id != null) [
|
||||||
"${id}.*"
|
"${id}.*"
|
||||||
"org.mpris.MediaPlayer2.${id}.*"
|
"org.mpris.MediaPlayer2.${id}.*"
|
||||||
])
|
])
|
||||||
|
@ -296,37 +309,41 @@ in
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
session_bus =
|
session_bus =
|
||||||
if launcher.dbus.session != null then
|
if app.dbus.session != null then
|
||||||
(launcher.dbus.session (extendDBusDefault launcher.id))
|
(app.dbus.session (extendDBusDefault app.id))
|
||||||
else
|
else
|
||||||
(extendDBusDefault launcher.id default);
|
(extendDBusDefault app.id default);
|
||||||
system_bus = launcher.dbus.system;
|
system_bus = app.dbus.system;
|
||||||
};
|
};
|
||||||
command = if launcher.command == null then name else launcher.command;
|
command = if app.command == null then app.name else app.command;
|
||||||
script = if launcher.script == null then ("exec " + command + " $@") else launcher.script;
|
script = if app.script == null then ("exec " + command + " $@") else app.script;
|
||||||
enablements =
|
enablements =
|
||||||
|
with app.capability;
|
||||||
(if wayland then 1 else 0)
|
(if wayland then 1 else 0)
|
||||||
+ (if x11 then 2 else 0)
|
+ (if x11 then 2 else 0)
|
||||||
+ (if dbus then 4 else 0)
|
+ (if dbus then 4 else 0)
|
||||||
+ (if pulse then 8 else 0);
|
+ (if pulse then 8 else 0);
|
||||||
conf = {
|
conf = {
|
||||||
inherit (launcher) id method;
|
inherit (app) id;
|
||||||
inherit user;
|
|
||||||
command = [
|
command = [
|
||||||
(pkgs.writeScript "${name}-start" ''
|
(pkgs.writeScript "${app.name}-start" ''
|
||||||
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
||||||
${script}
|
${script}
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
confinement = {
|
confinement = {
|
||||||
|
app_id = aid;
|
||||||
|
inherit (app) groups;
|
||||||
|
username = "u${toString fid}_a${toString aid}";
|
||||||
|
home = "${cfg.stateDir}/${toString fid}/${toString aid}";
|
||||||
sandbox = {
|
sandbox = {
|
||||||
inherit (launcher)
|
inherit (app)
|
||||||
userns
|
userns
|
||||||
net
|
net
|
||||||
dev
|
dev
|
||||||
env
|
env
|
||||||
;
|
;
|
||||||
map_real_uid = launcher.mapRealUid;
|
map_real_uid = app.mapRealUid;
|
||||||
filesystem =
|
filesystem =
|
||||||
[
|
[
|
||||||
{ src = "/bin"; }
|
{ src = "/bin"; }
|
||||||
|
@ -353,24 +370,19 @@ in
|
||||||
src = "/sys/devices";
|
src = "/sys/devices";
|
||||||
require = false;
|
require = false;
|
||||||
}
|
}
|
||||||
{
|
|
||||||
src = "/home/${user}";
|
|
||||||
write = true;
|
|
||||||
require = true;
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
++ optionals launcher.nix [
|
++ optionals app.nix [
|
||||||
{ src = "/nix/var"; }
|
{ src = "/nix/var"; }
|
||||||
{ src = "/var/db/nix-channels"; }
|
{ src = "/var/db/nix-channels"; }
|
||||||
]
|
]
|
||||||
++ optionals (if launcher.gpu != null then launcher.gpu else wayland || x11) [
|
++ optionals (if app.gpu != null then app.gpu else app.capability.wayland || app.capability.x11) [
|
||||||
{ src = "/run/opengl-driver"; }
|
{ src = "/run/opengl-driver"; }
|
||||||
{
|
{
|
||||||
src = "/dev/dri";
|
src = "/dev/dri";
|
||||||
dev = true;
|
dev = true;
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
++ launcher.extraPaths;
|
++ app.extraPaths;
|
||||||
auto_etc = true;
|
auto_etc = true;
|
||||||
override = [ "/var/run/nscd" ];
|
override = [ "/var/run/nscd" ];
|
||||||
};
|
};
|
||||||
|
@ -379,32 +391,19 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.writeShellScriptBin name (
|
pkgs.writeShellScriptBin app.name ''
|
||||||
if launcher.method == "simple" then
|
exec fortify app ${pkgs.writeText "fortify-${app.name}.json" (builtins.toJSON conf)} $@
|
||||||
''
|
''
|
||||||
exec sudo -u ${user} -i ${command} $@
|
) cfg.apps;
|
||||||
''
|
|
||||||
else
|
|
||||||
''
|
|
||||||
exec fortify app ${pkgs.writeText "fortify-${name}.json" (builtins.toJSON conf)} $@
|
|
||||||
''
|
|
||||||
)
|
|
||||||
) launchers;
|
|
||||||
in
|
in
|
||||||
foldlAttrs (
|
foldr (
|
||||||
acc: user: target:
|
app: acc:
|
||||||
acc
|
|
||||||
++ (foldlAttrs (
|
|
||||||
shares: name: launcher:
|
|
||||||
let
|
let
|
||||||
pkg = if launcher.share != null then launcher.share else pkgs.${name};
|
pkg = if app.share != null then app.share else pkgs.${app.name};
|
||||||
copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true";
|
copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true";
|
||||||
in
|
in
|
||||||
shares
|
optional (app.capability.wayland || app.capability.x11) (
|
||||||
++
|
pkgs.runCommand "${app.name}-share" { } ''
|
||||||
optional (launcher.method != "simple" && (launcher.capability.wayland || launcher.capability.x11))
|
|
||||||
(
|
|
||||||
pkgs.runCommand "${name}-share" { } ''
|
|
||||||
mkdir -p $out/share
|
mkdir -p $out/share
|
||||||
${copy "${pkg}/share/applications"}
|
${copy "${pkg}/share/applications"}
|
||||||
${copy "${pkg}/share/icons"}
|
${copy "${pkg}/share/icons"}
|
||||||
|
@ -415,22 +414,25 @@ in
|
||||||
--replace-warn '${pkg}/libexec/' ""
|
--replace-warn '${pkg}/libexec/' ""
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
) (wrap user target.launchers) target.launchers)
|
++ acc
|
||||||
) [ cfg.package ] cfg.target;
|
) (wrappers ++ [ cfg.package ]) cfg.apps;
|
||||||
};
|
}) cfg.users;
|
||||||
|
|
||||||
security.polkit.extraConfig =
|
|
||||||
let
|
|
||||||
allowList = builtins.toJSON (mapAttrsToList (name: _: name) cfg.target);
|
|
||||||
in
|
in
|
||||||
''
|
{
|
||||||
polkit.addRule(function(action, subject) {
|
useUserPackages = false; # prevent users.users entries from being added
|
||||||
if (action.id == "org.freedesktop.machine1.host-shell" &&
|
|
||||||
${allowList}.indexOf(action.lookup("user")) > -1 &&
|
users = foldlAttrs (
|
||||||
subject.user == "${cfg.user}") {
|
acc: _: fid:
|
||||||
return polkit.Result.YES;
|
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;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue