Outils pour utilisateurs

Outils du site


linux:ca-ssh

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
linux:ca-ssh [2026/06/03 13:12] – créée rootlinux:ca-ssh [2026/06/03 18:23] (Version actuelle) – [Étape 4.2 : Signature par la CA Yubikey] root
Ligne 1: Ligne 1:
-Setup CA SSH pour Make IT Simple+====== Setup CA SSH pour Make IT Simple ======
  
 Guide complet pour mettre en place une autorité de certification SSH dans un contexte MSP, avec support multi-Yubikey et intégration dans l'infrastructure MIS. Guide complet pour mettre en place une autorité de certification SSH dans un contexte MSP, avec support multi-Yubikey et intégration dans l'infrastructure MIS.
  
----+**Architecture allégée** : la clé privée de la CA est stockée sur une Yubikey 5 NFC dédiée (slot PIV), pas sur disque. Un backup d'urgence (fichier classique chiffré) est conservé dans Bitwarden pour les cas de perte matérielle. 
 + 
 +----
  
-## Compatibilité Windows+===== Compatibilité Windows =====
  
 **Réponse courte** : oui, mais avec une distinction importante entre les rôles. **Réponse courte** : oui, mais avec une distinction importante entre les rôles.
  
-Rôle Système Notes +Rôle Système Notes ^ 
-|------|---------|-------| +| **Poste de signature** (utilisation Yubikey CA) | Windows / Linux / macOS | OpenSSH + OpenSC ou ykcs11 
-| **Machine CA** (signature des certifs) | Linux (LXC Proxmox) | OpenSSH côté Linux uniquement pour ce setup +| **Poste technicien** (utilisation Yubikey perso) | Windows / Linux / macOS | OpenSSH inclus dans Windows 10/11 (1809+) |
-| **Poste technicien** (utilisation Yubikey) | Windows / Linux / macOS | OpenSSH inclus dans Windows 10/11 (1809+) |+
 | **Serveurs clients** (qui acceptent les certifs) | Linux principalement | Windows Server avec OpenSSH for Windows fonctionne aussi | | **Serveurs clients** (qui acceptent les certifs) | Linux principalement | Windows Server avec OpenSSH for Windows fonctionne aussi |
  
 **Côté Windows pour les techniciens** : **Côté Windows pour les techniciens** :
-OpenSSH client/server est intégré nativement à Windows 10 (build 1809+) et Windows 11 +  * OpenSSH client/server est intégré nativement à Windows 10 (build 1809+) et Windows 11 
-Vérification : `Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'` +  Vérification : %%Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'%% 
-Installation si absent : `Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0` +  Installation si absent : ''Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0'' 
-Pour les versions récentes d'OpenSSH (recommandé pour FIDO2) : `winget install Microsoft.OpenSSH.Beta` +  Pour les versions récentes d'OpenSSH (recommandé pour FIDO2) : ''winget install Microsoft.OpenSSH.Beta'' 
-Le support FIDO2/Yubikey (`ed25519-sk`) fonctionne nativement +  Le support FIDO2/Yubikey (''ed25519-sk'') fonctionne nativement 
-L'utilisation des certificats SSH est transparente : il suffit de placer le `-cert.pubà côté de la clé privée+  L'utilisation des certificats SSH est transparente : il suffit de placer le ''-cert.pub'' à côté de la clé privée
  
