Skip to Content

Machines distantes via SSH

--host exécute une commande ironflock sur une autre machine via SSH au lieu de celle-ci. Il vous sert à gérer des appareils depuis votre ordinateur portable, Mac compris, sans y installer ironflock. Donné plusieurs fois, ou avec un fichier d’hôtes, il exécute la commande sur plusieurs machines à la fois.

ironflock --host [user@]machine <command> [flags] ironflock --host [user@]machine --host [user@]machine ... <command> [flags] ironflock --hosts <file> [--parallel <n>] <command> [flags]

Toutes les commandes sauf completion s’exécutent ainsi sur une autre machine. --host, --hosts et --parallel sont des options globales : elles peuvent se placer avant ou après le nom de la commande.

Une machine

--host [user@]machine exécute la même commande avec les mêmes options sur cette machine. Ce qu’elle affiche revient tel quel, stdout sur stdout et stderr sur stderr, de même que son code de sortie.

$ ironflock --host pi@device-17 version ironflock 2.0.2 linux/arm64 $ ironflock --host pi@device-17 agent remove --yes --json 2>/dev/null {"action":"removed","removed":[...]}

Ce dont l’autre machine a besoin

  • Linux sur amd64, arm64 ou ARM 32 bits (v5 à v7). Sur un autre système, la commande se termine avec le code de sortie 3.
  • Un serveur SSH qui exécute des commandes. Ni scp ni SFTP ne sont nécessaires.
  • Un shell de connexion POSIX : sh, bash, dash ou zsh. Avec csh, tcsh et fish, la commande se termine avec le code de sortie 3 et un message qui le dit.
  • Un compte de connexion root, ou sudo, pour les commandes qui nécessitent root (voir Root sur l’autre machine).

La machine n’a pas besoin qu’ironflock y soit installé : chaque exécution apporte son propre binaire.

Paramètres SSH

--host utilise le ssh de cette machine : votre ~/.ssh/config, vos clés, l’agent SSH et les hôtes de rebond (jump hosts) fonctionnent comme avec ssh. Il désactive ce dont la commande n’a pas besoin : le transfert de l’agent et de X11, les redirections de ports, une RemoteCommand et une LocalCommand.

--host accepte [user@]machine et rien d’autre. Un port ou d’autres options SSH se mettent dans ~/.ssh/config :

Host device-* User pi Port 2222 StrictHostKeyChecking accept-new

Avec ceci, ironflock --host device-17 status se connecte en tant que pi sur le port 2222, et ssh accepte la clé d’hôte d’un appareil qu’il ne connaît pas encore. Une valeur de --host qui commence par - est refusée, tout comme une valeur qui contient d’autres caractères que des lettres, des chiffres, ., _, -, et les : et % d’une adresse IPv6.

Une seule connexion SSH sert à toute l’exécution lorsque le chemin de son socket de contrôle est assez court ; le socket est créé dans le dossier temporaire, sinon dans /tmp. ssh ne demande alors le mot de passe qu’une fois. Sinon, chaque étape de l’exécution ouvre sa propre connexion, et ssh le redemande pour chacune.

Un appareil configuré par ironflock agent install ou par IronFlockOS a un utilisateur SSH pour le propriétaire du projet, qui se connecte avec la clé du fichier .flock de l’appareil. Accès SSH indique le nom d’utilisateur et comment extraire la clé du fichier.

Ce qui se passe sur la machine

  1. Le binaire. La machine reçoit le binaire qui s’exécute ici lorsqu’elle a la même plateforme. Sinon, ironflock télécharge le binaire pour la plateforme de la machine depuis <download base>/cli/<os>/<arch>/<version>/ironflock et le vérifie ici avant de l’utiliser : sa signature (.asc) avec la clé de publication d’IronFlock que contient ce binaire, et ses informations de build, qui doivent nommer le programme ironflock, la plateforme de la machine et la même révision source que ce binaire. Le téléchargement est conservé dans votre dossier de cache (~/.cache/ironflock sous Linux, ~/Library/Caches/ironflock sur un Mac) et revérifié à chaque utilisation.
  2. Le dossier. Le binaire va dans un nouveau dossier que seul le compte de connexion peut lire (mktemp -d), sous ${TMPDIR:-/tmp} sur la machine, ou sous le répertoire personnel du compte lorsqu’un programme ne peut pas s’exécuter dans le dossier temporaire (un système de fichiers monté noexec). Lorsqu’il ne peut s’exécuter dans aucun des deux, la commande se termine avec le code de sortie 3 et nomme les deux. Avant d’exécuter quoi que ce soit depuis le dossier, la machine vérifie que le dossier et le binaire appartiennent au compte de connexion et ne sont pas des liens symboliques, et que le binaire a la version et la plateforme attendues.
  3. Les fichiers. Les fichiers désignés dans les options de la commande sont transmis (voir Fichiers désignés dans les options).
  4. La commande. Elle s’exécute sous le compte de connexion, ou via sudo (voir Root sur l’autre machine).
  5. Le nettoyage. Lorsque la commande se termine, la machine supprime le dossier avec le binaire et les copies : y compris après un échec, après une connexion perdue, et après les signaux HUP, INT, QUIT, TERM, PIPE, USR1 et USR2.

