nixos/userborn: init
This commit is contained in:
parent
711d937835
commit
4620067d79
@ -1347,6 +1347,7 @@
|
||||
./services/system/systembus-notify.nix
|
||||
./services/system/systemd-lock-handler.nix
|
||||
./services/system/uptimed.nix
|
||||
./services/system/userborn.nix
|
||||
./services/system/zram-generator.nix
|
||||
./services/torrent/deluge.nix
|
||||
./services/torrent/flexget.nix
|
||||
|
183
nixos/modules/services/system/userborn.nix
Normal file
183
nixos/modules/services/system/userborn.nix
Normal file
@ -0,0 +1,183 @@
|
||||
{
|
||||
utils,
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.userborn;
|
||||
userCfg = config.users;
|
||||
|
||||
userbornConfig = {
|
||||
groups = lib.mapAttrsToList (username: opts: {
|
||||
inherit (opts) name gid members;
|
||||
}) config.users.groups;
|
||||
|
||||
users = lib.mapAttrsToList (username: opts: {
|
||||
inherit (opts)
|
||||
name
|
||||
uid
|
||||
group
|
||||
description
|
||||
home
|
||||
password
|
||||
hashedPassword
|
||||
hashedPasswordFile
|
||||
initialPassword
|
||||
initialHashedPassword
|
||||
;
|
||||
isNormal = opts.isNormalUser;
|
||||
shell = utils.toShellPath opts.shell;
|
||||
}) config.users.users;
|
||||
};
|
||||
|
||||
userbornConfigJson = pkgs.writeText "userborn.json" (builtins.toJSON userbornConfig);
|
||||
|
||||
immutableEtc = config.system.etc.overlay.enable && !config.system.etc.overlay.mutable;
|
||||
# The filenames created by userborn.
|
||||
passwordFiles = [
|
||||
"group"
|
||||
"passwd"
|
||||
"shadow"
|
||||
];
|
||||
|
||||
in
|
||||
{
|
||||
|
||||
options.services.userborn = {
|
||||
|
||||
enable = lib.mkEnableOption "userborn";
|
||||
|
||||
package = lib.mkPackageOption pkgs "userborn" { };
|
||||
|
||||
passwordFilesLocation = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = if immutableEtc then "/var/lib/nixos" else "/etc";
|
||||
defaultText = lib.literalExpression ''if immutableEtc then "/var/lib/nixos" else "/etc"'';
|
||||
description = ''
|
||||
The location of the original password files.
|
||||
|
||||
If this is not `/etc`, the files are symlinked from this location to `/etc`.
|
||||
|
||||
The primary motivation for this is an immutable `/etc`, where we cannot
|
||||
write the files directly to `/etc`.
|
||||
|
||||
However this an also serve other use cases, e.g. when `/etc` is on a `tmpfs`.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(config.systemd.sysusers.enable && cfg.enable);
|
||||
message = "You cannot use systemd-sysusers and Userborn at the same time";
|
||||
}
|
||||
{
|
||||
assertion = config.system.activationScripts.users == "";
|
||||
message = "system.activationScripts.users has to be empty to use userborn";
|
||||
}
|
||||
{
|
||||
assertion = immutableEtc -> (cfg.passwordFilesLocation != "/etc");
|
||||
message = "When `system.etc.overlay.mutable = false`, `services.userborn.passwordFilesLocation` cannot be set to `/etc`";
|
||||
}
|
||||
];
|
||||
|
||||
system.activationScripts.users = lib.mkForce "";
|
||||
system.activationScripts.hashes = lib.mkForce "";
|
||||
|
||||
systemd = {
|
||||
|
||||
# Create home directories, do not create /var/empty even if that's a user's
|
||||
# home.
|
||||
tmpfiles.settings.home-directories = lib.mapAttrs' (
|
||||
username: opts:
|
||||
lib.nameValuePair opts.home {
|
||||
d = {
|
||||
mode = opts.homeMode;
|
||||
user = username;
|
||||
inherit (opts) group;
|
||||
};
|
||||
}
|
||||
) (lib.filterAttrs (_username: opts: opts.home != "/var/empty") userCfg.users);
|
||||
|
||||
services.userborn = {
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
requiredBy = [ "sysinit-reactivation.target" ];
|
||||
after = [
|
||||
"systemd-remount-fs.service"
|
||||
"systemd-tmpfiles-setup-dev-early.service"
|
||||
];
|
||||
before = [
|
||||
"systemd-tmpfiles-setup-dev.service"
|
||||
"sysinit.target"
|
||||
"shutdown.target"
|
||||
"sysinit-reactivation.target"
|
||||
];
|
||||
conflicts = [ "shutdown.target" ];
|
||||
restartTriggers = [
|
||||
userbornConfigJson
|
||||
cfg.passwordFilesLocation
|
||||
];
|
||||
# This way we don't have to re-declare all the dependencies to other
|
||||
# services again.
|
||||
aliases = [ "systemd-sysusers.service" ];
|
||||
|
||||
unitConfig = {
|
||||
Description = "Manage Users and Groups";
|
||||
DefaultDependencies = false;
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
TimeoutSec = "90s";
|
||||
|
||||
ExecStart = "${lib.getExe cfg.package} ${userbornConfigJson} ${cfg.passwordFilesLocation}";
|
||||
|
||||
ExecStartPre = lib.mkMerge [
|
||||
(lib.mkIf (!config.system.etc.overlay.mutable) [
|
||||
"${pkgs.coreutils}/bin/mkdir -p ${cfg.passwordFilesLocation}"
|
||||
])
|
||||
|
||||
# Make the source files writable before executing userborn.
|
||||
(lib.mkIf (!userCfg.mutableUsers) (
|
||||
lib.map (file: "-${pkgs.util-linux}/bin/umount ${cfg.passwordFilesLocation}/${file}") passwordFiles
|
||||
))
|
||||
];
|
||||
|
||||
# Make the source files read-only after userborn has finished.
|
||||
ExecStartPost = lib.mkIf (!userCfg.mutableUsers) (
|
||||
lib.map (
|
||||
file:
|
||||
"${pkgs.util-linux}/bin/mount --bind -o ro ${cfg.passwordFilesLocation}/${file} ${cfg.passwordFilesLocation}/${file}"
|
||||
) passwordFiles
|
||||
);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Statically create the symlinks to passwordFilesLocation when they're not
|
||||
# inside /etc because we will not be able to do it at runtime in case of an
|
||||
# immutable /etc!
|
||||
environment.etc = lib.mkIf (cfg.passwordFilesLocation != "/etc") (
|
||||
lib.listToAttrs (
|
||||
lib.map (
|
||||
file:
|
||||
lib.nameValuePair file {
|
||||
source = "${cfg.passwordFilesLocation}/${file}";
|
||||
mode = "direct-symlink";
|
||||
}
|
||||
) passwordFiles
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ nikstur ];
|
||||
|
||||
}
|
@ -19,8 +19,8 @@
|
||||
message = "`system.etc.overlay.enable` requires `boot.initrd.systemd.enable`";
|
||||
}
|
||||
{
|
||||
assertion = (!config.system.etc.overlay.mutable) -> config.systemd.sysusers.enable;
|
||||
message = "`system.etc.overlay.mutable = false` requires `systemd.sysusers.enable`";
|
||||
assertion = (!config.system.etc.overlay.mutable) -> (config.systemd.sysusers.enable || config.services.userborn.enable);
|
||||
message = "`!system.etc.overlay.mutable` requires `systemd.sysusers.enable` or `services.userborn.enable`";
|
||||
}
|
||||
{
|
||||
assertion = lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.6";
|
||||
|
Loading…
Reference in New Issue
Block a user