forked from security/hakurei
There is no reason to give the home directory special treatment, as this behaviour can be quite confusing. The home directory also does not necessarily require its own mount point, it could be provided by a parent or simply be ephemeral. Signed-off-by: Ophestra <cat@gensokyo.uk>
349 lines
12 KiB
Nix
349 lines
12 KiB
Nix
packages:
|
|
{
|
|
lib,
|
|
pkgs,
|
|
config,
|
|
...
|
|
}:
|
|
|
|
let
|
|
inherit (lib)
|
|
lists
|
|
attrsets
|
|
mkMerge
|
|
mkIf
|
|
mapAttrs
|
|
foldlAttrs
|
|
optional
|
|
optionals
|
|
;
|
|
|
|
cfg = config.environment.hakurei;
|
|
|
|
getsubuid = fid: aid: 1000000 + fid * 10000 + aid;
|
|
getsubname = fid: aid: "u${toString fid}_a${toString aid}";
|
|
getsubhome = fid: aid: "${cfg.stateDir}/u${toString fid}/a${toString aid}";
|
|
in
|
|
|
|
{
|
|
imports = [ (import ./options.nix packages) ];
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
(
|
|
let
|
|
conflictingApps = foldlAttrs (
|
|
acc: id: app:
|
|
(
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': id': app':
|
|
if id == id' || app.shareUid && app'.shareUid || app.identity != app'.identity then acc' else acc' ++ [ id ]
|
|
) [ ] cfg.apps
|
|
)
|
|
) [ ] cfg.apps;
|
|
in
|
|
{
|
|
assertion = (lists.length conflictingApps) == 0;
|
|
message = "the following hakurei apps have conflicting identities: " + (builtins.concatStringsSep ", " conflictingApps);
|
|
}
|
|
)
|
|
];
|
|
|
|
security.wrappers.hsu = {
|
|
|
|
source = "${cfg.hsuPackage}/bin/hsu";
|
|
setuid = true;
|
|
owner = "root";
|
|
setgid = true;
|
|
group = "root";
|
|
};
|
|
|
|
environment.etc.hsurc = {
|
|
mode = "0400";
|
|
text = foldlAttrs (
|
|
acc: username: fid:
|
|
"${toString config.users.users.${username}.uid} ${toString fid}\n" + acc
|
|
) "" cfg.users;
|
|
};
|
|
|
|
home-manager =
|
|
let
|
|
privPackages = mapAttrs (username: fid: {
|
|
home.packages = foldlAttrs (
|
|
acc: id: app:
|
|
[
|
|
(
|
|
let
|
|
extendDBusDefault = id: ext: {
|
|
filter = true;
|
|
|
|
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
|
|
own = [
|
|
"${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 id)) else (extendDBusDefault 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;
|
|
isGraphical = if app.gpu != null then app.gpu else app.enablements.wayland || app.enablements.x11;
|
|
|
|
conf = {
|
|
path =
|
|
if app.path == null then
|
|
pkgs.writeScript "${app.name}-start" ''
|
|
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
|
${script}
|
|
''
|
|
else
|
|
app.path;
|
|
args = if app.args == null then [ "${app.name}-start" ] else app.args;
|
|
|
|
inherit id;
|
|
|
|
inherit (dbusConfig) session_bus system_bus;
|
|
direct_wayland = app.insecureWayland;
|
|
|
|
username = getsubname fid app.identity;
|
|
home = getsubhome fid app.identity;
|
|
|
|
inherit (cfg) shell;
|
|
inherit (app) identity groups enablements;
|
|
|
|
container = {
|
|
inherit (app)
|
|
wait_delay
|
|
devel
|
|
userns
|
|
device
|
|
tty
|
|
multiarch
|
|
env
|
|
;
|
|
map_real_uid = app.mapRealUid;
|
|
host_net = app.hostNet;
|
|
host_abstract = app.hostAbstract;
|
|
|
|
filesystem =
|
|
let
|
|
bind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
};
|
|
optBind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
optional = true;
|
|
};
|
|
optDevBind = src: {
|
|
type = "bind";
|
|
inherit src;
|
|
dev = true;
|
|
optional = true;
|
|
};
|
|
in
|
|
[
|
|
(bind "/bin")
|
|
(bind "/usr/bin")
|
|
(bind "/nix/store")
|
|
(optBind "/sys/block")
|
|
(optBind "/sys/bus")
|
|
(optBind "/sys/class")
|
|
(optBind "/sys/dev")
|
|
(optBind "/sys/devices")
|
|
]
|
|
++ optionals app.nix [
|
|
(bind "/nix/var")
|
|
]
|
|
++ optionals isGraphical [
|
|
(optDevBind "/dev/dri")
|
|
(optDevBind "/dev/nvidiactl")
|
|
(optDevBind "/dev/nvidia-modeset")
|
|
(optDevBind "/dev/nvidia-uvm")
|
|
(optDevBind "/dev/nvidia-uvm-tools")
|
|
(optDevBind "/dev/nvidia0")
|
|
]
|
|
++ optionals app.useCommonPaths cfg.commonPaths
|
|
++ app.extraPaths
|
|
++ [
|
|
{
|
|
type = "bind";
|
|
dst = "/etc/";
|
|
src = "/etc/";
|
|
special = true;
|
|
}
|
|
{
|
|
type = "link";
|
|
dst = "/run/current-system";
|
|
linkname = "/run/current-system";
|
|
dereference = true;
|
|
}
|
|
]
|
|
++ optionals (isGraphical && config.hardware.graphics.enable) (
|
|
[
|
|
{
|
|
type = "link";
|
|
dst = "/run/opengl-driver";
|
|
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver"."L+".argument;
|
|
}
|
|
]
|
|
++ optionals (app.multiarch && config.hardware.graphics.enable32Bit) [
|
|
{
|
|
type = "link";
|
|
dst = "/run/opengl-driver-32";
|
|
linkname = config.systemd.tmpfiles.settings.graphics-driver."/run/opengl-driver-32"."L+".argument;
|
|
}
|
|
]
|
|
)
|
|
++ [
|
|
{
|
|
type = "bind";
|
|
src = getsubhome fid app.identity;
|
|
write = true;
|
|
ensure = true;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
checkedConfig =
|
|
name: value:
|
|
let
|
|
file = pkgs.writeText name (builtins.toJSON value);
|
|
in
|
|
pkgs.runCommand "checked-${name}" { nativeBuildInputs = [ cfg.package ]; } ''
|
|
ln -vs ${file} "$out"
|
|
hakurei show ${file}
|
|
'';
|
|
in
|
|
pkgs.writeShellScriptBin app.name ''
|
|
exec hakurei${if app.verbose then " -v" else ""} app ${checkedConfig "hakurei-app-${app.name}.json" conf} $@
|
|
''
|
|
)
|
|
]
|
|
++ (
|
|
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.enablements.wayland || app.enablements.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"}
|
|
|
|
if test -d "$out/share/applications"; then
|
|
substituteInPlace $out/share/applications/* \
|
|
--replace-warn '${pkg}/bin/' "" \
|
|
--replace-warn '${pkg}/libexec/' ""
|
|
fi
|
|
''
|
|
)
|
|
)
|
|
++ acc
|
|
) [ cfg.package ] cfg.apps;
|
|
}) cfg.users;
|
|
in
|
|
{
|
|
useUserPackages = false; # prevent users.users entries from being added
|
|
|
|
users =
|
|
mkMerge
|
|
(foldlAttrs
|
|
(
|
|
acc: _: fid:
|
|
foldlAttrs
|
|
(
|
|
acc: _: app:
|
|
(
|
|
let
|
|
key = getsubname fid app.identity;
|
|
in
|
|
{
|
|
usernames = acc.usernames // {
|
|
${key} = true;
|
|
};
|
|
merge = acc.merge ++ [
|
|
{
|
|
${key} = mkMerge (
|
|
[
|
|
app.extraConfig
|
|
{ home.packages = app.packages; }
|
|
]
|
|
++ lib.optional (!attrsets.hasAttrByPath [ key ] acc.usernames) cfg.extraHomeConfig
|
|
);
|
|
}
|
|
];
|
|
}
|
|
)
|
|
)
|
|
{
|
|
inherit (acc) usernames;
|
|
merge = acc.merge ++ [ { ${getsubname fid 0} = cfg.extraHomeConfig; } ];
|
|
}
|
|
cfg.apps
|
|
)
|
|
{
|
|
usernames = { };
|
|
merge = [ privPackages ];
|
|
}
|
|
cfg.users
|
|
).merge;
|
|
};
|
|
|
|
users =
|
|
let
|
|
getuser = fid: aid: {
|
|
isSystemUser = true;
|
|
createHome = true;
|
|
description = "Hakurei subordinate user ${toString aid} (u${toString fid})";
|
|
group = getsubname fid aid;
|
|
home = getsubhome fid aid;
|
|
uid = getsubuid fid aid;
|
|
};
|
|
getgroup = fid: aid: { gid = getsubuid fid aid; };
|
|
in
|
|
{
|
|
users = mkMerge (
|
|
foldlAttrs (
|
|
acc: _: fid:
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': _: app:
|
|
acc' ++ [ { ${getsubname fid app.identity} = getuser fid app.identity; } ]
|
|
) [ { ${getsubname fid 0} = getuser fid 0; } ] cfg.apps
|
|
) [ ] cfg.users
|
|
);
|
|
|
|
groups = mkMerge (
|
|
foldlAttrs (
|
|
acc: _: fid:
|
|
acc
|
|
++ foldlAttrs (
|
|
acc': _: app:
|
|
acc' ++ [ { ${getsubname fid app.identity} = getgroup fid app.identity; } ]
|
|
) [ { ${getsubname fid 0} = getgroup fid 0; } ] cfg.apps
|
|
) [ ] cfg.users
|
|
);
|
|
};
|
|
};
|
|
}
|