Aller au contenu

Authentification forte (YubiKey)

Chaque membre du réseau peut associer à son compte une clé YubiKey principale et une clé de secours, en plus de son mot de passe (qui reste obligatoire et sert de repli automatique).

Le principe clé : un seul enrôlement par clé, sur le poste d’administration. Tout le reste se propage déclarativement avec just apply — la clé n’est jamais branchée sur les hôtes du parc, seulement au moment de s’en servir.

UsageMécanismeEffet
Session, sudo, écran de connexionpam_u2f (sufficient)Toucher la clé remplace le mot de passe
Disques chiffrés (LUKS)FIDO2 hmac-secretDéverrouillage au boot en touchant la clé
SSO (Kanidm)Passkey WebAuthnConnexion sans mot de passe aux services
VaultwardenWebAuthn (2FA)Second facteur du coffre-fort

Un compte Kanidm expose plusieurs moyens d’authentification, indépendants les uns des autres. L’objectif est qu’aucune perte de clé ne verrouille un compte.

MoyenNature KanidmSupport
YubiKey principalePasskey WebAuthnClé physique (PIN + toucher)
YubiKey de secoursPasskey WebAuthnClé physique rangée à part
Clé logiciellePasskey WebAuthnCoffre-fort (Bitwarden, KeePassXC)
Mot de passe + TOTPCredential primaireFilet de sécurité, jamais supprimé

Les trois premiers sont des passkeys : Kanidm les stocke côte à côte et les propose ensemble au navigateur. Le quatrième vit ailleurs (le credential primaire) et ne peut pas être écrasé par une cérémonie WebAuthn.

  1. Brancher la YubiKey de l’utilisateur sur le poste admin, puis :

    Fenêtre de terminal
    just yubikey <login> # clé principale (PIN FIDO2 + ~3 touches)
    just yubikey <login> backup # clé de secours (optionnel)

    Si la clé n’a pas encore de PIN FIDO2, la recette le crée d’abord (indispensable pour Kanidm, voir plus bas).

  2. Committer et déployer :

    Fenêtre de terminal
    git add usr/secrets/yubikeys.json
    just commit "user(yubikey): <login>"
    just apply
  3. C’est tout : session et sudo répondent à la clé sur tous les postes de l’utilisateur, et les hôtes chiffrés inscrivent la clé dans leurs en-têtes LUKS eux-mêmes, au déploiement.

Le principe : isoler chaque cérémonie de tout ce qui pourrait l’intercepter. Une passkey physique s’enrôle dans un navigateur sans extension de coffre-fort, une passkey logicielle s’enrôle dans le navigateur habituel. L’ordre garantit qu’un filet de sécurité existe avant chaque étape risquée.

  1. Mot de passe et TOTP d’abord. Ils vivent hors des passkeys et ne peuvent pas être écrasés. Tant qu’ils existent, aucune erreur des étapes suivantes n’enferme l’utilisateur dehors.

  2. Clé logicielle ensuite, dans le navigateur habituel, extension du coffre-fort déverrouillée. Au moment où le coffre propose d’enregistrer, choisir créer un nouvel élément, jamais « enregistrer dans un élément existant ».

  3. YubiKey principale, dans un navigateur jetable et vierge : sans extension, l’interception est impossible.

    Fenêtre de terminal
    firefox --new-instance --profile "$(mktemp -d)" https://idm.domain.tld

    Puis coller le lien de réinitialisation Kanidm imprimé par just yubikey.

  4. YubiKey de secours, dans le même navigateur jetable, autre clé branchée.

  5. Vérifier le résultat avant de fermer : le compte doit présenter trois passkeys. Voir Diagnostic des passkeys.

Pour lister réellement les passkeys d’un compte, ouvrir une session interactive de mise à jour des credentials :

Fenêtre de terminal
kanidm person credential update <login> --name idm_admin

Pour voir ce que le serveur envoie réellement au navigateur, ouvrir la console (F12) sur la page de connexion, coller ce code, puis cliquer sur Passkey :

const _get = navigator.credentials.get.bind(navigator.credentials);
navigator.credentials.get = (o) => {
const b64 = (v) => btoa(String.fromCharCode(...new Uint8Array(v)));
const p = o.publicKey;
console.log("rpId:", p.rpId, "| uv:", p.userVerification);
console.log("allowCredentials:", (p.allowCredentials || []).map((c) => b64(c.id)));
return _get(o);
};

Le nombre d’entrées de allowCredentials est la vérité : c’est la liste exacte des passkeys que Kanidm connaît pour ce compte.

SymptômeCause probable
Le navigateur réclame le PIN d’une YubiKey, aucune fenêtre de coffre-fortPasskey logicielle orpheline, absente du coffre
allowCredentials compte moins d’entrées que prévuUne cérémonie a écrasé une passkey
Le PIN est réclamé alors que la clé physique n’est pas viséeKanidm n’émet pas de transports, voir ci-dessous

Une même clé physique peut servir à plusieurs utilisateurs : répéter simplement l’enrôlement pour chaque login, clé branchée.

Fenêtre de terminal
just yubikey <login1>
just yubikey <login2> # même clé physique, autre compte

Chaque enrôlement crée des credentials propres au compte (PAM, LUKS, et un lien de réinitialisation Kanidm par utilisateur pour refaire la cérémonie web avec la même clé). Les passkeys Kanidm sont non résidentes : elles n’occupent aucun emplacement sur la clé, le nombre de comptes est illimité. Une révocation (just yubikey <login> main revoke) ne touche que le login visé.

FichierContenuStatut
usr/secrets/yubikeys.jsonCredentials publics (PAM, id FIDO2, sel)Public, versionné
secrets.yamlyubikey/<login>/<clé>/luks-secretSecret LUKS dérivé de la cléChiffré sops
secrets.yamlluks-passphrasePassphrase saisie à l’installation diskoChiffré sops (demandée au 1ᵉʳ enrôlement)
  • PAM : l’origine est fixe (pam://<domaine>), donc un credential vaut sur tout le parc. Le module darkone.system.yubikey (activé automatiquement dès que le registre existe) distribue /etc/u2f_mappings sur chaque hôte.
  • LUKS : le secret dérivé (HMAC(clé, sel)) n’est calculable qu’avec la clé physique. Chaque hôte chiffré l’inscrit dans son en-tête LUKS (keyslot + token systemd-fido2, le format exact de systemd-cryptenroll) via un service idempotent, et retire ceux qui ne sont plus déclarés.
Fenêtre de terminal
just yubikey <login> main revoke # ou backup
just commit "user(yubikey): revoke <login>" && just apply

Au déploiement, le credential PAM disparaît des hôtes et les keyslots LUKS correspondants sont supprimés. Penser aussi à retirer la passkey côté Kanidm (interface web ou jeton de réinitialisation) et Vaultwarden.

  • Le TOTP reste disponible côté web (Kanidm, Vaultwarden), pas au login local.
  • Déverrouillage LUKS sans code PIN (touche seule) dans cette version.
  • À venir : clés SSH FIDO2 (ed25519-sk) et signature git, mode « second facteur obligatoire » par hôte, déchiffrement sops admin conditionné à la clé (age-plugin-fido2-hmac).