Téléverser les fichiers vers "modules"
This commit is contained in:
+7
-40
@@ -1,44 +1,11 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# Module NixOS officiel : installe YubiKey Manager et les règles udev
|
# Étape 1 LAB : la YubiKey est uniquement initialisée par le setup.
|
||||||
# nécessaires pour que ykman puisse accéder à la clé en session utilisateur.
|
# 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;
|
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ in
|
|||||||
outlookWeb
|
outlookWeb
|
||||||
|
|
||||||
# FIDO2 / YubiKey
|
# FIDO2 / YubiKey
|
||||||
pam_u2f
|
|
||||||
libfido2
|
libfido2
|
||||||
yubikey-manager
|
yubikey-manager
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -136,15 +136,20 @@ let
|
|||||||
executable = true;
|
executable = true;
|
||||||
text = ''
|
text = ''
|
||||||
#!${pinPython}/bin/python3
|
#!${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:
|
Input on stdin, one UTF-8 line each:
|
||||||
1. current PIN
|
1. new PIN
|
||||||
2. new PIN
|
2. confirmation
|
||||||
3. confirmation
|
|
||||||
|
|
||||||
Output contains technical tokens only, never the PIN values.
|
Output contains technical tokens only, never PIN values.
|
||||||
Exactly one CTAP2 change_pin operation is attempted.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -167,22 +172,16 @@ let
|
|||||||
return value.rstrip("\r\n")
|
return value.rstrip("\r\n")
|
||||||
|
|
||||||
|
|
||||||
current = read_secret()
|
|
||||||
new_pin = read_secret()
|
new_pin = read_secret()
|
||||||
confirmation = read_secret()
|
confirmation = read_secret()
|
||||||
|
|
||||||
if new_pin != confirmation:
|
if new_pin != confirmation:
|
||||||
fail("CONFIRM_MISMATCH", 31)
|
fail("CONFIRM_MISMATCH", 31)
|
||||||
|
|
||||||
if len(current) < 4 or len(new_pin) < 4:
|
if len(new_pin) < 4 or len(new_pin.encode("utf-8")) > 63:
|
||||||
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:
|
|
||||||
fail("PIN_POLICY", 32)
|
fail("PIN_POLICY", 32)
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
device = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
devices = list(CtapHidDevice.list_devices())
|
devices = list(CtapHidDevice.list_devices())
|
||||||
@@ -201,57 +200,51 @@ let
|
|||||||
ctap = Ctap2(device)
|
ctap = Ctap2(device)
|
||||||
info = ctap.get_info()
|
info = ctap.get_info()
|
||||||
|
|
||||||
# clientPin=True means a PIN is currently configured.
|
# Étape 1 stricte : on ne doit jamais tenter de valider/changer
|
||||||
if not info.options.get("clientPin", False):
|
# un PIN existant. Ce contrôle ne consomme aucune tentative de PIN.
|
||||||
fail("PIN_NOT_CONFIGURED", 36)
|
if info.options.get("clientPin", False):
|
||||||
|
fail("PIN_ALREADY_CONFIGURED", 36)
|
||||||
|
|
||||||
client_pin = ClientPin(ctap)
|
client_pin = ClientPin(ctap)
|
||||||
|
client_pin.set_pin(new_pin)
|
||||||
|
|
||||||
# IMPORTANT: exactly one PIN-changing command. Do not verify the old PIN
|
# GET_INFO ne consomme pas de tentative de PIN. Vérifier que la clé
|
||||||
# first: that would add an unnecessary extra PIN operation.
|
# annonce désormais bien clientPin=True avant de déclarer le succès.
|
||||||
client_pin.change_pin(current, new_pin)
|
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 = ""
|
new_pin = ""
|
||||||
confirmation = ""
|
confirmation = ""
|
||||||
|
|
||||||
print("OK", flush=True)
|
print("OK", flush=True)
|
||||||
raise SystemExit(0)
|
raise SystemExit(0)
|
||||||
|
|
||||||
except CtapError as exc:
|
except CtapError as exc:
|
||||||
code = exc.code
|
code = exc.code
|
||||||
|
|
||||||
if code == CtapError.ERR.PIN_INVALID:
|
|
||||||
fail("WRONG_PIN", 40)
|
|
||||||
if code == CtapError.ERR.PIN_AUTH_BLOCKED:
|
if code == CtapError.ERR.PIN_AUTH_BLOCKED:
|
||||||
fail("PIN_AUTH_BLOCKED", 41)
|
fail("PIN_AUTH_BLOCKED", 41)
|
||||||
if code == CtapError.ERR.PIN_BLOCKED:
|
if code == CtapError.ERR.PIN_BLOCKED:
|
||||||
fail("PIN_BLOCKED", 42)
|
fail("PIN_BLOCKED", 42)
|
||||||
if code == CtapError.ERR.PIN_NOT_SET:
|
|
||||||
fail("PIN_NOT_CONFIGURED", 43)
|
|
||||||
if code == CtapError.ERR.PIN_POLICY_VIOLATION:
|
if code == CtapError.ERR.PIN_POLICY_VIOLATION:
|
||||||
fail("PIN_POLICY", 44)
|
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):
|
except (PermissionError, OSError):
|
||||||
fail("NO_YUBIKEY", 46)
|
fail("NO_YUBIKEY", 47)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# python-fido2 may reject an invalid PIN before sending CTAP2.
|
fail("PIN_POLICY", 48)
|
||||||
fail("PIN_POLICY", 47)
|
|
||||||
|
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Do not expose exception text: it could contain device/environment
|
fail("PIN_HELPER_ERROR", 49)
|
||||||
# details and is not needed by the UI. The setup log stores the token.
|
|
||||||
fail("PIN_HELPER_ERROR", 48)
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
current = ""
|
|
||||||
new_pin = ""
|
new_pin = ""
|
||||||
confirmation = ""
|
confirmation = ""
|
||||||
for dev in devices:
|
for dev in devices:
|
||||||
@@ -264,7 +257,7 @@ let
|
|||||||
|
|
||||||
workstationSetup = pkgs.stdenv.mkDerivation {
|
workstationSetup = pkgs.stdenv.mkDerivation {
|
||||||
pname = "nixos-workstations-setup";
|
pname = "nixos-workstations-setup";
|
||||||
version = "1.5.0";
|
version = "1.6.0";
|
||||||
|
|
||||||
src = ../workstation-setup;
|
src = ../workstation-setup;
|
||||||
|
|
||||||
@@ -301,7 +294,7 @@ let
|
|||||||
|
|
||||||
state_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/nixos-workstations"
|
state_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/nixos-workstations"
|
||||||
password_marker="$state_dir/password-initialized"
|
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.
|
# Une session déjà finalisée n'affiche plus l'assistant.
|
||||||
if [ -e "$password_marker" ] && [ -e "$pin_marker" ]; then
|
if [ -e "$password_marker" ] && [ -e "$pin_marker" ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user