Wrapper Crystal isolé autour du binaire ssh système, pour les
outils de provisioning et d’automatisation qui doivent ignorer
complètement la configuration utilisateur (~/.ssh/config,
~/.ssh/known_hosts, ssh-agent).
Un outil de provisioning qui invoque ssh hérite par défaut de tout
l’environnement utilisateur : fichier config, known_hosts, agent,
clés par défaut. Cela rend l’exécution non reproductible entre
postes, et pollue les sessions interactives (un reboot en rescue fait
changer la clé d’hôte → known_hosts cassé, prompt fingerprint).
Le shard ssh impose à chaque connexion un contexte totalement
isolé :
-
-F /dev/null: ignore~/.ssh/config -
UserKnownHostsFile=/dev/null+GlobalKnownHostsFile=/dev/null: pas deknown_hostslu ni écrit -
IdentityAgent=none: ignoressh-agent -
IdentitiesOnly=yes: n’essaie que la clé passée en-i -
BatchMode=yes: aucun prompt interactif (pas de mot de passe, pas de confirmation de fingerprint) -
StrictHostKeyChecking=no: les changements de clé d’hôte ne bloquent pas — adapté aux flows rescue/bootstrap -
ControlMaster=auto+ControlPath=/tmp/ssh-%C-%i
ControlPersist=10m: multiplexage natif OpenSSH, le 1er ssh ouvre un master, les exec suivants vers la même cible se branchent dessus via un socket UNIX local. Évite le ré-handshake TCP+TLS+auth à chaque commande (gain ~10× sur des flows à 50+ exec, typeberyl applyqui déroule 28 recettes).
Conséquence : deux postes avec des configurations ssh différentes
exécuteront le même code de la même manière, et beryl ne pollue
plus votre ~/.ssh/known_hosts avec des clés jetables. Et
l’enchaînement de commandes vers le même hôte est rapide grâce au
multiplexage.
require "ssh"
# Découvre la clé privée par convention Aloli :
# "philippe-aloli-fr" → ~/.ssh/philippe.aloli.fr.key
store = SSH::KeyStore.new
key = store.path_for!("philippe-aloli-fr")
conn = SSH::Connection.new(
host: "ns3156789.ip-51-83-6.eu",
user: "root",
identity_file: key,
)
result = conn.exec("uname -a")
puts result.stdoutEn cas d’échec de l’auth publickey, BatchMode=yes garantit un
retour d’erreur immédiat — pas de prompt qui fige un flow
automatisé.
SSH::KeyStore découvre les clés dans un dossier (par défaut
~/.ssh) avec la convention Aloli :
-
clé privée :
<name>.key(pas<name>ni<name>.pem) -
clé publique :
<name>.pub
Le <name> peut contenir des points (ex: philippe.aloli.fr). Le
path_for("philippe-aloli-fr") teste le nom tel quel puis avec
- → . pour matcher la convention de fichiers.
Avantage du suffixe .key : un .gitignore générique *.key exclut
toutes les clés privées, contrairement à des conventions sans
extension.
StrictHostKeyChecking=no laisse théoriquement la porte à un MITM
entre le poste et l’hôte. Ce shard cible des outils de provisioning
qui parlent à des rescue ou des images fraîchement installées —
contextes où la clé d’hôte n’est pas stable de toute façon. Pour les
connexions interactives où la clé d’hôte doit être vérifiée, utilisez
ssh directement, pas ce shard.