From 64a09838567c5baefa93b246b973dea51915f51b Mon Sep 17 00:00:00 2001 From: Olivier <1+olivier@noreply.raspot.in> Date: Tue, 18 Aug 2026 14:44:18 +0200 Subject: [PATCH] Actualiser modules/users.nix --- modules/users.nix | 591 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 562 insertions(+), 29 deletions(-) diff --git a/modules/users.nix b/modules/users.nix index cf44b37..a5061fd 100644 --- a/modules/users.nix +++ b/modules/users.nix @@ -1,34 +1,567 @@ { pkgs, ... }: +let + # ============================================================ + # Assistant interactif de première connexion + # + # Étape 1 : changement du mot de passe local + # Étape 2 : changement du PIN YubiKey + # ============================================================ + + firstLoginSetup = pkgs.writeShellScript "first-login-setup" '' + set -u + + state_dir="$HOME/.local/state/nixos-workstations" + + password_marker="$state_dir/password-initialized" + yubikey_marker="$state_dir/yubikey-pin-initialized" + + runtime_dir="''${XDG_RUNTIME_DIR:-/tmp}" + active_file="$runtime_dir/nixos-workstations-first-login.active" + + # ---------------------------------------------------------- + # Permet au superviseur de savoir que l'assistant tourne. + # ---------------------------------------------------------- + + printf '%s\n' "$$" > "$active_file" + + cleanup() { + ${pkgs.coreutils}/bin/rm -f "$active_file" + } + + trap cleanup EXIT HUP INT TERM + + # ---------------------------------------------------------- + # Couleurs + # ---------------------------------------------------------- + + RESET="\033[0m" + BOLD="\033[1m" + + BLUE="\033[38;5;39m" + CYAN="\033[38;5;45m" + GREEN="\033[38;5;82m" + YELLOW="\033[38;5;220m" + RED="\033[38;5;196m" + GREY="\033[38;5;245m" + WHITE="\033[38;5;255m" + + separator() { + printf "''${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━''${RESET}\n" + } + + clear_screen() { + printf "\033[2J\033[H" + } + + header() { + clear_screen + + echo + separator + echo + + printf " ''${BOLD}''${CYAN}🔐 CONFIGURATION DE VOTRE COMPTE''${RESET}\n" + + echo + separator + echo + } + + # ========================================================== + # ÉTAPE 1 : MOT DE PASSE LOCAL + # ========================================================== + + if [ ! -e "$password_marker" ]; then + + header + + printf " ''${BOLD}''${CYAN}ÉTAPE 1 SUR 2''${RESET}\n" + echo + + printf " ''${BOLD}''${WHITE}Personnalisation du mot de passe de secours''${RESET}\n" + echo + + printf " Votre compte possède actuellement un mot de passe temporaire.\n" + printf " Vous devez maintenant le remplacer par votre mot de passe\n" + printf " personnel.\n" + + echo + separator + echo + + printf " ''${CYAN}①''${RESET} Saisissez votre mot de passe temporaire actuel.\n" + echo + + printf " ''${CYAN}②''${RESET} Choisissez votre nouveau mot de passe personnel.\n" + echo + + printf " ''${CYAN}③''${RESET} Confirmez votre nouveau mot de passe.\n" + + echo + separator + echo + + printf " ''${GREY}Ce mot de passe reste local au poste.''${RESET}\n" + printf " ''${GREY}Il pourra être utilisé comme solution de secours si votre''${RESET}\n" + printf " ''${GREY}YubiKey est indisponible.''${RESET}\n" + + echo + separator + echo + + printf " ''${BOLD}''${YELLOW}Veuillez maintenant suivre les instructions ci-dessous.''${RESET}\n" + echo + + # -------------------------------------------------------- + # IMPORTANT NixOS + # + # On utilise volontairement le wrapper SUID NixOS. + # + # NE PAS remplacer par : + # ${pkgs.shadow}/bin/passwd + # -------------------------------------------------------- + + if /run/wrappers/bin/passwd; then + + # Le marqueur est créé immédiatement après le succès + # confirmé par passwd. + ${pkgs.coreutils}/bin/mkdir -p "$state_dir" + ${pkgs.coreutils}/bin/touch "$password_marker" + + echo + separator + echo + + printf " ''${BOLD}''${GREEN}✓ MOT DE PASSE MODIFIÉ AVEC SUCCÈS''${RESET}\n" + + echo + printf " Votre nouveau mot de passe personnel est maintenant actif.\n" + + echo + separator + echo + + ${pkgs.coreutils}/bin/sleep 3 + + else + + echo + separator + echo + + printf " ''${BOLD}''${RED}✗ LE MOT DE PASSE N'A PAS ÉTÉ MODIFIÉ''${RESET}\n" + + echo + printf " L'opération a été annulée ou a échoué.\n" + printf " Cette étape sera automatiquement reproposée.\n" + + echo + separator + echo + + ${pkgs.coreutils}/bin/sleep 3 + + exit 1 + fi + + fi + + # ========================================================== + # ÉTAPE 2 : PIN YUBIKEY + # ========================================================== + + if [ ! -e "$yubikey_marker" ]; then + + header + + printf " ''${BOLD}''${CYAN}ÉTAPE 2 SUR 2''${RESET}\n" + echo + + printf " ''${BOLD}''${WHITE}Personnalisation du PIN de votre YubiKey''${RESET}\n" + echo + + printf " Votre YubiKey possède actuellement le PIN temporaire\n" + printf " fourni lors de la préparation du poste.\n" + + echo + separator + echo + + printf " ''${CYAN}①''${RESET} Saisissez votre PIN temporaire actuel.\n" + echo + + printf " ''${CYAN}②''${RESET} Choisissez votre nouveau PIN personnel.\n" + echo + + printf " ''${CYAN}③''${RESET} Confirmez votre nouveau PIN.\n" + + echo + separator + echo + + printf " ''${GREY}Votre PIN est enregistré directement dans la YubiKey.''${RESET}\n" + printf " ''${GREY}Il n'est ni enregistré sur ce poste, ni dans Git.''${RESET}\n" + printf " ''${GREY}Le service informatique ne connaît pas votre nouveau PIN.''${RESET}\n" + + echo + separator + echo + + printf " ''${BOLD}''${YELLOW}⚠ ATTENTION AU PAVÉ NUMÉRIQUE''${RESET}\n" + echo + + printf " Vérifiez que ''${BOLD}Verr Num''${RESET} est activé avant de saisir votre PIN.\n" + printf " En cas de doute, utilisez les chiffres situés au-dessus\n" + printf " des lettres du clavier.\n" + + echo + separator + echo + + printf " ''${BOLD}''${YELLOW}Veuillez maintenant suivre les instructions ci-dessous.''${RESET}\n" + echo + + if ${pkgs.yubikey-manager}/bin/ykman fido access change-pin; then + + # Le marqueur est créé immédiatement après que ykman + # confirme réellement le changement du PIN. + ${pkgs.coreutils}/bin/mkdir -p "$state_dir" + ${pkgs.coreutils}/bin/touch "$yubikey_marker" + + echo + separator + echo + + printf " ''${BOLD}''${GREEN}✓ PIN YUBIKEY MODIFIÉ AVEC SUCCÈS''${RESET}\n" + + echo + printf " Votre nouveau PIN personnel est maintenant actif.\n" + + echo + separator + echo + + ${pkgs.coreutils}/bin/sleep 3 + + else + + echo + separator + echo + + printf " ''${BOLD}''${RED}✗ LE PIN N'A PAS ÉTÉ MODIFIÉ''${RESET}\n" + + echo + printf " L'opération a été annulée ou a échoué.\n" + echo + + if [ -e "$password_marker" ]; then + printf " ''${GREEN}✓ Votre nouveau mot de passe est déjà enregistré.''${RESET}\n" + printf " Il ne vous sera pas demandé de le modifier à nouveau.\n" + echo + fi + + printf " Seule la configuration du PIN sera reproposée.\n" + + echo + separator + echo + + ${pkgs.coreutils}/bin/sleep 3 + + exit 1 + fi + + fi + + # ========================================================== + # CONFIGURATION TERMINÉE + # ========================================================== + + header + + printf " ''${BOLD}''${GREEN}✓ VOTRE POSTE EST PRÊT''${RESET}\n" + + echo + separator + echo + + printf " La configuration initiale de votre compte est terminée.\n" + + echo + printf " ''${GREEN}✓''${RESET} Mot de passe personnel configuré\n" + printf " ''${GREEN}✓''${RESET} PIN personnel YubiKey configuré\n" + + echo + separator + echo + + printf " ''${BOLD}Pour vos prochaines connexions :''${RESET}\n" + + echo + printf " ''${BOLD}YubiKey + PIN personnel''${RESET}\n" + + echo + printf " Votre mot de passe local reste disponible comme solution\n" + printf " de secours si votre YubiKey est indisponible.\n" + + echo + separator + echo + + printf " Fermeture automatique" + + for i in 1 2 3 4 5; do + printf "." + ${pkgs.coreutils}/bin/sleep 1 + done + + echo + + exit 0 + ''; + + # ============================================================ + # Superviseur + # + # Son rôle : + # - lancer l'assistant ; + # - le relancer s'il est fermé avant la fin ; + # - NE JAMAIS tuer KDE ou la session. + # ============================================================ + + firstLoginSupervisor = + pkgs.writeShellScript "first-login-supervisor" '' + set -u + + current_user="$(${pkgs.coreutils}/bin/id -un)" + + # Pour le moment, uniquement Alice. + if [ "$current_user" != "alice" ]; then + exit 0 + fi + + state_dir="$HOME/.local/state/nixos-workstations" + + password_marker="$state_dir/password-initialized" + yubikey_marker="$state_dir/yubikey-pin-initialized" + + runtime_dir="''${XDG_RUNTIME_DIR:-/tmp}" + + active_file="$runtime_dir/nixos-workstations-first-login.active" + supervisor_lock="$runtime_dir/nixos-workstations-first-login.lock" + + KDIALOG="${pkgs.kdePackages.kdialog}/bin/kdialog" + KONSOLE="${pkgs.kdePackages.konsole}/bin/konsole" + + # -------------------------------------------------------- + # Empêche deux superviseurs de fonctionner simultanément. + # -------------------------------------------------------- + + exec 9>"$supervisor_lock" + + if ! ${pkgs.util-linux}/bin/flock -n 9; then + exit 0 + fi + + # -------------------------------------------------------- + # Tout est déjà configuré. + # -------------------------------------------------------- + + if [ -e "$password_marker" ] && [ -e "$yubikey_marker" ]; then + exit 0 + fi + + # Laisse Plasma finir son démarrage. + ${pkgs.coreutils}/bin/sleep 4 + + # ======================================================== + # Boucle de provisioning + # ======================================================== + + while \ + [ ! -e "$password_marker" ] || \ + [ ! -e "$yubikey_marker" ] + do + + # ------------------------------------------------------ + # Présentation KDE + # ------------------------------------------------------ + + "$KDIALOG" \ + --title "Finalisation de votre poste" \ + --warningcontinuecancel \ + "Bienvenue Alice. + +Votre poste est presque prêt. + +Pour terminer sa configuration, vous devez personnaliser vos deux moyens d'authentification : + + 🔑 Votre mot de passe local de secours + + 🔐 Le PIN de votre YubiKey + +Les nouveaux secrets que vous choisirez sont personnels. + +Ils ne sont ni enregistrés dans Git, ni communiqués au service informatique. + +Cette opération ne doit être effectuée qu'une seule fois." + + dialog_result=$? + + # ------------------------------------------------------ + # Alice ferme la fenêtre d'accueil. + # + # On ne touche PAS à KDE. + # L'assistant sera simplement reproposé. + # ------------------------------------------------------ + + if [ "$dialog_result" -ne 0 ]; then + + "$KDIALOG" \ + --title "Configuration requise" \ + --sorry \ + "La configuration initiale de votre compte n'est pas terminée. + +Cette étape est obligatoire. + +L'assistant va être reproposé." + + ${pkgs.coreutils}/bin/sleep 1 + + continue + fi + + # ------------------------------------------------------ + # Supprime un éventuel fichier actif devenu obsolète. + # ------------------------------------------------------ + + if [ -s "$active_file" ]; then + + assistant_pid="$( + ${pkgs.coreutils}/bin/cat "$active_file" 2>/dev/null || true + )" + + if [ -n "$assistant_pid" ]; then + + if ! ${pkgs.coreutils}/bin/kill -0 "$assistant_pid" 2>/dev/null; then + ${pkgs.coreutils}/bin/rm -f "$active_file" + fi + + else + ${pkgs.coreutils}/bin/rm -f "$active_file" + fi + + fi + + # ------------------------------------------------------ + # Lance l'assistant. + # ------------------------------------------------------ + + "$KONSOLE" \ + --separate \ + --fullscreen \ + --hide-menubar \ + --hide-tabbar \ + -e ${firstLoginSetup} \ + >/dev/null 2>&1 & + + # Laisse au script le temps d'écrire active_file. + ${pkgs.coreutils}/bin/sleep 2 + + # ====================================================== + # Attend la fin réelle de l'assistant + # ====================================================== + + while true; do + + # Les deux opérations sont terminées. + if [ -e "$password_marker" ] && [ -e "$yubikey_marker" ]; then + break + fi + + # Le script n'est plus déclaré actif. + if [ ! -s "$active_file" ]; then + break + fi + + assistant_pid="$( + ${pkgs.coreutils}/bin/cat "$active_file" 2>/dev/null || true + )" + + if [ -z "$assistant_pid" ]; then + break + fi + + # Le script a disparu. + if ! ${pkgs.coreutils}/bin/kill -0 "$assistant_pid" 2>/dev/null; then + + ${pkgs.coreutils}/bin/rm -f "$active_file" + + break + fi + + ${pkgs.coreutils}/bin/sleep 1 + + done + + # ====================================================== + # Tout est terminé + # ====================================================== + + if [ -e "$password_marker" ] && [ -e "$yubikey_marker" ]; then + + "$KDIALOG" \ + --title "Poste configuré" \ + --msgbox \ + "✓ Votre poste est prêt. + +Votre mot de passe personnel et le PIN de votre YubiKey ont été configurés. + +Pour vos prochaines connexions, utilisez : + + YubiKey + votre PIN personnel + +Votre mot de passe local reste disponible comme solution de secours." + + exit 0 + fi + + # ====================================================== + # Configuration partielle / fenêtre fermée / erreur + # ====================================================== + + "$KDIALOG" \ + --title "Configuration incomplète" \ + --sorry \ + "La configuration initiale n'est pas entièrement terminée. + +Les étapes déjà réalisées sont conservées. + +L'assistant va reprendre uniquement les étapes restantes." + + ${pkgs.coreutils}/bin/sleep 1 + + done + + exit 0 + ''; + +in { - # Les mots de passe peuvent être modifiés localement. - # Un nixos-rebuild ne réécrase pas le mot de passe choisi par l'utilisateur. - users.mutableUsers = true; + # ============================================================ + # Lancement automatique lors de l'ouverture de session KDE + # ============================================================ - # Compte d'administration local - users.users.localadm = { - isNormalUser = true; - description = "Administrateur local"; - extraGroups = [ - "networkmanager" - "wheel" - ]; - - packages = with pkgs; [ - kdePackages.kate - ]; - }; - - # Utilisateur standard - users.users.alice = { - isNormalUser = true; - description = "Alice"; - - # Alice n'est pas administratrice. - extraGroups = [ ]; - - # Utilisé uniquement lors de la création initiale du compte. - # Demo pass : LaboTest@1980 - initialHashedPassword = "$y$j9T$XiIgBUxXjqAYbQQs3eBtr.$dT01AYyRiBtjoaF1MIG83cx2h0bRxEILE6tpilE2V9D"; - }; + environment.etc."xdg/autostart/yubikey-first-login.desktop".text = '' + [Desktop Entry] + Type=Application + Name=Finalisation du poste + Comment=Configuration initiale du compte utilisateur + Exec=${firstLoginSupervisor} + OnlyShowIn=KDE; + NoDisplay=true + StartupNotify=false + ''; } \ No newline at end of file