--host ne laisse aucun ironflock sur la machine, même après agent install. Continuez ensuite avec --host, ou installez ironflock sur la machine avec la ligne d’installation, qui, sans argument, se contente de l’installer. self-update via --host met à jour ce /usr/local/bin/ironflock de la machine, et non le binaire de l’exécution (ironflock self-update).

Un dossier ironflock.* reste sur la machine dans deux cas :

  • Après une coupure de courant ou un kill -9 sur la machine pendant que la commande s’exécute. Le dossier contient le binaire et les copies.
  • Lorsque la connexion est perdue après la copie et avant le démarrage de la commande, ou que l’exécution est interrompue ici pendant que l’étape qui démarre la commande établit la connexion. Le dossier contient le binaire, et aussi les copies si vous avez exécuté la commande dans un terminal.

Une copie d’un fichier .flock contient le secret de l’appareil, une copie d’un fichier de phrase secrète la phrase secrète WiFi, une copie d’un fichier de mot de passe le mot de passe du proxy. Seuls le compte de connexion et root peuvent les lire. Supprimez un tel dossier à la main.

Fichiers désignés dans les options

OptionCommandes
-c, --config (le fichier .flock)agent install, agent configure, doctor, os flash, os install, os reset
--wifi-password-fileos flash
--proxy-password-fileagent install, agent configure, os install

Chaque fichier est lu ici, et un chemin relatif est relatif au dossier de travail de cette machine. Un fichier de phrase secrète ou de mot de passe peut être un pipe (/dev/stdin), lu une seule fois. Le contenu est envoyé à la machine sur l’entrée standard de la connexion SSH. Ni un fichier ni sa taille ne figurent sur une ligne de commande, ni ici ni sur la machine, où chaque utilisateur peut lire les lignes de commande. La machine vérifie chaque taille par rapport au maximum qu’accepte la commande, puis que tout est arrivé.

Sur la machine, la copie est <folder>/1/device.flock : dans un dossier à part (mode 0700) à l’intérieur du dossier de l’exécution, avec le nom qu’elle porte ici et le mode 0600. La commande y reçoit ce chemin, par exemple --config=/tmp/ironflock.k3Xf9QpL2m/1/device.flock, si bien qu’une erreur qui porte sur le contenu du fichier nomme la copie.

Les cas suivants mettent fin à la commande sur cette machine :

  • Code de sortie 1 — un fichier qui n’est pas un fichier ordinaire (un fichier de phrase secrète ou de mot de passe peut aussi être un pipe), ou un fichier plus grand que ce qu’accepte la commande : 5 Mio pour un fichier .flock, 4096 octets pour un fichier de phrase secrète ou de mot de passe.
  • Code de sortie 2 — un nom de fichier qui contient un caractère de contrôle.
  • Code de sortie 2 — --image-file et --output de os flash. --host ne copie aucune image sur la machine et ne rapporte aucun fichier. Exécutez cette commande ici sans --host, ou sur la machine elle-même. Sans --image-file, la machine télécharge l’image elle-même.

Root sur l’autre machine

  • Une commande qui nécessite root s’exécute via sudo lorsque le compte de connexion n’est pas root. sudo sans mot de passe et sans terminal fonctionne partout.
  • version, os images et os drives s’exécutent sous le compte de connexion.
  • status et doctor lisent davantage avec root. Ils s’exécutent en tant que root lorsque le compte de connexion est root ou peut utiliser sudo sans mot de passe et sans terminal. Sinon, ils s’exécutent sous le compte de connexion, sans rien demander, et leur conseil indique la commande avec --host root@machine.
  • agent logs lit un fichier qui appartient à root et s’exécute via sudo.
  • Lorsque sudo demande un mot de passe, ou que sa configuration exige un terminal (requiretty), il a besoin d’un terminal sur la machine. Il faut pour cela un terminal sur stdin, stdout et stderr ici. Sans terminal, la commande se termine avec le code de sortie 3 avant que quoi que ce soit ne soit copié. Connectez-vous alors en root, autorisez le compte à utiliser sudo sans mot de passe et sans terminal, ou exécutez la commande dans un terminal sans redirection.
  • Si sudo refuse au moment où la commande va s’exécuter, elle se termine aussi avec le code de sortie 3.