-**Côté machine CA** : on reste sur Linux (LXC Debian sur Proxmox). C'est plus simple, plus auditable, et compatible avec l'écosystème MIS existant.+**Pour signer avec la Yubikey CA depuis Windows** : Yubico fournit ''ykcs11.dll'' (inclus dans ''yubico-piv-tool''qui peut être utilisé comme provider PKCS#11 pour ''ssh-keygen -D''. Marche aussi.
  
----+----
  
-## Notes Debian 13 (Trixie)+===== Notes Debian 13 (Trixie) =====
  
 Cette documentation cible Debian 13 (codename **Trixie**), sortie le 9 août 2025. Quelques points importants pour notre cas d'usage SSH CA : Cette documentation cible Debian 13 (codename **Trixie**), sortie le 9 août 2025. Quelques points importants pour notre cas d'usage SSH CA :
  
-### Versions embarquées+==== Versions embarquées ====
  
-Composant Bookworm (Debian 12) Trixie (Debian 13) +Composant Bookworm (Debian 12) Trixie (Debian 13) ^
-|-----------|---------------------|--------------------|+
 | OpenSSH | 9.2p1 | 10.0p1 | | OpenSSH | 9.2p1 | 10.0p1 |
 | systemd | 252 | 257 | | systemd | 252 | 257 |
Ligne 40: Ligne 40:
 | Python | 3.11 | 3.13 | | Python | 3.11 | 3.13 |
  
-### Changements pertinents pour la CA SSH+==== Changements pertinents pour la CA SSH ====
  
 **OpenSSH 10.0** : meilleurs algos par défaut, mais surtout : **OpenSSH 10.0** : meilleurs algos par défaut, mais surtout :
-Les **clés DSA ne sont plus supportées du tout**, même avec les options de compat (`HostKeyAlgorithms``PubkeyAcceptedAlgorithms`). Aucun impact pour nous (on n'utilise que Ed25519/Ed25519-sk), mais à savoir si tu te connectes à de très vieux équipements (un `apt install openssh-client-ssh1` peut servir pour ces cas-là)+  * Les **clés DSA ne sont plus supportées du tout**, même avec les options de compat (''HostKeyAlgorithms''''PubkeyAcceptedAlgorithms''). Aucun impact pour nous (on n'utilise que Ed25519/ECCP384), mais à savoir si tu te connectes à de très vieux équipements. 
-Le support FIDO2 (libfido2) est plus mature qu'en Bookworm. +  Le support FIDO2 (libfido2) est plus mature qu'en Bookworm. 
-Les algorithmes post-quantum (mlkem768x25519-sha256) sont disponibles par défaut pour le key exchange.+  Les algorithmes post-quantum (mlkem768x25519-sha256) sont disponibles par défaut pour le key exchange.
  
-**`/tmp` est maintenant un tmpfs** en Trixie (RAM). Aucun impact sur nos workflows (les `.pub` font quelques centaines d'octets), mais à garder en tête pour des scripts qui manipuleraient de gros fichiers temporaires.+==== Support à long terme ====
  
-**`/etc/sysctl.conf` n'est plus lu** : utiliser `/etc/sysctl.d/99-mis.conf` à la place pour tes éventuelles personnalisations kernel. Sans impact direct sur le SSH CA, mais bon à savoir pour tes playbooks Ansible.+  Full support jusqu'à **août 2028** 
 +  * LTS jusqu'à **juin 2030**
  
-**openssh-server ne lit plus `~/.pam_environment`** : si jamais tu avais des configs spécifiques pour des sessions SSH, à migrer vers `~/.bash_profile` ou la conf PAM système. Non pertinent pour notre workflow MIS.+C'est donc une base saine pour démarrer une infra MSP qui doit vivre 4-5 ans sans upgrade majeur.
  
-### Support à long terme+----
  
-- Full support jusqu'à **août 2028** +===== Vue d'ensemble de l'architecture =====
-- LTS jusqu'à **juin 2030**+
  
-C'est donc une base saine pour démarrer une infra MSP qui doit vivre 4-5 ans sans upgrade majeur.+<code> 
 +┌──────────────────────────┐ 
 +│  Poste de signature      │ 
 +│  (laptop MIS Vincent)    │ 
 +│                          │ 
 +│  ┌────────────────────┐  │     ┌─────────────────────────┐ 
 +│  │ Yubikey CA dédiée  │──┼────►│ ssh-keygen -s           │ 
 +│  │ (slot PIV 9c)      │  │     │ via PKCS#11             │ 
 +│  │ ECCP384            │  │     │ → certificat signé      │ 
 +│  │ touch CACHED       │  │     └─────────────────────────┘ 
 +│  └────────────────────┘  │ 
 +│                          │ 
 +│  ┌────────────────────┐  │ 
 +│  │ Fichier emergency  │  │  (utilisé uniquement si 
 +│  │ chiffré age        │  │   Yubikey CA perdue) 
 +│  │ dans Bitwarden     │  │ 
 +│  └────────────────────┘  │ 
 +└──────────────────────────┘ 
 +              │ 
 +              │ déploiement via Ansible 
 +              ▼ 
 +┌──────────────────────────────────┐ 
 +│  Serveurs clients                │ 
 +│  /etc/ssh/mis-users-ca.pub       │ ← contient 2 lignes : 
 +│  (200+ VMs)                      │   1. Yubikey CA pub 
 +│                                  │   2. Emergency CA pub 
 +│  /etc/ssh/mis-revoked-keys       │ ← KRL pour révocations 
 +└──────────────────────────────────┘ 
 +</code>
  
-### Images officielles+**Le principe** : 
 +  * La CA quotidienne vit sur Yubikey PIV → clé privée non-extractible, touch + PIN obligatoires 
 +  * La CA emergency est un fichier classique chiffré avec ''age'', stocké dans Bitwarden 
 +  * Les **deux** clés publiques sont déployées sur les serveurs comme CAs valides 
 +  * En cas de perte de la Yubikey CA, on déchiffre l'emergency, on s'en sert pour signer en attendant la nouvelle Yubikey, puis on révoque (rolling rotation)
  
-Cloud image : https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 +----
-- Template LXC Proxmox : `debian-13-standard_13.X-Y_amd64.tar.zst` (récupérable via `pveam`)+
  
----+===== Phase 1 : Préparation du poste de signature =====
  
-## Vue d'ensemble de l'architecture+==== Étape 1.1 : Matériel requis ====
  
-``` +  * **1x Yubikey 5 NFC dédiée à la CA** (~50 €)À **séparer** de tes Yubikeys SSH d'authentification quotidienneCette Yubikey reste rangée la plupart du temps, branchée uniquement pour signer
-┌──────────────────┐ +  * Ton laptop MIS comme poste de signature (Linux, macOS ou Windows)
-│   Yubikey x2     │  Techniciens MIS (Vincent, futur tech...) +
-│   par tech       │  → génèrent leurs clés ed25519-sk localement +
-└────────┬─────────┘ +
-         │ .pub envoyée à Odoo +
-         ▼ +
-┌──────────────────┐         ┌──────────────────────┐ +
-│      Odoo        │ ──API──►│  Proxmox MIS         │ +
-│ makeitsimple_    │         │  └─ LXC mis-ssh-ca   │ +
-│   ssh_ca         │  démarre│     (éteinte 99%     │ +
-│                  │  à la   │      du temps)       │ +
-│                  │  demande│                      │ +
-└────────┬─────────┘         └──────────┬───────────┘ +
-         │                              │ ssh-keygen -s +
-         │                              ▼ +
-         │                   ┌──────────────────────┐ +
-         │                   │  Certificat SSH      │ +
-         │◄──────────────────│  signé (-cert.pub)   │ +
-         │                   └──────────────────────┘ +
-         │ +
-         │ déploiement +
-         ▼ +
-┌──────────────────┐ +
-│  Serveurs        │ +
-│  clients         │  /etc/ssh/mis-users-ca.pub +
-│  (200+ VMs     │  /etc/ssh/mis-revoked-keys +
-└──────────────────┘ +
-```+
  
----+==== Étape 1.2 : Installation des outils ====
  
-## Phase 1 Préparation de la LXC CA+**Sur Debian / Ubuntu / WSL2** :
  
-### Étape 1.1 : Création du conteneur+<code bash> 
 +sudo apt update 
 +sudo apt install -y \ 
 +    openssh-client \ 
 +    yubikey-manager \ 
 +    opensc \ 
 +    age 
 +</code>
  
-Sur le Proxmox MIS (pas un hyperviseur client) :+**Sur macOS** (Homebrew) :
  
-```bash +<code bash> 
-# Variables +brew install yubikey-manager opensc age openssh 
-CTID=200 +</code>
-HOSTNAME="mis-ssh-ca" +
-STORAGE="local-zfs" +
-BRIDGE="vmbr10"          # VLAN management isolé +
-TEMPLATE="debian-13-standard_13.5-1_amd64.tar.zst" +
-# Vérifier le nom exact disponible avec : +
-#   pveam update && pveam available | grep debian-13 +
-# Et télécharger si pas encore présent : +
-#   pveam download local debian-13-standard_13.5-1_amd64.tar.zst+
  
-# Création LXC +**Sur Windows** :
-pct create $CTID local:vztmpl/$TEMPLATE \ +
-  --hostname $HOSTNAME \ +
-  --cores 1 --memory 512 --swap 256 \ +
-  --rootfs $STORAGE:8 \ +
-  --net0 name=eth0,bridge=$BRIDGE,ip=10.10.10.50/24,gw=10.10.10.1 \ +
-  --nameserver 10.10.10.1 \ +
-  --onboot 0 \ +
-  --start 0 \ +
-  --unprivileged 1 \ +
-  --features nesting=0,keyctl=0 \ +
-  --password "TEMP_PASSWORD_CHANGE_IMMEDIATELY"+
  
-Démarrage +<code powershell> 
-pct start $CTID +Yubikey Manager : https://www.yubico.com/support/download/yubikey-manager/ 
-```+# Installer aussi yubico-piv-tool qui contient ykcs11.dll 
 +# OpenSSH inclus avec Windows 10/11 (sinon : Add-WindowsCapability) 
 +# age : winget install FiloSottile.age 
 +</code>
  
-### Étape 1.Durcissement initial+==== Étape 1.Localisation du provider PKCS#11 ====
  
-```bash +Cette information est cruciale pour les commandes de signature. Note le chemin selon ton OS :
-# Entrer dans la LXC +
-pct enter $CTID+
  
-Hardening de base +^ OS ^ Chemin PKCS#11 (OpenSC) ^ 
-apt update && apt upgrade -y +| Debian/Ubuntu (amd64) | ''/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so'' | 
-apt install -y openssh-server fail2ban ufw vim+| Debian/Ubuntu (arm64) | ''/usr/lib/aarch64-linux-gnu/opensc-pkcs11.so''
 +| macOS (Intel) | ''/usr/local/lib/opensc-pkcs11.so''
 +| macOS (Apple Silicon) | ''/opt/homebrew/lib/opensc-pkcs11.so''
 +| Windows | ''C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll'' |
  
-# Firewall SSH uniquement depuis le subnet de management +Vérification rapide qu'il existe :
-ufw default deny incoming +
-ufw default deny outgoing +
-ufw allow from 10.10.10.0/24 to any port 22 proto tcp +
-ufw allow out 53/udp                                   # DNS pour apt +
-ufw allow out 80,443/tcp                               # apt updates +
-ufw enable+
  
-Utilisateur dédié pour la signature +<code bash> 
-useradd -m -s /bin/bash ca-signer +Linux/macOS 
-mkdir -p /home/ca-signer/.ssh +ls -la $PKCS11_PATH 
-chmod 700 /home/ca-signer/.ssh+</code>
  
-# Désactivation auth password sur SSH +Pour la suite du document, on utilisera la variable ''$PKCS11'' à adapter à ton chemin :
-sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no//etc/ssh/sshd_config +
-sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no//etc/ssh/sshd_config +
-systemctl restart ssh +
-```+
  
-### Étape 1.3 : Stockage chiffré (optionnel mais recommandé)+<code bash> 
 +export PKCS11=/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so 
 +</code>
  
-Pour aller plus loin, la rootfs de la LXC peut être stockée sur un dataset ZFS chiffré côté hôte Proxmox :+==== Étape 1.4 : Initialisation des PINs de la Yubikey CA ====
  
-```bash +⚠️ **Yubikey CA branchée**. Vérifie que c'est la bonne (pas une Yubikey SSH du quotidien:
-# Sur l'hôte Proxmox (à faire AVANT la création de la LXC) +
-zfs create -o encryption=aes-256-gcm -o keyformat=passphrase rpool/encrypted-vault +
-# La passphrase sera demandée à chaque démarrage de l'hôte +
-```+
  
----+<code bash> 
 +ykman info 
 +# Doit afficher le serial number de la Yubikey CA 
 +</code>
  
-## Phase 2 Génération des CAs+**PIN PIV** (8 caractères minimum, sera demandé à chaque session de signature) :
  
-### Étape 2.1 CA pour les utilisateurs (techniciens)+<code bash> 
 +ykman piv access change-pin 
 +Default PIN: 123456 
 +New PIN[choisir 8+ chars] 
 +</code>
  
-```bash +**PUK** (utilisé pour débloquer si trop d'échecs PIN, à conserver précieusement) : 
-Connecté en tant que ca-signer dans la LXC + 
-mkdir -p ~/mis-ca && cd ~/mis-ca+<code bash
 +ykman piv access change-puk 
 +Default PUK: 12345678 
 +# New PUK: [générer 8 chars aléatoires] 
 +</code> 
 + 
 +**Management Key** (utilisée pour les opérations admin PIV, on la protège avec le PIN) : 
 + 
 +<code bash> 
 +ykman piv access change-management-key --algorithm AES256 --generate --protect 
 +# --protect = stocke la management key chiffrée par le PIN 
 +</code> 
 + 
 +📋 **Notes à conserver dans Bitwarden, entrée "MIS Yubikey CA"** : 
 +  * Serial number de la Yubikey 
 +  * PIN PIV 
 +  * PUK 
 +  * (la management key est protégée par le PIN, pas besoin de la stocker à part) 
 + 
 +---- 
 + 
 +===== Phase 2 : Génération des CAs ===== 
 + 
 +==== Étape 2.1 : CA principale sur Yubikey (users) ==== 
 + 
 +<code bash> 
 +# Yubikey CA branchée 
 +mkdir -p ~/mis-ssh-ca && cd ~/mis-ssh-ca
 chmod 700 . chmod 700 .
  
-ssh-keygen -t ed25519 -a 200 +# Génération de la paire ECCP384 dans le slot 9c (Digital Signature) 
-  -f mis-users-ca +ykman piv keys generate \ 
-  -C "MIS Users CA Make IT Simple" +  --algorithm ECCP384 
-  -N 'PASSPHRASE_TRES_ROBUSTE_ICI' +  --pin-policy ONCE 
-```+  --touch-policy CACHED 
 +  9c \ 
 +  mis-users-ca-yubi-cert.pub 
 +# → demande le PIN 
 +# → demande le touch 
 +</code>
  
-### Étape 2.2 : CA pour les hôtes (serveurs)+Décortiquons les options :
  
-```bash+^ Option ^ Valeur ^ Sens ^ 
 +| ''--algorithm'' | ''ECCP384'' | Courbe elliptique NIST P-384 (compromis sécurité/perf) | 
 +| ''--pin-policy'' | ''ONCE'' | PIN demandé 1x par session (après débranchement = re-demandé) | 
 +| ''--touch-policy'' | ''CACHED'' | Touch valide 15s après un précédent touch | 
 +| ''9c'' | slot | Slot "Digital Signature" - sémantique appropriée | 
 + 
 +**Alternative plus stricte** : ''--touch-policy ALWAYS'' exige un touch à **chaque** signature. Plus sûr mais pénible pour signer plusieurs certifs d'affilée (enrôlement de plusieurs Yubikeys d'un nouveau tech). ''CACHED'' est un bon compromis. 
 + 
 +==== Étape 2.2 : Certificat self-signed PIV ==== 
 + 
 +PIV exige qu'un certificat X.509 soit présent dans le slot, même s'il ne sert à rien dans notre usage SSH : 
 + 
 +<code bash
 +ykman piv certificates generate \ 
 +  --subject "CN=MIS Users CA,O=Make IT Simple,C=BE"
 +  --valid-days 3650 \ 
 +  9c \ 
 +  mis-users-ca-yubi-cert.pub 
 +# → demande le PIN 
 +</code> 
 + 
 +==== Étape 2.3 : Export de la clé publique au format SSH ==== 
 + 
 +C'est cette clé publique qui sera déployée sur tous les serveurs comme **CA principale acceptée** : 
 + 
 +<code bash> 
 +ssh-keygen -D $PKCS11 -e > mis-users-ca-yubi.pub 
 + 
 +# Inspection 
 +cat mis-users-ca-yubi.pub 
 +# Doit afficher : ecdsa-sha2-nistp384 AAAA... 
 + 
 +# Renommer avec un commentaire explicite (facultatif) 
 +sed -i 's|$| MIS Users CA - Yubikey|' mis-users-ca-yubi.pub 
 +</code> 
 + 
 +==== Étape 2.4 : Génération de la CA emergency (container éphémère) ==== 
 + 
 +La CA emergency est une clé privée Ed25519 classique, mais qui ne doit **jamais exister en clair sur ton laptop** après génération. La meilleure approche : un container Docker éphémère sans réseau, qui ne laisse aucune trace après destruction. 
 + 
 +**Pourquoi pas juste sur ton laptop ?** Parce qu'entre la génération en clair et le chiffrement age, la clé existe en clair sur ton disque. Avec un container sans réseau, aucun process tiers (même un éventuel malware) ne peut exfiltrer pendant cette fenêtre. 
 + 
 +**Génération via Docker** : 
 + 
 +<code bash> 
 +# Lancement d'un container Debian 13 minimal, SANS RÉSEAU 
 +docker run --rm -it \ 
 +  --network none \ 
 +  --hostname offline-ca-gen \ 
 +  -v ~/mis-ssh-ca:/output \ 
 +  debian:trixie-slim bash 
 +</code> 
 + 
 +**Dans le container** (aucun accès Internet possible) : 
 + 
 +<code bash> 
 +# Installation des outils (depuis le cache APT du container, pas Internet) 
 +# Si l'image n'a pas les paquets, faire docker build préalable (voir note ci-dessous) 
 +apt-get install -y openssh-client age coreutils 2>/dev/null || echo "Image sans cache, voir note" 
 + 
 +cd /tmp 
 + 
 +# Génération Ed25519 classique
 ssh-keygen -t ed25519 -a 200 \ ssh-keygen -t ed25519 -a 200 \
-  -f mis-hosts-ca \ +  -f mis-users-ca-emergency 
-  -C "MIS Hosts CA - Make IT Simple" \ +  -C "MIS Users CA - EMERGENCY USE ONLY" \ 
-  -N 'AUTRE_PASSPHRASE_TRES_ROBUSTE' +  -N ''
-```+
  
-### Étape 2.3 : Backup chiffré des clés privées+Chiffrement avec age - passphrase en interactif (PAS en argument !) 
 +age --passphrase \ 
 +  -o mis-users-ca-emergency.age \ 
 +  mis-users-ca-emergency 
 +→ entrer une passphrase TRÈS robuste (Diceware 6-8 mots recommandé)
  
-⚠️ **Étape critique**. Sans backup, perdre la LXC = perdre tout le contrôle d'accès.+# Effacement sécurisé de la version en clair 
 +shred -u mis-users-ca-emergency
  
-```bash +Copie des fichiers finaux vers l'hôte via le volume monté 
-Création d'une archive chiffrée +cp mis-users-ca-emergency.age /output/ 
-cd ~/mis-ca +cp mis-users-ca-emergency.pub /output/
-tar czf - mis-users-ca mis-hosts-ca | \ +
-  age -p > mis-ca-backup-$(date +%Y%m%d).tar.gz.age +
-# Demande une passphrase distincte des passphrases des CAs+
  
-À conserver dans : +Sortie du container → destruction automatique (--rm
-# 1. Bitwarden (attachment de l'entrée "MIS SSH CA Master Backup"+exit 
-# 2. Clé USB chiffrée dans coffre physique +</code>
-# 3. (optionnel) Coffre bancaire pour archive papier-QR de la passphrase +
-```+
  
----+**Note** : si ''debian:trixie-slim'' n'a pas les paquets en cache, prépare une image avec les outils inclus une fois pour toutes : 
 + 
 +<code bash> 
 +# À faire une seule fois, sur ton hôte 
 +cat > /tmp/Dockerfile <<'EOF' 
 +FROM debian:trixie-slim 
 +RUN apt-get update && \ 
 +    apt-get install -y --no-install-recommends \ 
 +        openssh-client age coreutils && \ 
 +    rm -rf /var/lib/apt/lists/
 +EOF 
 + 
 +docker build -t mis-ca-gen:latest -f /tmp/Dockerfile /tmp/ 
 + 
 +# Puis utiliser cette image (en remplaçant debian:trixie-slim) : 
 +docker run --rm -it --network none -v ~/mis-ssh-ca:/output mis-ca-gen:latest bash 
 +</code> 
 + 
 +**Variante Podman rootless** (si tu préfères ne pas dépendre d'un daemon Docker root) : 
 + 
 +<code bash> 
 +podman run --rm -it --network none -v ~/mis-ssh-ca:/output:Z mis-ca-gen:latest bash 
 +</code> 
 + 
 +**Résultat sur ton hôte** : 
 + 
 +<code bash> 
 +ls -la ~/mis-ssh-ca/ 
 +# mis-users-ca-emergency.age       ← clé privée DOUBLEMENT chiffrable (age + Bitwarden) 
 +# mis-users-ca-emergency.pub       ← clé publique (pas un secret) 
 +# mis-users-ca-yubi.pub            ← clé publique de la Yubikey CA 
 +# mis-users-ca-yubi-cert.pub       ← cert X.509 PIV (inutile pour SSH) 
 +</code> 
 + 
 +==== Étape 2.5 : CA pour les hôtes (optionnelle pour démarrer) ==== 
 + 
 +Même approche pour la CA d'hôtes, si tu décides de la mettre en place : 
 +  * Soit sur la **même Yubikey** dans le slot 9a (Authentication) ou 9d (Key Management) 
 +  * Soit avec un fichier classique chiffré, plus simple pour démarrer 
 + 
 +Comme tu as décidé de ne pas faire les certifs machines tout de suite, on saute cette étape pour le moment. À reprendre quand tu te lanceras dans les certifs d'hôtes. 
 + 
 +==== Étape 2.6 : Stratégie de stockage des secrets (double chiffrement) ==== 
 + 
 +**Principe** : la clé emergency est protégée par **deux couches** indépendantes : 
 + 
 +<code> 
 +Clé privée Ed25519 (claire) 
 +        ↓ couche 1 : chiffrement age (passphrase forte) 
 +Fichier .age (illisible sans passphrase age) 
 +        ↓ couche 2 : attachment dans Bitwarden (chiffré par master password) 
 +Coffre Bitwarden (illisible sans master password) 
 +</code> 
 + 
 +Pour que cette défense en profondeur fonctionne, **les deux secrets doivent être stockés à des endroits différents** : sinon une seule compromission donne accès à tout. 
 + 
 +=== Ce qui va dans Bitwarden === 
 + 
 +Entrée **"MIS SSH CA - Master Backup"** : 
 + 
 +**Notes (champ texte de l'entrée)** : 
 + 
 +<code> 
 +═══════════════════════════════════════════════ 
 +PROCÉDURE DE RÉCUPÉRATION D'URGENCE 
 + 
 +1. Récupérer la passphrase age : 
 +   - Coffre bureau MIS, enveloppe scellée "CA Emergency" 
 +   - OU coffre backup (maison Vincent / coffre bancaire) 
 + 
 +2. Télécharger mis-users-ca-emergency.age depuis cette entrée 
 + 
 +3. Sur container éphémère : 
 +     docker run --rm -it --network none -v $PWD:/work mis-ca-gen bash 
 +     cd /work 
 +     age --decrypt mis-users-ca-emergency.age > recovery-key 
 +     chmod 600 recovery-key 
 +     # utiliser pour signer (voir Phase 9 du runbook) 
 +     shred -u recovery-key 
 +     exit 
 + 
 +═══════════════════════════════════════════════ 
 +INFOS YUBIKEY CA 
 + 
 +Serial number    : [serial à compléter] 
 +PIN PIV          : [PIN à compléter] 
 +PUK              : [PUK à compléter] 
 +Algorithme       : ECCP384 
 +Slot             : 9c 
 +Touch policy     : CACHED 
 +PIN policy       : ONCE 
 + 
 +═══════════════════════════════════════════════ 
 +FINGERPRINTS (vérification d'intégrité) 
 + 
 +Yubikey CA pub   : [ssh-keygen -l -f mis-users-ca-yubi.pub] 
 +Emergency CA pub : [ssh-keygen -l -f mis-users-ca-emergency.pub] 
 + 
 +═══════════════════════════════════════════════ 
 +</code> 
 + 
 +**Attachments (fichiers attachés à l'entrée)** : 
 + 
 +^ Fichier ^ Sensibilité ^ Pourquoi dans Bitwarden ^ 
 +| ''mis-users-ca-emergency.age'' | DOUBLEMENT chiffré | Clé privée emergency, inutilisable sans la passphrase age | 
 +| ''mis-users-ca-emergency.pub'' | Publique (pas un secret) | Pratique pour la déployer et la vérifier | 
 +| ''mis-users-ca-yubi.pub'' | Publique (pas un secret) | Pratique pour le déploiement | 
 +| ''mis-users-ca-deploy.pub'' | Publique | Concaténation des 2 pubs prête à déployer | 
 + 
 +**Ce qui ne doit JAMAIS aller dans Bitwarden** : 
 +  * ❌ La passphrase age (sinon couche 1 + couche 2 = même point de défaillance) 
 +  * ❌ La clé privée emergency en clair 
 +  * ❌ Un export PKCS#12 de la Yubikey (impossible techniquement, mais à ne pas chercher à faire) 
 + 
 +=== Ce qui va sur papier (hors numérique) === 
 + 
 +La **passphrase age** uniquement. À écrire à la main sur papier : 
 + 
 +  * **Localisation principale** : coffre physique au bureau MIS, dans une enveloppe scellée "CA Emergency - Vincent uniquement" 
 +  * **Localisation backup** : coffre physique à la maison de Vincent (ou coffre bancaire), même enveloppe 
 + 
 +**Format recommandé** : passphrase Diceware 6-8 mots, plus facile à recopier sans erreur qu'une chaîne aléatoire. 
 + 
 +Exemple : ''correct horse battery staple winter coffee'' (à remplacer évidemment). 
 + 
 +**Approche avancée (optionnelle)** : Shamir Secret Sharing avec ''ssss-split'' pour découper la passphrase en N parts dont K nécessaires. Exemple : 3 parts, 2 nécessaires, distribuées entre Vincent + associé + avocat de confiance. Sécurité maximale, mais à mettre en place quand tout le reste est rodé. 
 + 
 +=== Backup hors-Bitwarden additionnel === 
 + 
 +En complément (ceinture-bretelles) : 
 +  * Clé USB chiffrée LUKS dans coffre physique avec une copie de tout ''~/mis-ssh-ca/'' 
 +  * À refaire à chaque rotation/renouvellement de CA 
 +  * La passphrase LUKS est encore un secret différent (toujours pas dans Bitwarden) 
 + 
 +==== Étape 2.7 : Test de récupération (CRITIQUE) ==== 
 + 
 +⚠️ **Avant de tout ranger dans le coffre et de considérer le setup terminé**, tu dois faire un test de récupération complet. C'est l'étape qui te garantit que le jour J, la procédure fonctionnera vraiment. 
 + 
 +<code bash> 
 +# 1. Dans un dossier temporaire de test 
 +mkdir /tmp/test-recovery && cd /tmp/test-recovery 
 + 
 +# 2. Copier le .age depuis Bitwarden (téléchargement de l'attachment) 
 +# OU depuis ~/mis-ssh-ca/ si encore dispo 
 +cp ~/mis-ssh-ca/mis-users-ca-emergency.age . 
 + 
 +# 3. Aller récupérer le papier dans le coffre, lire la passphrase 
 +#    (Geste réel, pas une simulation : tu dois vérifier que la passphrase 
 +#     est bien lisible et que tu peux la recopier sans erreur) 
 + 
 +# 4. Déchiffrer en utilisant la passphrase du papier 
 +age --decrypt mis-users-ca-emergency.age > recovery-key 
 +# → entrer la passphrase telle qu'écrite sur papier 
 + 
 +# 5. Vérifier que la clé déchiffrée est bien la bonne 
 +ssh-keygen -y -f recovery-key > recovered.pub 
 +diff recovered.pub ~/mis-ssh-ca/mis-users-ca-emergency.pub 
 +# Sortie attendue : VIDE (les deux fichiers sont identiques) 
 +# Si différents → la passphrase a été mal recopiée OU le .age est corrompu 
 + 
 +# 6. Nettoyage immédiat 
 +shred -u recovery-key recovered.pub 
 +cd .. && rm -rf /tmp/test-recovery 
 +</code> 
 + 
 +**Si le test réussit, tu as validé** : 
 +  * ✅ La passphrase est correctement écrite sur papier (lisible, sans erreur) 
 +  * ✅ Le fichier ''.age'' dans Bitwarden n'est pas corrompu 
 +  * ✅ Tu sais exécuter la procédure (le geste compte autant que le savoir) 
 +  * ✅ Les fingerprints correspondent 
 + 
 +**Si le test échoue**, NE PAS continuer. Identifier le problème (passphrase mal recopiée ? mauvais fichier ''.age'' ?) et regénérer si nécessaire. 
 + 
 +**À refaire annuellement** dans le cadre du drill de récupération (voir checklist en fin de doc). 
 + 
 +---- 
 + 
 +===== Phase 3 : Bootstrap d'un serveur client ===== 
 + 
 +==== Étape 3.1 : Préparation du fichier de CAs publiques ==== 
 + 
 +Sur ton poste de signature, prépare un fichier qui contient les **deux** clés publiques (Yubikey + emergency) que tu vas déployer sur tous les serveurs : 
 + 
 +<code bash> 
 +cd ~/mis-ssh-ca 
 +cat mis-users-ca-yubi.pub mis-users-ca-emergency.pub > mis-users-ca-deploy.pub 
 + 
 +cat mis-users-ca-deploy.pub 
 +# Doit contenir 2 lignes : 
 +# ecdsa-sha2-nistp384 AAAA... MIS Users CA - Yubikey 
 +# ssh-ed25519 AAAA... MIS Users CA - EMERGENCY USE ONLY 
 +</code> 
 + 
 +C'est ce fichier ''mis-users-ca-deploy.pub'' qui sera distribué sur tout le parc. OpenSSH accepte plusieurs CAs dans un même ''TrustedUserCAKeys'', une par ligne.
  
-## Phase 3 : Bootstrap d'un serveur client+==== Étape 3.2 Déploiement sur un serveur client ====
  
-### Étape 3.1 : Déploiement de la CA users sur le serveur+Sur un serveur client (Debian 13 provisionné via cloud-init) :
  
-Sur un serveur client (Debian provisionné via cloud-init) :+<code bash> 
 +# Copie du fichier de CAs 
 +scp ~/mis-ssh-ca/mis-users-ca-deploy.pub mis-admin@srv01.acme.lan:/tmp/
  
-```bash +Côté serveur, déploiement 
-Copie de la clé publique de la CA users +ssh mis-admin@srv01.acme.lan 
-scp ca-signer@10.10.10.50:~/mis-ca/mis-users-ca.pub /etc/ssh/mis-users-ca.pub +sudo install -o root -g root -m 0644 /tmp/mis-users-ca-deploy.pub /etc/ssh/mis-users-ca.pub
-chmod 644 /etc/ssh/mis-users-ca.pub+
  
 # Création d'un KRL vide initial # Création d'un KRL vide initial
-ssh-keygen -k -f /etc/ssh/mis-revoked-keys +sudo ssh-keygen -k -f /etc/ssh/mis-revoked-keys 
-chmod 644 /etc/ssh/mis-revoked-keys+sudo chmod 644 /etc/ssh/mis-revoked-keys
  
 # Configuration sshd # Configuration sshd
-cat >> /etc/ssh/sshd_config <<'EOF'+sudo tee -a /etc/ssh/sshd_config <<'EOF'
  
 # ===== MIS SSH CA ===== # ===== MIS SSH CA =====
Ligne 248: Ligne 526:
  
 # Test et reload # Test et reload
-sshd -t && systemctl reload ssh +sudo sshd -t && sudo systemctl reload ssh 
-```+</code>
  
 À industrialiser via Ansible (voir Phase 7). À industrialiser via Ansible (voir Phase 7).
  
-### Étape 3.2 : Certificat d'hôte pour le serveur (recommandé)+----
  
-Sur la CA :+===== Phase 4 Enrôlement d'une Yubikey technicien =====
  
-```bash +==== Étape 4.Génération sur le poste du technicien ====
-# Récupération de la clé publique du serveur +
-scp mis-admin@srv01.acme.lan:/etc/ssh/ssh_host_ed25519_key.pub /tmp/srv01.pub+
  
-# Signature comme hôte +**Sur Windows** (PowerShell, Yubikey du tech branchée:
-ssh-keygen -s ~/mis-ca/mis-hosts-ca \ +
-  -I "srv01.acme.lan-$(date +%Y)" \ +
-  -h \ +
-  -n "srv01.acme.lan,srv01,192.168.1.50"+
-  -V "+104w"+
-  /tmp/srv01.pub+
  
-# Envoi sur le serveur +<code powershell>
-scp /tmp/srv01-cert.pub mis-admin@srv01.acme.lan:/tmp/ +
-ssh mis-admin@srv01.acme.lan "sudo mv /tmp/srv01-cert.pub /etc/ssh/ssh_host_ed25519_key-cert.pub" +
- +
-# Activation côté serveur +
-ssh mis-admin@srv01.acme.lan "sudo sed -i '/^HostCertificate/d' /etc/ssh/sshd_config && \ +
-  echo 'HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub' | sudo tee -a /etc/ssh/sshd_config && \ +
-  sudo systemctl reload ssh" +
-``` +
- +
-### Étape 3.3 : Configuration côté postes techniciens +
- +
-Sur **chaque** poste tech (Windows ou Linux), dans `~/.ssh/known_hosts` : +
- +
-``` +
-@cert-authority *.acme.lan,*.mis.lan,10.10.* ssh-ed25519 AAAAC3... MIS Hosts CA +
-``` +
- +
-(la clé publique correspond au contenu de `mis-hosts-ca.pub`) +
- +
-À partir de ce moment, plus aucun warning de host key à la première connexion sur un serveur MIS. +
- +
---- +
- +
-## Phase 4 : Enrôlement d'une Yubikey technicien +
- +
-### Étape 4.1 : Génération sur le poste du technicien +
- +
-**Sur Windows** : +
- +
-```powershell +
-# PowerShell, Yubikey branchée+
 # Définition d'un PIN FIDO2 si pas déjà fait (via Yubikey Manager GUI) # Définition d'un PIN FIDO2 si pas déjà fait (via Yubikey Manager GUI)
  
Ligne 306: Ligne 545:
   -f $env:USERPROFILE\.ssh\id_ed25519_sk_yubi1 `   -f $env:USERPROFILE\.ssh\id_ed25519_sk_yubi1 `
   -C "vincent@yubi-primary"   -C "vincent@yubi-primary"
-```+</code>
  
 **Sur Linux/macOS** : **Sur Linux/macOS** :
  
-```bash+<code bash>
 ssh-keygen -t ed25519-sk -O resident -O application=ssh:mis -O verify-required \ ssh-keygen -t ed25519-sk -O resident -O application=ssh:mis -O verify-required \
   -f ~/.ssh/id_ed25519_sk_yubi1 \   -f ~/.ssh/id_ed25519_sk_yubi1 \
   -C "vincent@yubi-primary"   -C "vincent@yubi-primary"
-```+</code>
  
-Résultat : `id_ed25519_sk_yubi1(stub) et `id_ed25519_sk_yubi1.pub(clé publique).+Résultat : ''id_ed25519_sk_yubi1'' (stub) et ''id_ed25519_sk_yubi1.pub'' (clé publique).
  
-### Étape 4.2 : Signature par la CA+==== Étape 4.2 : Signature par la CA Yubikey ====
  
-Le technicien envoie sa `.pub` à l'admin MIS (via Odoo, mail, ou git interne)Sur la CA :+Le tech envoie sa ''.pub'' au poste de signature**Yubikey CA branchée** :
  
-```bash +<code bash> 
-# Sur la LXC, en tant que ca-signer +cd ~/mis-ssh-ca 
-cd ~/mis-ca+export PKCS11=/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
  
 # Variables # Variables
-PUBKEY_PATH=/tmp/vincent-yubi-primary.pub+PUBKEY=/tmp/vincent-yubi-primary.pub
 CERT_ID="vincent-yubi-primary-$(date +%Y)" CERT_ID="vincent-yubi-primary-$(date +%Y)"
 PRINCIPALS="mis-admin,vincent" PRINCIPALS="mis-admin,vincent"
 VALIDITY="+52w" VALIDITY="+52w"
-SERIAL=1001            # incrémenté à chaque signature+SERIAL=1001
  
-ssh-keygen -s mis-users-ca \+ssh-keygen -s mis-users-ca-yubi.pub \ 
 +  -D $PKCS11 \
   -I "$CERT_ID" \   -I "$CERT_ID" \
   -n "$PRINCIPALS" \   -n "$PRINCIPALS" \
Ligne 342: Ligne 582:
   -O no-agent-forwarding \   -O no-agent-forwarding \
   -O permit-pty \   -O permit-pty \
-  "$PUBKEY_PATH"+  "$PUBKEY"
  
-Le fichier vincent-yubi-primary-cert.pub est généré à côté de la .pub +Appuyer sur la yubikey dès que le pin est introduit 
-```+# → demande le PIN PIV (1x par session grâce à --pin-policy ONCE) 
 +# → demande le touch sur la Yubikey CA 
 +# → fichier vincent-yubi-primary-cert.pub généré à côté de la .pub 
 +</code>
  
-Renvoyer le `-cert.pub` au technicien.+**Note importante** : avec ''-D $PKCS11'', l'argument ''-s'' n'est **pas** la clé privée (qui est sur la Yubikey) mais la clé **publique** de la CAC'est elle qui permet à ''ssh-keygen'' d'identifier laquelle utiliser sur le token.
  
-### Étape 4.3 : Mise en place côté technicien+Renvoyer le ''-cert.pub'' au technicien.
  
-Le tech place le `-cert.pub` à côté de sa clé :+==== Étape 4.Mise en place côté technicien ====
  
-```+Le tech place le ''-cert.pub'' à côté de sa clé : 
 + 
 +<code>
 ~/.ssh/ ~/.ssh/
 ├── id_ed25519_sk_yubi1 ├── id_ed25519_sk_yubi1
 ├── id_ed25519_sk_yubi1.pub ├── id_ed25519_sk_yubi1.pub
 └── id_ed25519_sk_yubi1-cert.pub    ← nouveau └── id_ed25519_sk_yubi1-cert.pub    ← nouveau
-```+</code>
  
 Vérification du contenu du certificat : Vérification du contenu du certificat :
  
-```bash+<code bash>
 ssh-keygen -L -f ~/.ssh/id_ed25519_sk_yubi1-cert.pub ssh-keygen -L -f ~/.ssh/id_ed25519_sk_yubi1-cert.pub
-```+</code>
  
-Doit afficher : Principals, Valid from/to, Critical Options, Extensions.+Doit afficher : Signing CA (''ECDSA SHA256:...'' = ta Yubikey CA), Principals, Valid from/to.
  
-### Étape 4.4 : Test de connexion+==== Étape 4.4 : Test de connexion ====
  
-```bash+<code bash>
 ssh -i ~/.ssh/id_ed25519_sk_yubi1 mis-admin@srv01.acme.lan ssh -i ~/.ssh/id_ed25519_sk_yubi1 mis-admin@srv01.acme.lan
-# → PIN FIDO2 demandé +# → PIN FIDO2 demandé (côté Yubikey du tech) 
-# → touch Yubikey+# → touch Yubikey du tech
 # → connecté # → connecté
-```+</code>
  
-**Indicateur clé** aucune modification d'`authorized_keys` sur le serveur. Tout passe par le certificat.+==== Étape 4.5 Enrôlement de la Yubikey de backup ====
  
-### Étape 4.5 : Enrôlement de la Yubikey de backup+**Répéter Étape 4.1 à 4.4 avec la deuxième Yubikey du tech**. Utiliser : 
 +  * Serial différent (''-z 1002''
 +  * Identifiant unique (''vincent-yubi-backup-2026'')
  
-**Répéter Étape 4.1 à 4.4 avec la deuxième Yubikey**. Important utiliser un serial différent (`-z 1002`) et un identifiant unique (`vincent-yubi-backup-2026`).+==== Étape 4.Configuration `~/.ssh/configcôté tech ====
  
-À la fin, le technicien a 6 fichiers dans `~/.ssh/` : +<code> 
- +Host *.acme.lan acme-* *.mis.lan
-``` +
-id_ed25519_sk_yubi1 +
-id_ed25519_sk_yubi1.pub +
-id_ed25519_sk_yubi1-cert.pub +
-id_ed25519_sk_yubi2 +
-id_ed25519_sk_yubi2.pub +
-id_ed25519_sk_yubi2-cert.pub +
-``` +
- +
-### Étape 4.6 : Configuration `~/.ssh/config` côté tech +
- +
-``` +
-Host *.acme.lan acme-*+
     User mis-admin     User mis-admin
     IdentityFile ~/.ssh/id_ed25519_sk_yubi1     IdentityFile ~/.ssh/id_ed25519_sk_yubi1
Ligne 404: Ligne 638:
     CertificateFile ~/.ssh/id_ed25519_sk_yubi1-cert.pub     CertificateFile ~/.ssh/id_ed25519_sk_yubi1-cert.pub
     CertificateFile ~/.ssh/id_ed25519_sk_yubi2-cert.pub     CertificateFile ~/.ssh/id_ed25519_sk_yubi2-cert.pub
 +</code>
  
-Host *.mis.lan +----
-    User mis-admin +
-    IdentityFile ~/.ssh/id_ed25519_sk_yubi1 +
-    IdentityFile ~/.ssh/id_ed25519_sk_yubi2 +
-    IdentitiesOnly yes +
-    CertificateFile ~/.ssh/id_ed25519_sk_yubi1-cert.pub +
-    CertificateFile ~/.ssh/id_ed25519_sk_yubi2-cert.pub +
-``` +
- +
-SSH essaiera Yubi1 d'abord, puis Yubi2 si la première n'est pas branchée. Aucune intervention nécessaire pour basculer. +
- +
----+
  
-## Phase 5 : Révocation d'une clé+===== Phase 5 : Révocation d'une clé =====
  
-### Étape 5.1 : Mise à jour de la KRL+==== Étape 5.1 : Mise à jour de la KRL ====
  
-Sur la CA, si on perd la Yubikey backup de Vincent :+Yubikey CA branchée sur le poste de signature :
  
-```bash +<code bash> 
-cd ~/mis-ca+cd ~/mis-ssh-ca
  
-Création du spec de révocation+Spec de révocation
 cat > /tmp/krl-spec.txt <<'EOF' cat > /tmp/krl-spec.txt <<'EOF'
 # Révocation par serial # Révocation par serial
Ligne 439: Ligne 663:
 EOF EOF
  
-# Génération/mise à jour de la KRL +# Génération/mise à jour de la KRL via Yubikey 
-ssh-keygen -k -f mis-revoked-keys -s mis-users-ca -u /tmp/krl-spec.txt+ssh-keygen -k -f mis-revoked-keys 
 +  -s mis-users-ca-yubi.pub \ 
 +  -D $PKCS11 \ 
 +  -u /tmp/krl-spec.txt
 # -u = update (ajoute au KRL existant) # -u = update (ajoute au KRL existant)
-Sans -u, le KRL est remplacé+→ demande PIN + touch 
 +</code>
  
-# Vérification du contenu +==== Étape 5.2 : Déploiement de la KRL via Ansible ====
-ssh-keygen -Q -f mis-revoked-keys -t krl +
-```+
  
-### Étape 5.2 : Déploiement de la KRL+<code bash> 
 +ansible-playbook -i inventory.yml mis-update-krl.yml 
 +</code>
  
-Via Ansible sur tout le parc (voir Phase 7) :+En quelques minutes, la Yubikey backup n'est plus utilisable sur aucun serveur MIS. **Aucune modification de ''sshd_config'' ni d'''authorized_keys'' n'est requise.**
  
-```bash +----
-ansible-playbook -i inventory.yml mis-deploy-krl.yml +
-```+
  
-En quelques minutes, la Yubikey backup n'est plus utilisable sur aucun serveur MIS. **Aucune modification de `sshd_config` ni d'`authorized_keys` n'est requise.**+===== Phase 6 : Renouvellement des certificats =====
  
----+==== Option A : Manuel ====
  
-## Phase 6 : Renouvellement des certificats+Refaire l'étape 4.2 (signature) avec un nouvel identifiant ''vincent-yubi-primary-2027'' et envoyer le nouveau ''-cert.pub'' au tech. Le tech remplace son ancien ''-cert.pub''.
  
-Les certificats ayant une durée de vie de 1 an, prévoir un workflow de renouvellement : +==== Option B : Automatisé via Odoo ====
- +
-### Option A : Manuel +
- +
-Refaire les étapes 4.2 (signature) avec un nouvel identifiant `vincent-yubi-primary-2027` et envoyer le nouveau `-cert.pub` au tech. Le tech remplace son ancien `-cert.pub`. +
- +
-### Option B : Automatisé via Odoo+
  
 Cron Odoo qui scanne les certificats expirant dans 30 jours et : Cron Odoo qui scanne les certificats expirant dans 30 jours et :
-1. Notifie le tech par mail +  - Notifie le tech par mail 
-2. Propose un wizard de renouvellement +  Propose un wizard de renouvellement 
-3. Si le tech accepte, regénère un certif via l'API de la CA +  Si le tech accepte, déclenche une demande de signature (qui attend que tu branches la Yubikey CA + valides physiquement) 
-4. Envoie le nouveau `-cert.pubau tech (par mail chiffré ou portail download)+  Envoie le nouveau ''-cert.pub'' au tech
  
----+----
  
-## Phase 7 : Industrialisation Ansible+===== Phase 7 : Industrialisation Ansible =====
  
-### Playbook de déploiement initial CA + KRL+==== Playbook de déploiement initial CA + KRL ====
  
-`playbooks/mis-ssh-ca-deploy.yml:+''playbooks/mis-ssh-ca-deploy.yml'' :
  
-```yaml+<code yaml>
 --- ---
 - name: Deploy MIS SSH CA on all client servers - name: Deploy MIS SSH CA on all client servers
Ligne 490: Ligne 710:
   become: true   become: true
   vars:   vars:
-    ca_pub_path: files/mis-users-ca.pub+    ca_pub_path: files/mis-users-ca-deploy.pub   # contient les 2 CAs (Yubikey + emergency)
     krl_path: files/mis-revoked-keys     krl_path: files/mis-revoked-keys
      
   tasks:   tasks:
-    - name: Deploy MIS users CA public key+    - name: Deploy MIS users CA public keys
       copy:       copy:
         src: "{{ ca_pub_path }}"         src: "{{ ca_pub_path }}"
Ligne 544: Ligne 764:
         name: ssh         name: ssh
         state: reloaded         state: reloaded
-```+</code>
  
-### Playbook de mise à jour KRL uniquement (rapide)+==== Playbook de mise à jour KRL uniquement ====
  
-`playbooks/mis-update-krl.yml:+''playbooks/mis-update-krl.yml'' :
  
-```yaml+<code yaml>
 --- ---
 - name: Update MIS SSH KRL on all client servers - name: Update MIS SSH KRL on all client servers
Ligne 569: Ligne 789:
         name: ssh         name: ssh
         state: reloaded         state: reloaded
-```+</code>
  
----+----
  
-## Phase 8 : Compte parachute (break-glass)+===== Phase 8 : Compte parachute (break-glass) =====
  
 ⚠️ **Ne JAMAIS sauter cette étape.** ⚠️ **Ne JAMAIS sauter cette étape.**
  
-Indépendamment de la CA, chaque serveur doit avoir au moins un accès de secours :+C'est un parachute **indépendant** de la CA SSH (ni Yubikeyni emergency CA). Le compte break-glass utilise une clé SSH classique, pour le cas où **toute** la PKI CA serait inaccessible (perte Yubikey + Bitwarden corrompu + papier coffre perdu, scénario apocalypse).
  
-```bash+<code bash>
 # Sur un poste sûr, génération d'une clé classique pour le parachute # Sur un poste sûr, génération d'une clé classique pour le parachute
 ssh-keygen -t ed25519 -a 200 -f mis-breakglass -C "MIS BREAK-GLASS - DO NOT USE" ssh-keygen -t ed25519 -a 200 -f mis-breakglass -C "MIS BREAK-GLASS - DO NOT USE"
Ligne 585: Ligne 805:
  
 # Stocker mis-breakglass (privé) : # Stocker mis-breakglass (privé) :
-#   - Chiffré dans Bitwarden (attachment)+#   - Chiffré dans Bitwarden (attachment) - entrée distincte de la CA
 #   - Sur clé USB chiffrée dans coffre physique #   - Sur clé USB chiffrée dans coffre physique
 #   - Supprimer du disque local après stockage sécurisé #   - Supprimer du disque local après stockage sécurisé
  
 # Garder mis-breakglass.pub pour déploiement # Garder mis-breakglass.pub pour déploiement
-```+</code>
  
 Intégrer la clé publique dans le template cloud-init / virt-customize de **tous** les serveurs : Intégrer la clé publique dans le template cloud-init / virt-customize de **tous** les serveurs :
  
-```bash +<code bash>
-# Dans virt-customize de la golden image+
 virt-customize -a debian-13-genericcloud-amd64.qcow2 \ virt-customize -a debian-13-genericcloud-amd64.qcow2 \
   --run-command 'useradd -m -s /bin/bash mis-breakglass' \   --run-command 'useradd -m -s /bin/bash mis-breakglass' \
Ligne 601: Ligne 820:
   --run-command 'echo "mis-breakglass ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/mis-breakglass' \   --run-command 'echo "mis-breakglass ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/mis-breakglass' \
   ...   ...
-```+</code>
  
-Le jour où la CA est inaccessible (perte de toutes les Yubikeys, corruption de la LXC, etc.), tu sors la clé du coffre Bitwarden, tu te connectes avec, tu reconstruis la situation.+----
  
----+===== Phase 9 : Procédure de récupération d'urgence =====
  
-## Phase 9 Migration future vers Yubikey PIV pour la CA+==== Cas 1 : Yubikey CA perdue / cassée, emergency disponible ====
  
-Quand le workflow sera rodé (6-12 mois), migration de la clé privée de la CA depuis disque vers Yubikey PIV :+**Workflow** :
  
-```bash +  - Acheter une nouvelle Yubikey 5 NFC dédiée à la CA 
-Sur un poste sûr, Yubikey dédiée "CA Master" branchée +  - Déchiffrer temporairement l'emergency : 
-apt install yubikey-manager opensc+   ```bash 
 +   cd ~/mis-ssh-ca 
 +   age --decrypt mis-users-ca-emergency.age > /tmp/emergency-key 
 +   chmod 600 /tmp/emergency-key 
 +   ``` 
 +  - **Utiliser l'emergency CA pour signer une nouvelle CA Yubikey** (chaîne de confiance temporaire) : 
 +   ```bash 
 +   Préparer la nouvelle Yubikey (Étapes 1.4 + 2.1 à 2.3) 
 +   # Tu obtiens mis-users-ca-yubi-NEW.pub 
 +   ``` 
 +  - Mettre à jour ''mis-users-ca-deploy.pub'' pour inclure la **nouvelle** Yubikey CA + l'emergency (toujours) : 
 +   ```bash 
 +   cat mis-users-ca-yubi-NEW.pub mis-users-ca-emergency.pub > mis-users-ca-deploy.pub 
 +   ``` 
 +  - Déployer via Ansible sur tout le parc 
 +  - Révoquer l'ancienne Yubikey CA via KRL (signature avec la NEW Yubikey) : 
 +   ```bash 
 +   cat > /tmp/krl-revoke-old-ca.txt <<'EOF' 
 +   # Révocation de l'ancienne CA Yubikey perdue 
 +   sha256: SHA256:<fingerprint de mis-users-ca-yubi-OLD.pub> 
 +   EOF 
 +    
 +   ssh-keygen -k -f mis-revoked-keys \ 
 +     -s mis-users-ca-yubi-NEW.pub \ 
 +     -D $PKCS11 \ 
 +     -u /tmp/krl-revoke-old-ca.txt 
 +   ``` 
 +  - **Effacement sécurisé** du fichier emergency en clair : 
 +   ```bash 
 +   shred -u /tmp/emergency-key 
 +   ``` 
 +  - Mise à jour de l'entrée Bitwarden avec le nouveau serial Yubikey
  
-# Génération d'une nouvelle paire ECCP384 dans slot 9c +**Important** : tous les certificats utilisateurs signés par l'ancienne Yubikey CA sont **toujours valides** (puisque l'ancienne CA est encore dans le déploiement jusqu'à révocation, et que la révocation cible le pub de la CA, pas les certifs signés). Pour invalider tous les certifs émis par l'ancienne CA, supprimer son ''.pub'' du déploiement et redéployer.
-ykman piv keys generate --algorithm ECCP384 --pin-policy ONCE --touch-policy CACHED \ +
-  9c /tmp/mis-ca-piv.pub+
  
-# Certificat self-signed pour PIV +==== Cas 2 : Tout est perdu sauf break-glass ====
-ykman piv certificates generate --subject "CN=MIS Users CA" 9c /tmp/mis-ca-piv.pub+
  
-# Export au format SSH +Scénario apocalypse : Yubikey CA perdue + Bitwarden inaccessible + papier coffre détruit.
-ssh-keygen -D /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so -e > mis-users-ca-yubi.pub +
-```+
  
-Pour signer ensuite :+  - Connexion sur chaque serveur via SSH avec ''mis-breakglass'' : 
 +   ```bash 
 +   ssh -i ~/.bitwarden-recovered/mis-breakglass mis-breakglass@srv01.acme.lan 
 +   ``` 
 +  - Régénération complète d'une nouvelle CA (depuis zéro, Phase 2) 
 +  - Déploiement manuel du nouveau ''mis-users-ca.pub'' sur chaque serveur via ''mis-breakglass'' 
 +  - Re-signature de toutes les Yubikeys techniciens 
 +  - Rotation du break-glass aussi (puisqu'il a servi)
  
-```bash +**À tester** : faire un drill annuel de récupération sur un environnement de staging pour valider que la procédure marche **avant** d'en avoir vraiment besoin.
-ssh-keygen -s mis-users-ca-yubi.pub \ +
-  -D /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so \ +
-  -I "vincent-yubi-primary-2027"+
-  -n "mis-admin,vincent"+
-  -V "+52w"+
-  vincent-yubi-primary.pub +
-# → PIN PIV demandé +
-# → touch Yubikey +
-# → certificat signé +
-```+
  
-La clé privée de la CA n'existe plus sur disque, elle est uniquement dans la Yubikey "CA Master" (qui doit avoir une jumelle de backup stockée en coffre). +----
- +
----+
  
-## Annexes+===== Annexes =====
  
-### A. Commandes utiles de diagnostic+==== A. Commandes utiles de diagnostic ====
  
-```bash+<code bash>
 # Inspecter un certificat utilisateur # Inspecter un certificat utilisateur
 ssh-keygen -L -f id_ed25519_sk_yubi1-cert.pub ssh-keygen -L -f id_ed25519_sk_yubi1-cert.pub
 +
 +# Voir l'état PIV de la Yubikey CA
 +ykman piv info
 +
 +# Voir le slot 9c (où vit la CA)
 +ykman piv keys metadata 9c
  
 # Voir la KRL active sur un serveur # Voir la KRL active sur un serveur
Ligne 659: Ligne 905:
 # Output: "id_ed25519_sk_yubi1-cert.pub: ok" ou "REVOKED" # Output: "id_ed25519_sk_yubi1-cert.pub: ok" ou "REVOKED"
  
-# Côté serveur, voir quelles connexions par certificat+# Côté serveur, voir les connexions par certificat
 journalctl -u ssh | grep "Accepted publickey.*ID" journalctl -u ssh | grep "Accepted publickey.*ID"
  
-# Lister les clés résidentes sur une Yubikey +# Lister les clés publiques visibles via PKCS#11 (avec Yubikey CA branchée) 
-ssh-keygen -+ssh-keygen -D $PKCS11 -e
-# Récupère les stubs sur le poste courant depuis la Yubikey +
-```+
  
-### B. Format complet d'une signature ssh-keygen+Tester une signature sans toucher à un fichier 
 +echo "test"ssh-keygen -Y sign -n test -f mis-users-ca-yubi.pub -D $PKCS11 
 +</code>
  
-```bash +==== B. Format complet d'une signature ssh-keygen avec Yubikey ==== 
-ssh-keygen -s CA_PRIVATE_KEY \+ 
 +<code bash> 
 +ssh-keygen -s CA_PUBLIC_KEY \ 
 +  -D PKCS11_PROVIDER \
   -I CERT_IDENTIFIER \   -I CERT_IDENTIFIER \
   -n PRINCIPAL1,PRINCIPAL2 \   -n PRINCIPAL1,PRINCIPAL2 \
Ligne 678: Ligne 927:
   -O OPTION2 \   -O OPTION2 \
   USER_PUBLIC_KEY.pub   USER_PUBLIC_KEY.pub
-```+</code>
  
-Option Description | +Option Description ^ 
-|--------|-------------| +''-s'' | Chemin vers la clé **publique** de la CA (quand ''-D'' est utilisé) 
-| `-s| Chemin vers la clé privée de la CA | +''-D'' | Path vers le module PKCS#11 (''opensc-pkcs11.so'') | 
-`-I| Identifiant lisible du certificat (visible dans logs sshd) | +| ''-I'' | Identifiant lisible du certificat (visible dans logs sshd) | 
-`-n| Principals (noms d'utilisateurs autorisés), séparés par virgule | +''-n'' | Principals (noms d'utilisateurs autorisés), séparés par virgule | 
-`-V| Validité (`+52w``+30d``+8h``20260601:20270601`) | +''-V'' | Validité (''+52w''''+30d''''+8h''''20260601:20270601'') | 
-`-z| Serial unique (entier) | +''-z'' | Serial unique (entier) | 
-`-h| Génère un certificat d'**hôte** (sinon utilisateur par défaut) | +''-h'' | Génère un certificat d'**hôte** (sinon utilisateur par défaut) | 
-`-O clear| Reset les permissions par défaut | +''-O clear'' | Reset les permissions par défaut | 
-`-O no-port-forwarding| Désactive le port forwarding | +''-O no-port-forwarding'' | Désactive le port forwarding | 
-`-O no-agent-forwarding| Désactive l'agent forwarding | +''-O no-agent-forwarding'' | Désactive l'agent forwarding | 
-`-O no-X11-forwarding` | Désactive X11 | +''-O permit-pty'' | Autorise l'allocation PTY (interactif) | 
-| `-O permit-pty| Autorise l'allocation PTY (interactif) | +''-O force-command="..."'' | Force l'exécution d'une commande spécifique | 
-`-O force-command="..."| Force l'exécution d'une commande spécifique | +''-O source-address="10.0.0.0/8"'' | Restreint l'origine IP |
-`-O source-address="10.0.0.0/8"| Restreint l'origine IP |+
  
-### C. Liens utiles+==== C. Liens utiles ====
  
-OpenSSH manual `ssh-keygen(1)`man ssh-keygen` +  * OpenSSH manual ''ssh-keygen(1)'' ''man ssh-keygen'' (sections CERTIFICATES et KEY REVOCATION LISTS) 
-Documentation officielle Yubico FIDO2 SSH : https://developers.yubico.com/SSH/ +  Documentation officielle Yubico FIDO2 SSH : https://developers.yubico.com/SSH/ 
-- Cloud-init reference : https://cloudinit.readthedocs.io+  * Documentation Yubico PIV : https://developers.yubico.com/PIV/Guides/SSH//with//PIV//and//PKCS11.html 
-- Format des certificats SSH (technique) : `PROTOCOL.certkeysdans le code OpenSSH+  * Documentation ''age'' : https://age-encryption.org/ 
 +  * Format des certificats SSH (technique) : ''PROTOCOL.certkeys'' dans le code OpenSSH
  
----+----
  
-## Checklist de mise en place+===== Checklist de mise en place =====
  
-### Phase 0 - Préparation +==== Phase 0 - Matériel et logiciels ==== 
-[ ] Yubikeys commandées (minimum 2 par technicien + 1 vault+  * [ ] Yubikey 5 NFC dédiée à la CA commandée 
-[ ] PINs FIDO2 définis sur chaque Yubikey (via Yubikey Manager) +  * [ ] Yubikeys 5 NFC techniciens commandées (2 par tech minimum
-- [ ] Bitwarden ready pour stockage des secrets+  [ ] Bitwarden ready avec entrée "MIS SSH CA Master Backup" 
 +  * [ ] Coffre physique identifié pour passphrase + clé USB de backup 
 +  * [ ] OpenSSH + yubikey-manager + opensc + age installés sur le poste de signature
  
-### Phase 1-2 - CA +==== Phase 1-2 - CA ==== 
-[ ] LXC `mis-ssh-ca` créée et durcie +  [ ] PINs PIV de la Yubikey CA configurés (PIN + PUK + management key) 
-- [ ] CA users générée + passphrase robuste +  * [ ] CA principale générée sur Yubikey (slot 9c, ECCP384) 
-[ ] CA hosts générée + passphrase robuste +  * [ ] Clé publique Yubikey CA exportée (''mis-users-ca-yubi.pub'') 
-- [ ] Backup chiffré des CAs dans Bitwarden + offline +  * [ ] Image Docker ''mis-ca-gen'' préparée 
-[ ] LXC éteinte par défaut, allumage à la demande+  * [ ] Emergency CA générée dans container éphémère chiffrée avec age 
 +  [ ] Bitwarden : entrée "MIS SSH CA - Master Backup" avec attachments 
 +  * [ ] Passphrase age écrite sur papier dans coffre bureau MIS 
 +  * [ ] Passphrase age (copie) dans coffre backup (maison ou bancaire) 
 +  * [ ] Backup additionnel sur clé USB LUKS dans coffre 
 +  [ ] **Test de récupération validé** (Étape 2.7) avant rangement définitif
  
-### Phase 3 - Bootstrap parc +==== Phase 3 - Bootstrap parc ==== 
-[ ] Compte break-glass `mis-breakglasscréé et clé déployée partout +  [ ] Compte break-glass ''mis-breakglass'' créé et clé déployée partout (indépendant CA) 
-[ ] Playbook Ansible `mis-ssh-ca-deploy.ymltesté sur 1-2 serveurs +  [ ] Playbook Ansible ''mis-ssh-ca-deploy.yml'' testé sur 1-2 serveurs 
-- [ ] Déploiement progressif sur le parc complet +  * [ ] Fichier ''mis-users-ca-deploy.pub'' contient les 2 CAs (Yubikey + emergency) 
-- [ ] Certificats d'hôtes générés pour les serveurs critiques+  * [ ] Déploiement progressif sur le parc complet
  
-### Phase 4 - Techniciens +==== Phase 4 - Techniciens ==== 
-[ ] Premières Yubikeys enrôlées (toi en priorité) +  [ ] Premières Yubikeys enrôlées (toi en priorité) 
-[ ] Tests de connexion validés +  [ ] Tests de connexion validés 
-[ ] Configuration `~/.ssh/configdocumentée pour les techs+  [ ] Configuration ''~/.ssh/config'' documentée pour les techs
  
-### Phase 5+ - Industrialisation +==== Phase 5+ - Industrialisation ==== 
-[ ] Module Odoo `makeitsimple_ssh_ca(modèles + wizards) +  [ ] Module Odoo ''makeitsimple_ssh_ca'' (modèles + wizards) 
-[ ] Intégration Semaphore pour déploiement KRL +  [ ] Intégration Semaphore pour déploiement KRL 
-[ ] Cron de notification d'expiration +  [ ] Cron de notification d'expiration des certifs 
-[ ] Migration CA vers Yubikey PIV (à 6-12 mois)+  [ ] **Drill de récupération annuel** (cas 1 emergencytesté en staging
  
----+----
  
-*Document maintenu par Make IT Simple - dernière révision : 2026*+//Document maintenu par Make IT Simple - dernière révision : 2026//
linux/ca-ssh.1780492342.txt.gz · Dernière modification : de root