feat: add persistent ids to all users/groups

This commit is contained in:
Leon Schwarzäugl 2026-01-12 22:15:57 +01:00
parent 37a8e17cc9
commit 7f65f74fef
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
62 changed files with 533 additions and 173 deletions

View file

@ -1,4 +1,4 @@
{ self, pkgs, lib, config, globals, ... }:
{ self, pkgs, lib, config, globals, confLib, ... }:
let
inherit (config.repo.secrets.common) dnsProvider dnsBase dnsMail;
@ -21,7 +21,10 @@ in
'';
};
users.groups.acme.members = lib.mkIf config.swarselmodules.server.nginx [ "nginx" ];
users = {
persistentIds.acme = confLib.mkIds 967;
groups.acme.members = lib.mkIf config.swarselmodules.server.nginx [ "nginx" ];
};
security.acme = {
acceptTerms = true;

View file

@ -45,9 +45,9 @@ in
};
boot = lib.mkIf (!config.swarselsystems.isClient) {
kernelParams = lib.mkIf (!config.swarselsystems.isCloud && ((config.swarselsystems.localVLANs == [ ]) || isRouter)) [
"ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none"
];
# kernelParams = lib.mkIf (!config.swarselsystems.isCloud && ((config.swarselsystems.localVLANs == []) || isRouter)) [
# "ip=${localIp}::${gatewayIp}:${subnetMask}:${config.networking.hostName}::none"
# ];
initrd = {
secrets."/tmp${hostKeyPathBase}" = if minimal then (lib.mkForce generatedHostKey) else (lib.mkForce hostKeyPath); # need to mkForce this or it behaves stateful
availableKernelModules = config.swarselsystems.networkKernelModules;

View file

@ -13,6 +13,9 @@ in
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users = {
persistentIds = {
firefly-iii = confLib.mkIds 983;
};
groups.${serviceGroup} = { };
users.${serviceUser} = {
group = lib.mkForce serviceGroup;

View file

@ -12,9 +12,14 @@ in
# networking.firewall.allowedTCPPorts = [ servicePort ];
users.users.${serviceUser} = {
group = serviceGroup;
isSystemUser = true;
users = {
persistentIds = {
forgejo = confLib.mkIds 985;
};
users.${serviceUser} = {
group = serviceGroup;
isSystemUser = true;
};
};
users.groups.${serviceGroup} = { };

View file

@ -9,10 +9,15 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.users.${serviceUser} = {
extraGroups = [ "users" ];
group = serviceGroup;
isSystemUser = true;
users = {
persistentIds = {
freshrss = confLib.mkIds 986;
};
users.${serviceUser} = {
extraGroups = [ "users" ];
group = serviceGroup;
isSystemUser = true;
};
};
users.groups.${serviceGroup} = { };

View file

@ -13,6 +13,10 @@ in
icon = "${self}/files/topology-images/${serviceName}.png";
};
users.persistentIds = {
homebox = confLib.mkIds 981;
};
globals = {
networks = {
${webProxyIf}.hosts = lib.mkIf isProxied {
@ -29,14 +33,25 @@ in
};
};
systemd.services.homebox = {
environment = {
TMPDIR = "/var/lib/homebox/.tmp";
};
serviceConfig = {
# ReadWritePaths = "/var/lib/homebox";
RuntimeDirectory = "homebox";
BindPaths = "/run/homebox:/var/lib/homebox/.tmp";
};
};
services.${serviceName} = {
enable = true;
package = pkgs.dev.homebox;
package = pkgs.bisect.homebox;
database.createLocally = true;
settings = {
HBOX_WEB_PORT = builtins.toString servicePort;
HBOX_OPTIONS_ALLOW_REGISTRATION = "false";
HBOX_STORAGE_CONN_STRING = "file:///Vault/data/homebox";
HBOX_STORAGE_CONN_STRING = "file:///var/lib/homebox";
HBOX_STORAGE_PREFIX_PATH = ".data";
};
};

103
modules/nixos/server/id.nix Normal file
View file

@ -0,0 +1,103 @@
{ lib, config, confLib, ... }:
let
inherit (lib)
concatLists
flip
mapAttrsToList
mkDefault
mkIf
mkOption
types
;
cfg = config.users.persistentIds;
in
{
options = {
swarselmodules.server.ids = lib.mkEnableOption "enable persistent ids on server";
users.persistentIds = mkOption {
default = { };
description = ''
Maps a user or group name to its expected uid/gid values. If a user/group is
used on the system without specifying a uid/gid, this module will assign the
corresponding ids defined here, or show an error if the definition is missing.
'';
type = types.attrsOf (
types.submodule {
options = {
uid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The uid to assign if it is missing in `users.users.<name>`.";
};
gid = mkOption {
type = types.nullOr types.int;
default = null;
description = "The gid to assign if it is missing in `users.groups.<name>`.";
};
};
}
);
};
users.users = mkOption {
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
config.uid =
let
persistentUid = cfg.${name}.uid or null;
in
mkIf (persistentUid != null) (mkDefault persistentUid);
}
)
);
};
users.groups = mkOption {
type = types.attrsOf (
types.submodule (
{ name, ... }:
{
config.gid =
let
persistentGid = cfg.${name}.gid or null;
in
mkIf (persistentGid != null) (mkDefault persistentGid);
}
)
);
};
};
config = lib.mkIf config.swarselmodules.server.ids {
assertions =
concatLists
(
flip mapAttrsToList config.users.users (
name: user: [
{
assertion = user.uid != null;
message = "non-persistent uid detected for '${name}', please assign one via `users.persistentIds`";
}
{
assertion = !user.autoSubUidGidRange;
message = "non-persistent subUids/subGids detected for: ${name}";
}
]
)
)
++ flip mapAttrsToList config.users.groups (
name: group: {
assertion = group.gid != null;
message = "non-persistent gid detected for '${name}', please assign one via `users.persistentIds`";
}
);
users.persistentIds = {
systemd-coredump = confLib.mkIds 998;
systemd-oom = confLib.mkIds 997;
polkituser = confLib.mkIds 973;
nscd = confLib.mkIds 972;
};
};
}

View file

@ -7,8 +7,14 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.users.${serviceUser} = {
extraGroups = [ "video" "render" "users" ];
users = {
persistentIds = {
immich = confLib.mkIds 989;
redis-immich = confLib.mkIds 977;
};
users.${serviceUser} = {
extraGroups = [ "video" "render" "users" ];
};
};
topology.self.services.${serviceName}.info = "https://${serviceDomain}";

View file

@ -7,10 +7,12 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.users.${serviceUser} = {
extraGroups = [ "video" "render" "users" ];
users = {
persistentIds.jellyfin = confLib.mkIds 994;
users.${serviceUser} = {
extraGroups = [ "video" "render" "users" ];
};
};
# nixpkgs.config.packageOverrides = pkgs: {
# intel-vaapi-driver = pkgs.intel-vaapi-driver.override { enableHybridCodec = true; };
# };

View file

@ -34,6 +34,9 @@ in
users = {
persistentIds = {
kanidm = confLib.mkIds 984;
};
users.${serviceUser} = {
group = serviceGroup;
isSystemUser = true;

View file

@ -12,11 +12,14 @@ in
calibre
];
users.users.${serviceUser} = {
extraGroups = [ "users" ];
users = {
persistentIds.kavita = confLib.mkIds 995;
users.${serviceUser} = {
extraGroups = [ "users" ];
};
};
sops.secrets.kavita-token = { inherit sopsFile; owner = serviceUser; };
# networking.firewall.allowedTCPPorts = [ servicePort ];

View file

@ -82,6 +82,8 @@ in
{ directory = serviceDir; mode = "0700"; }
];
users.persistentIds.kea = confLib.mkIds 968;
topology = {
extractors.kea.enable = false;
self.services.${serviceName} = {

View file

@ -314,6 +314,11 @@ in
# restart the bridges daily. this is done for the signal bridge mainly which stops carrying
# messages out after a while.
users.persistentIds = {
mautrix-signal = confLib.mkIds 993;
mautrix-whatsapp = confLib.mkIds 992;
mautrix-telegram = confLib.mkIds 991;
};
nodes =
let

View file

@ -42,6 +42,11 @@ in
};
users = {
persistentIds = {
nextcloud-exporter = confLib.mkIds 988;
node-exporter = confLib.mkIds 987;
grafana = confLib.mkIds 974;
};
users = {
nextcloud-exporter = {
extraGroups = [ "nextcloud" ];

View file

@ -16,6 +16,11 @@ in
kanidm-nextcloud-client = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; };
};
users.persistentIds = {
nextcloud = confLib.mkIds 990;
redis-nextcloud = confLib.mkIds 976;
};
globals.services.${serviceName} = {
domain = serviceDomain;
inherit proxyAddress4 proxyAddress6 isHome serviceAddress;

View file

@ -1,10 +1,15 @@
{ lib, config, pkgs, globals, ... }:
{ lib, config, pkgs, globals, confLib, ... }:
let
nfsUser = globals.user.name;
in
{
options.swarselmodules.server.nfs = lib.mkEnableOption "enable nfs on server";
config = lib.mkIf config.swarselmodules.server.nfs {
users.persistentIds = {
avahi = confLib.mkIds 978;
};
services = {
# add a user with sudo smbpasswd -a <user>
samba = {

View file

@ -11,6 +11,9 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.persistentIds = {
opksshuser = confLib.mkIds 980;
};
services.${serviceName} = {
enable = true;

View file

@ -12,8 +12,13 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.users.${serviceUser} = {
extraGroups = [ "users" ];
users = {
persistentIds = {
redis-paperless = confLib.mkIds 975;
};
users.${serviceUser} = {
extraGroups = [ "users" ];
};
};
sops.secrets = {

View file

@ -1,9 +1,11 @@
{ lib, config, ... }:
{ lib, config, confLib, ... }:
{
config = lib.mkIf (config?swarselmodules.server.mpd || config?swarselmodules.server.navidrome) {
config = lib.mkIf (config.swarselmodules.server.mpd || config.swarselmodules.server.navidrome) {
security.rtkit.enable = true; # this is required for pipewire real-time access
users.persistentIds.rtkit = confLib.mkIds 996;
services.pipewire = {
enable = true;
pulse.enable = true;

View file

@ -1,4 +1,4 @@
{ config, lib, ... }:
{ config, lib, confLib, ... }:
let
serviceName = "podman";
in
@ -6,6 +6,10 @@ in
options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselmodules.server.${serviceName} {
users.persistentIds = {
podman = confLib.mkIds 969;
};
virtualisation = {
podman.enable = true;
oci-containers.backend = "podman";

View file

@ -29,6 +29,10 @@ in
};
};
users.persistentIds = {
radicale = confLib.mkIds 982;
};
topology.self.services.${serviceName}.info = "https://${serviceDomain}";
globals = {

View file

@ -11,6 +11,10 @@ in
paths = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
withPostgres = lib.mkOption {
type = lib.types.bool;
default = false;
};
};
config = lib.mkIf config.swarselmodules.server.restic {

View file

@ -1,4 +1,4 @@
{ self, lib, config, withHomeManager, ... }:
{ self, lib, config, withHomeManager, confLib, ... }:
{
options.swarselmodules.server.ssh = lib.mkEnableOption "enable ssh on server";
config = lib.mkIf config.swarselmodules.server.ssh {
@ -21,17 +21,22 @@
}
];
};
users.users = {
"${config.swarselsystems.mainUser}".openssh.authorizedKeys.keyFiles = lib.mkIf withHomeManager [
(self + /secrets/public/ssh/yubikey.pub)
(self + /secrets/public/ssh/magicant.pub)
# (lib.mkIf config.swarselsystems.isBastionTarget (self + /secrets/public/ssh/jump.pub))
];
root.openssh.authorizedKeys.keyFiles = [
(self + /secrets/public/ssh/yubikey.pub)
(self + /secrets/public/ssh/magicant.pub)
# (lib.mkIf config.swarselsystems.isBastionTarget (self + /secrets/public/ssh/jump.pub))
];
users = {
persistentIds = {
sshd = lib.mkIf config.swarselmodules.server.ids (confLib.mkIds 979);
};
users = {
"${config.swarselsystems.mainUser}".openssh.authorizedKeys.keyFiles = lib.mkIf withHomeManager [
(self + /secrets/public/ssh/yubikey.pub)
(self + /secrets/public/ssh/magicant.pub)
# (lib.mkIf config.swarselsystems.isBastionTarget (self + /secrets/public/ssh/jump.pub))
];
root.openssh.authorizedKeys.keyFiles = [
(self + /secrets/public/ssh/yubikey.pub)
(self + /secrets/public/ssh/magicant.pub)
# (lib.mkIf config.swarselsystems.isBastionTarget (self + /secrets/public/ssh/jump.pub))
];
};
};
security.sudo.extraConfig = ''
Defaults env_keep+=SSH_AUTH_SOCK

View file

@ -25,6 +25,10 @@ in
# this user/group section is probably unneeded
users = {
persistentIds = {
prowlarr = confLib.mkIds 971;
readarr = confLib.mkIds 970;
};
groups = {
dockeruser = {
gid = 1155;