Téléverser les fichiers vers "modules"

This commit is contained in:
2026-08-19 11:40:56 +02:00
parent c7e98d14a4
commit 2ab3c19731
3 changed files with 36 additions and 77 deletions
+7 -40
View File
@@ -1,44 +1,11 @@
{ ... }:
{
# Module NixOS officiel : installe YubiKey Manager et les règles udev
# nécessaires pour que ykman puisse accéder à la clé en session utilisateur.
# Étape 1 LAB : la YubiKey est uniquement initialisée par le setup.
# Aucune authentification PAM/FIDO2 n'est activée à ce stade et aucun
# credential spécifique à une clé n'est conservé dans Git.
#
# YubiKey Manager apporte notamment les règles udev permettant l'accès
# utilisateur à la clé. L'enrôlement systemd-homed viendra à l'étape 3.
programs.yubikey-manager.enable = true;
# Déploiement du mapping utilisateur <-> YubiKey
environment.etc."u2f-mappings".source = ../u2f-mappings;
# Authentification PAM avec FIDO2 / YubiKey
security.pam.u2f = {
enable = true;
# Une authentification YubiKey réussie suffit.
# Le mot de passe reste disponible comme mécanisme de secours
# pour les comptes qui ne s'authentifient pas par FIDO2.
control = "sufficient";
settings = {
authfile = "/etc/u2f-mappings";
# Identifiant commun à tous nos futurs postes.
origin = "pam://nixos-workstations";
appid = "pam://nixos-workstations";
# Indique à l'utilisateur qu'il doit toucher la clé.
cue = true;
# Le PIN FIDO2 est maintenant obligatoire.
pinverification = 1;
# Pas de biométrie / vérification utilisateur intégrée.
# La vérification repose explicitement sur le PIN.
userverification = 0;
};
};
# Le changement de mot de passe via `passwd` doit rester une opération
# PAM/Unix classique. Le module U2F global ne doit pas intercepter
# l'authentification demandée par `passwd`, sinon le helper attend une
# interaction FIDO2 qu'il ne doit pas gérer à cette étape.
security.pam.services.passwd.u2f.enable = false;
}
}
-1
View File
@@ -35,7 +35,6 @@ in
outlookWeb
# FIDO2 / YubiKey
pam_u2f
libfido2
yubikey-manager
];
+29 -36
View File
@@ -136,15 +136,20 @@ let
executable = true;
text = ''
#!${pinPython}/bin/python3
"""Change a FIDO2 PIN without exposing secrets in argv.
"""Initialize the PIN of a virgin FIDO2 authenticator.
STEP 1 LAB contract:
- exactly one FIDO2 HID device must be connected;
- the authenticator must support CTAP2;
- no FIDO2 PIN must currently be configured;
- the new PIN and its confirmation arrive on stdin;
- secrets are never placed in argv or emitted in output.
Input on stdin, one UTF-8 line each:
1. current PIN
2. new PIN
3. confirmation
1. new PIN
2. confirmation
Output contains technical tokens only, never the PIN values.
Exactly one CTAP2 change_pin operation is attempted.
Output contains technical tokens only, never PIN values.
"""
import sys
@@ -167,22 +172,16 @@ let
return value.rstrip("\r\n")
current = read_secret()
new_pin = read_secret()
confirmation = read_secret()
if new_pin != confirmation:
fail("CONFIRM_MISMATCH", 31)
if len(current) < 4 or len(new_pin) < 4:
fail("PIN_POLICY", 32)
# CTAP2 PINs are UTF-8 strings with a maximum encoded size of 63 bytes.
if len(current.encode("utf-8")) > 63 or len(new_pin.encode("utf-8")) > 63:
if len(new_pin) < 4 or len(new_pin.encode("utf-8")) > 63:
fail("PIN_POLICY", 32)
devices = []
device = None
try:
devices = list(CtapHidDevice.list_devices())
@@ -201,57 +200,51 @@ let
ctap = Ctap2(device)
info = ctap.get_info()
# clientPin=True means a PIN is currently configured.
if not info.options.get("clientPin", False):
fail("PIN_NOT_CONFIGURED", 36)
# Étape 1 stricte : on ne doit jamais tenter de valider/changer
# un PIN existant. Ce contrôle ne consomme aucune tentative de PIN.
if info.options.get("clientPin", False):
fail("PIN_ALREADY_CONFIGURED", 36)
client_pin = ClientPin(ctap)
client_pin.set_pin(new_pin)
# IMPORTANT: exactly one PIN-changing command. Do not verify the old PIN
# first: that would add an unnecessary extra PIN operation.
client_pin.change_pin(current, new_pin)
# GET_INFO ne consomme pas de tentative de PIN. Vérifier que la clé
# annonce désormais bien clientPin=True avant de déclarer le succès.
if not ctap.get_info().options.get("clientPin", False):
fail("PIN_STATE_NOT_UPDATED", 37)
# Forget Python references as soon as the operation is complete.
current = ""
new_pin = ""
confirmation = ""
print("OK", flush=True)
raise SystemExit(0)
except CtapError as exc:
code = exc.code
if code == CtapError.ERR.PIN_INVALID:
fail("WRONG_PIN", 40)
if code == CtapError.ERR.PIN_AUTH_BLOCKED:
fail("PIN_AUTH_BLOCKED", 41)
if code == CtapError.ERR.PIN_BLOCKED:
fail("PIN_BLOCKED", 42)
if code == CtapError.ERR.PIN_NOT_SET:
fail("PIN_NOT_CONFIGURED", 43)
if code == CtapError.ERR.PIN_POLICY_VIOLATION:
fail("PIN_POLICY", 44)
if code == CtapError.ERR.PIN_NOT_SET:
fail("PIN_STATE_ERROR", 45)
fail("PIN_FAILED", 45)
fail("PIN_FAILED", 46)
except (PermissionError, OSError):
fail("NO_YUBIKEY", 46)
fail("NO_YUBIKEY", 47)
except ValueError:
# python-fido2 may reject an invalid PIN before sending CTAP2.
fail("PIN_POLICY", 47)
fail("PIN_POLICY", 48)
except SystemExit:
raise
except Exception:
# Do not expose exception text: it could contain device/environment
# details and is not needed by the UI. The setup log stores the token.
fail("PIN_HELPER_ERROR", 48)
fail("PIN_HELPER_ERROR", 49)
finally:
current = ""
new_pin = ""
confirmation = ""
for dev in devices:
@@ -264,7 +257,7 @@ let
workstationSetup = pkgs.stdenv.mkDerivation {
pname = "nixos-workstations-setup";
version = "1.5.0";
version = "1.6.0";
src = ../workstation-setup;
@@ -301,7 +294,7 @@ let
state_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/nixos-workstations"
password_marker="$state_dir/password-initialized"
pin_marker="$state_dir/yubikey-pin-initialized"
pin_marker="$state_dir/yubikey-pin-created"
# Une session déjà finalisée n'affiche plus l'assistant.
if [ -e "$password_marker" ] && [ -e "$pin_marker" ]; then