{ 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 { # ============================================================ # Lancement automatique lors de l'ouverture de session KDE # ============================================================ 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 ''; }