Lorsque la commande indique là-bas quoi exécuter ensuite, elle nomme une commande pour ce côté-ci, sans sudo, car --host obtient root lui-même : ironflock --host pi@device-17 agent start.

Terminal, questions et sortie

  • Avec un terminal ici, la commande en reçoit un aussi sur la machine, pour pouvoir poser ses questions. Son stdout et son stderr reviennent alors ensemble, dans votre terminal.
  • Sans terminal ici (un pipe, --json vers un programme, la CI), stdout et stderr restent séparés, et la commande ne pose aucune question : --yes confirme. Ce qu’affichent les fichiers du compte sur la machine, comme un .bashrc qui fait un echo, est retiré de stdout, afin que stdout ne transporte que le résultat de la commande.
  • --yes, --json et les options de la commande sont transmis. La base de téléchargement est transmise sous la forme de --download-base : l’option, ou la variable IRONFLOCK_DL_BASE de cette machine. Avec une base de téléchargement qui contient des identifiants (https://user:[email protected]/dl), la commande se termine avec le code de sortie 2 avant qu’aucune machine ne soit jointe, car sur la machine elle figurerait sur une ligne de commande que chaque utilisateur peut y lire.
  • --help, help, un groupe sans commande et completion obtiennent leur réponse ici : ce qu’ils affichent est identique sur chaque machine.
  • Ce que la machine envoie sur stdout avant l’exécution de la commande (ses réponses et le texte des fichiers du compte) est lu jusqu’à 64 Kio. Au-delà, l’exécution se termine avec le code de sortie 1 avant que la commande ne s’y exécute.

Ctrl-C et connexions perdues

Ctrl-C ici atteint la commande là-bas comme le ferait un Ctrl-C dans son propre terminal, et une connexion perdue l’atteint comme un raccrochage (hangup), avec ou sans terminal ici. Après Ctrl-C, --host attend jusqu’à 15 secondes que la machine indique que la commande s’est terminée là-bas, avec son code de sortie (130 lorsque c’est Ctrl-C qui l’a terminée). Si la machine ne le fait pas à temps, ou si la connexion se termine avant, --host se termine avec 130 et indique que la commande s’exécute peut-être encore là-bas. Une exécution dont la commande n’avait pas démarré à l’arrivée du Ctrl-C ne la démarre pas.

os install via --host a ses propres règles : une fois la réponse donnée à sa question, une connexion perdue ne l’arrête plus, et la commande attend ici que la machine revienne sous IronFlockOS ; voir ironflock os install.

Codes de sortie et erreurs

Les codes de sortie sont ceux de la commande (Codes de sortie). --host ajoute ceux-ci :

CodeSignification
255ssh a échoué : pas de connexion, compte de connexion ou clé d’hôte refusés, connexion coupée
3la machine ne peut pas exécuter la commande : autre système ou processeur, shell de connexion qui n’est pas un shell POSIX, aucun dossier où un programme peut s’exécuter, sudo qui n’autorise pas le compte de connexion
130une interruption, ici (Ctrl-C) ou de la commande sur la machine (raccrochage, interruption ou signal de terminaison là-bas)

Un échec de --host lui-même est une erreur comme une autre : une ligne Error: sur stderr ou, avec --json, l’objet d’erreur sur stdout. Une seule exception : lorsque la sortie de la commande était déjà arrivée, cette sortie est le résultat, et l’erreur se limite à la ligne Error: sur stderr, même avec --json.

Plusieurs machines

--host donné plusieurs fois, ou --hosts <file>, exécute la commande sur chaque machine, comme --host l’exécute sur une seule. Les machines de --host passent en premier, puis celles du fichier. Un fichier d’hôtes qui ne contient qu’une machine s’exécute comme plusieurs machines.

$ ironflock --host pi@vm1 --host pi@vm2 --host pi@vm3 status pi@vm2: Agent: installed in /opt/ironflock/device-agent pi@vm2: Version: 1.6.9 pi@vm2: Service: running (ironflock-device-agent.service) pi@vm3: ssh: connect to host vm3 port 22: Connection timed out pi@vm3: Error: the SSH connection to pi@vm3 failed pi@vm1: Agent: installed in /opt/ironflock/device-agent ... Summary of "status" on 3 machines, exit code 255: 2 done, 1 ssh. pi@vm1 done pi@vm2 done pi@vm3 255 ssh: the SSH connection to pi@vm3 failed (ssh: connect to host vm3 port 22: Connection timed out)

Le fichier d’hôtes

Le fichier nomme un [user@]machine par ligne. Un # commence un commentaire, qui court jusqu’à la fin de la ligne, et les lignes vides sont ignorées. Le fichier peut aussi être un pipe, comme --hosts <(...), et fait au plus 1 Mio.

# Ligne de conditionnement 3 pi@device-17 pi@device-18 pi@device-19 # the spare unit
# Afficher l'état de chaque machine du fichier, 20 à la fois ironflock --hosts line3.txt --parallel 20 status # Mettre à jour l'agent sur chaque machine du fichier ; agent update pose une question, --yes est donc nécessaire ironflock --hosts line3.txt agent update --yes # Donner aux appareils d'un site un même compte proxy ; le mot de passe provient d'un fichier ironflock --hosts site-a.txt agent configure --https-proxy http://[email protected]:3128 --proxy-password-file proxy-password.txt --yes

Les cas suivants se terminent avec le code de sortie 2 avant toute exécution, et l’erreur nomme le fichier et la ligne :

  • une ligne qui contient plus d’un mot ;
  • une machine que --host refuserait ;
  • une machine nommée deux fois. C’est aussi le cas de la même machine avec deux utilisateurs, car la commande s’y exécuterait deux fois en même temps. Une adresse et un nom d’une même machine ne sont pas reconnus comme identiques.

Exécutions parallèles

Au plus --parallel machines s’exécutent en même temps : 10 sans l’option, de 1 à 100. Elles démarrent dans l’ordre donné, et la suivante démarre dès qu’une autre se termine. --parallel sans plusieurs machines se termine avec le code de sortie 2.

SSH sans questions

Aucune machine ne reçoit de terminal, et ssh ne pose aucune question (BatchMode=yes). Les cas suivants échouent sur la machine concernée avec le code de sortie 255 et les propres mots de ssh, et les autres machines continuent :

  • un compte de connexion qui exige un mot de passe ;
  • une clé dont la phrase secrète n’est pas dans l’agent SSH ;
  • une clé d’hôte que ssh ne connaît pas encore.

Connectez-vous une fois avec ssh pour accepter une nouvelle clé d’hôte, ou définissez StrictHostKeyChecking accept-new pour ces machines dans ~/.ssh/config, comme dans l’exemple ci-dessus. ssh renonce à se connecter au bout de 30 secondes (ConnectTimeout=30, quoi qu’en dise ~/.ssh/config) : une machine éteinte est donc signalée à ce moment-là. Un sudo qui y exige un mot de passe ou un terminal se termine avec le code de sortie 3 sur cette machine ; status et doctor s’exécutent alors sous le compte de connexion.

Sortie de plusieurs machines

  • Chaque ligne qu’écrit une machine arrive entière, précédée de <host>: : son stdout sur stdout, son stderr sur stderr. Les lignes des machines se mêlent dans l’ordre où elles arrivent.
  • Une dernière ligne sans saut de ligne en reçoit un. Une ligne de plus de 64 Kio arrive en morceaux de 64 Kio, chacun précédé de l’hôte.
  • Les caractères de contrôle autres que la tabulation arrivent échappés (\x1b, \r), dans les lignes et dans le récapitulatif, afin qu’aucune machine ne déplace le curseur ni n’écrase la ligne d’une autre. Un retour chariot en fin de ligne est supprimé.
  • Une exécution qui échoue ici ajoute sa ligne Error: précédée de l’hôte, comme --host l’écrit pour une seule machine.
  • À la fin, stderr reçoit un récapitulatif : le code de sortie et le nombre de machines par issue, puis une ligne par machine dans l’ordre donné. La ligne indique done, ou le code de sortie, son type et la raison : l’erreur d’ici (pour 255, avec les propres mots de ssh) ou la ligne Error: de la commande. Une machine qui n’a pas confirmé la fin de sa commande après Ctrl-C est interrupted, may still run.

Code de sortie de plusieurs machines

La commande se termine avec le pire code de sortie des machines : 0 lorsque chaque machine s’est terminée avec 0. Sinon, du plus grave au moins grave :

  1. 130 — interruption, ici ou sur une machine.
  2. 1 — la commande a échoué sur une machine et l’a peut-être laissée à moitié modifiée, quel que soit le code avec lequel elle s’y est terminée.
  3. 255 — une machine n’a pas été jointe, ou la connexion a été coupée.
  4. 3 — une machine n’était pas dans l’état dont la commande a besoin, ou ne peut pas l’exécuter.
  5. 4 — une question a reçu la réponse non.

Le code de sortie 2 signale une ligne de commande erronée ici, comme pour chaque commande.

Lignes JSON

Avec --json, stdout reçoit une ligne par machine lorsque sa commande s’est terminée, dans l’ordre où elles se terminent, et rien d’autre. Un programme lit les lignes au fur et à mesure qu’elles arrivent.

{"host":"pi@vm1","state":"done","exit_code":0,"result":{...}}
ChampSignification
hostla machine telle que la nomme --host ou le fichier d’hôtes
statedone : la commande s’est terminée avec 0. failed : elle s’est terminée avec un autre code là-bas, ou son exécution a échoué ici. interrupted : l’exécution a été interrompue, et la machine a indiqué que la commande s’est terminée. may_still_run : l’exécution a été interrompue ici, et la machine n’a pas indiqué dans le délai d’attente que la commande s’est terminée. not_started : l’exécution a été interrompue avant que vienne le tour de la machine ; rien ne s’y est exécuté.
exit_codecomment la commande s’est terminée sur la machine, ou comment son exécution s’est terminée ici : 255 lorsque la machine n’a pas été jointe ou que la connexion a été coupée, 3 lorsque la machine ne peut pas exécuter la commande, 130 lorsque l’exécution a été interrompue. Un autre code, comme 137 après un kill, est celui de la machine tel quel, avec l’état failed.
resultce que la commande a écrit avec --json, tel que le décrit la page de la commande. Pour agent logs, le tableau de ses lignes ; au plus 16 Mio sont conservés. Une commande qui écrit son résultat et se termine avec 1 (doctor, agent update) l’a aussi.
errorpourquoi la commande ne s’est pas terminée avec 0, avec kind et message : l’erreur de l’exécution ici (type ssh pour une machine qui n’a pas été jointe, le message se terminant par les mots de ssh entre parenthèses), sinon l’objet d’erreur de la commande, sinon un objet formé de son code de sortie et de sa dernière ligne sur stderr
outputce que la commande a écrit sur stdout et qui n’est pas du JSON ; ou, lorsqu’elle a écrit plus de 16 Mio, une phrase qui le dit

stderr contient les lignes des machines, dont la propre ligne Error: d’une commande lorsqu’elle en écrit une avec --json (agent update), ainsi que le récapitulatif. Les erreurs des exécutions d’ici figurent uniquement dans les lignes sur stdout.

# Les machines sur lesquelles status ne s'est pas terminé avec 0 ironflock --hosts line3.txt status --json 2>/dev/null | jq -r 'select(.state != "done") | .host'

Ctrl-C sur plusieurs machines

Ctrl-C, SIGTERM ou SIGHUP met fin aux exécutions comme à une exécution sur une seule machine : chaque commande là-bas le reçoit, et chaque machine a 15 secondes pour confirmer que sa commande s’est terminée. Une machine qui ne le fait pas est may_still_run. Les machines dont le tour n’est pas venu n’exécutent rien. Le récapitulatif les liste comme not started, et la commande se termine avec 130.

Commandes exécutables sur plusieurs machines

CommandesSur plusieurs machines
version, status, doctor, os images, os drives, agent restart, agent starttoujours
agent logstoujours ; avec --follow, pas avec --json, car la ligne d’une machine arrive quand sa commande se termine, et --follow ne se termine pas
agent stop, agent update, agent configure, agent remove, os update, os rollback, self-updateavec --yes : elles posent une question, et sur plusieurs machines à la fois personne n’est interrogé
completionréponse donnée ici et sur aucune machine, comme pour --help
agent install, os install, os flash, os resetjamais : leur fichier .flock désigne un seul appareil, ou elles effacent un disque, un lecteur ou les données d’un appareil après une question qui nomme la machine concernée

Une option qui désigne un fichier sur cette machine est refusée avec plusieurs machines, car le fichier irait à chaque machine : -c de doctor et de agent configure désigne un seul appareil. L’exception est --proxy-password-file de agent configure, qui permet un même compte proxy pour les machines d’un site.

Une ligne de commande qui ne s’exécute pas sur plusieurs machines se termine avec le code de sortie 2 et dit pourquoi, avant que le fichier d’hôtes ne soit lu et avant qu’aucune machine ne soit jointe.

Last updated on