{ config, lib, pkgs, ... }: let cfg = config.nixosWorkstations.homedUser; # LAB : le mot de passe initial est volontairement un mot de passe par défaut # connu. Il ne constitue pas un secret durable : Alice doit le remplacer lors # de sa première session. Pour une future version production, ce credential # devra venir d'une source chiffrée/externe et non du Nix store. initialPasswordCredential = pkgs.writeText "nixos-workstations-${cfg.user}-initial-password" cfg.initialPassword; provisionService = "nixos-workstations-provision-${cfg.user}"; in { options.nixosWorkstations.homedUser = { enable = lib.mkEnableOption "précréation d'un utilisateur systemd-homed chiffré"; user = lib.mkOption { type = lib.types.str; default = "alice"; description = "Compte systemd-homed à préparer automatiquement."; }; realName = lib.mkOption { type = lib.types.str; default = "Alice"; description = "Nom complet de l'utilisateur systemd-homed."; }; initialPassword = lib.mkOption { type = lib.types.str; default = "LaboTest@1980"; description = "Mot de passe temporaire utilisé uniquement pour le premier accès LAB."; }; diskSize = lib.mkOption { type = lib.types.str; default = "10G"; description = "Taille initiale du conteneur LUKS2 du home."; }; }; config = lib.mkIf cfg.enable { services.homed.enable = true; # Les comptes nixbld NixOS ont des UID > 1000. Nous n'utilisons pas le # workflow first-boot de homed : les comptes sont créés explicitement par # le service ci-dessous. services.userdbd.silenceHighSystemUsers = true; # SDDM doit accepter la plage d'UID systemd-homed et son greeter doit # charger libnss_systemd directement. Cette configuration a été validée en # LAB : sans ce LD_LIBRARY_PATH, getpwent() n'énumère pas Alice sous NixOS. services.displayManager.sddm.settings = { Users = { MinimumUid = 1000; MaximumUid = 60513; }; General = { GreeterEnvironment = "QT_WAYLAND_SHELL_INTEGRATION=layer-shell,LD_LIBRARY_PATH=${config.system.nssModules.path}"; }; }; # Précréation automatique d'Alice. localadm ne lance qu'un # nixos-rebuild switch : ce service est démarré automatiquement et ne fait # rien si le compte homed existe déjà. # # RemainAfterExit est volontairement conservé : SDDM dépend de la réussite # de ce service au démarrage. Le script lui-même reste idempotent. systemd.services.${provisionService} = { description = "Prépare le compte systemd-homed ${cfg.user}"; wantedBy = [ "multi-user.target" ]; before = [ "display-manager.service" ]; after = [ "systemd-homed.service" ]; requires = [ "systemd-homed.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; LoadCredential = [ "home.new-password:${initialPasswordCredential}" ]; }; path = [ pkgs.coreutils pkgs.getent pkgs.systemd ]; script = '' set -eu user=${lib.escapeShellArg cfg.user} if ${pkgs.systemd}/bin/homectl inspect "$user" >/dev/null 2>&1; then echo "Compte homed $user déjà présent : aucune modification." exit 0 fi # Refuser une collision avec un compte UNIX classique du même nom. if ${pkgs.getent}/bin/getent passwd "$user" >/dev/null 2>&1; then echo "ERREUR : $user existe déjà mais n'est pas un compte systemd-homed." >&2 exit 1 fi echo "Création du compte systemd-homed $user et de son home LUKS2..." ${pkgs.systemd}/bin/homectl --no-ask-password create "$user" \ --real-name=${lib.escapeShellArg cfg.realName} \ --storage=luks \ --fs-type=ext4 \ --disk-size=${lib.escapeShellArg cfg.diskSize} \ --access-mode=0700 \ --shell=/run/current-system/sw/bin/bash \ --password-change-now=no \ --recovery-key=no ''; }; # Au boot, le compte doit exister avant le lancement du greeter SDDM. systemd.services.display-manager = { after = [ "${provisionService}.service" ]; requires = [ "${provisionService}.service" ]; }; }; }