Pipe stdin → SSH → psql : pourquoi et comment¶
Le pattern¶
echo "SQL ICI" | ssh root@serveur "docker exec -i container psql -U user -d db"
Deux étapes liées par | (pipe) :
echo "..."— génère le SQL comme texte brut sur la sortie standard|— redirige cette sortie vers l'entrée standard (stdin) de la commande suivantessh root@serveur "..."— se connecte au VPS et y exécute une commandedocker exec -i container— exécute dans le container (-i= lit depuis stdin)psql -U user -d db— reçoit le SQL depuis stdin et l'exécute
Pourquoi pas -c "SQL" via SSH ?¶
Le shell interprète les $ comme des variables avant d'envoyer la commande.
# DANGEREUX — $2b$12$abc devient "b2abc" (variables vides)
ssh root@vps "docker exec postgres psql -c \"UPDATE t SET pwd='$2b$12$abc'\""
# SÛR — le $ passe tel quel, aucune interprétation
echo "UPDATE t SET pwd='\$2b\$12\$abc'" | ssh root@vps "docker exec -i postgres psql"
Cas concret : les hash bcrypt ($2b$12$...) sont détruits par le shell si passés en argument. Le pipe stdin les transmet intacts.
Guillemets dans le SQL¶
Dans echo "...", les guillemets doubles intérieurs doivent être échappés avec \" :
# Colonne PostgreSQL avec guillemets doubles (casse sensible)
echo "UPDATE utilisateurs SET \"idRole\" = 'role_admin' WHERE email = 'x@y.com';"
# ^^^^^^^^ ← échappé car déjà dans "..."
Les guillemets simples 'valeur' n'ont pas besoin d'être échappés dans echo "...".
Variante : fichier temporaire (pour SQL long)¶
# Écrire le SQL dans un fichier
cat > /tmp/query.sql << 'EOF'
UPDATE utilisateurs
SET "idRole" = 'role_super_admin',
"motDePasse" = '$2b$12$abc...',
"updatedAt" = NOW()
WHERE email = 'user@domaine.com';
EOF
# Exécuter via pipe
cat /tmp/query.sql | ssh root@vps "docker exec -i shared-postgres psql -U fichepaie_user -d gestionFichePaie"
Le heredoc << 'EOF' (avec guillemets autour de EOF) désactive aussi l'interprétation des $.
Pourquoi $2b devient vide — explication détaillée¶
Le shell Unix interprète tout $mot comme une variable à substituer.
Si la variable n'existe pas, elle est remplacée par une chaîne vide — sans erreur, silencieusement.
NOM="Bernard"
echo "Bonjour $NOM" # → Bonjour Bernard (variable existante)
echo "Bonjour $TOTO" # → Bonjour (variable inexistante = vide)
echo "Bonjour $2b" # → Bonjour (idem — $2b n'existe pas)
Un hash bcrypt ressemble exactement à des variables shell :
$2b$12$rwmRd6YnxHabhp2i.wXNbOYo
^^^ ^^^
| └── variable $12 → vide
└──────── variable $2b → vide
Résultat après interprétation du shell :
# Hash original
$2b$12$rwmRd6YnxHabhp2i.wXNbOYo
# Après substitution des variables ($2b=vide, $12=vide)
rwmRd6YnxHabhp2i.wXNbOYo
Le hash est corrompu, bcrypt.compare() retourne false, la connexion échoue — sans aucun message d'erreur explicite.
La seule protection : ne jamais laisser le shell interpréter le SQL contenant un hash. Le pipe stdin court-circuite cette interprétation car le texte est transmis directement au processus destinataire.
Récapitulatif des règles¶
| Situation | Méthode | Raison |
|---|---|---|
SQL simple sans $ |
-c "SQL" ou pipe |
Les deux marchent |
SQL avec hash bcrypt ($2b$...) |
Pipe stdin obligatoire | Le shell mange les $ |
| SQL long ou multi-lignes | Fichier temp + pipe | Plus lisible |
| SQL généré par Node.js | execSync(cmd, { input: sql }) |
Pipe programmatique |