Skip to Content

Máquinas remotas via SSH

O --host executa um comando ironflock em outra máquina via SSH em vez de nesta. Com ele, você gerencia dispositivos a partir do seu notebook, inclusive de um Mac, sem instalar o ironflock neles. Informado mais de uma vez, ou com um arquivo de hosts, ele executa o comando em muitas máquinas ao mesmo tempo.

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

Todo comando exceto completion roda em outra máquina dessa forma. --host, --hosts e --parallel são flags globais: podem vir antes ou depois do nome do comando.

Uma máquina

O --host [user@]machine executa o mesmo comando com as mesmas flags nessa máquina. O que ele imprime volta como está, stdout para o stdout e stderr para o stderr, e o mesmo vale para o código de saída.

$ 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":[...]}

O que a outra máquina precisa ter

  • Linux em amd64, arm64 ou ARM de 32 bits (v5 a v7). Outro sistema termina com o código de saída 3.
  • Um servidor SSH que executa comandos. Não é preciso scp nem SFTP.
  • Um shell de login POSIX: sh, bash, dash ou zsh. csh, tcsh e fish terminam com o código de saída 3 e uma mensagem que diz isso.
  • Login como root, ou sudo, para os comandos que precisam de root (veja Root na outra máquina).

A máquina não precisa ter o ironflock instalado: cada execução leva o seu próprio binário.

Configurações de SSH

O --host usa o ssh desta máquina, então o seu ~/.ssh/config, as suas chaves, o agente SSH e os jump hosts funcionam como funcionam com o ssh. Ele desativa o que o comando não precisa: encaminhamento de agente e de X11, encaminhamentos de porta, um RemoteCommand e um LocalCommand.

O --host aceita [user@]machine e nada mais. Uma porta ou outras opções vão no ~/.ssh/config:

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

Com isso, ironflock --host device-17 status faz login como pi na porta 2222, e o ssh aceita a chave de host de um dispositivo que ele ainda não conhece. Um valor de --host que começa com - é recusado, assim como um que tenha outros caracteres além de letras, dígitos, ., _, - e o : e o % de um endereço IPv6.

Uma única conexão SSH atende a toda a execução quando o caminho do seu socket de controle é curto o bastante; o socket é criado na pasta temporária, senão em /tmp. Nesse caso, o ssh pede a senha uma vez. Caso contrário, cada etapa da execução abre a sua própria conexão, e o ssh pede a senha de novo em cada uma.

Um dispositivo configurado pelo ironflock agent install ou pelo IronFlockOS tem um usuário SSH para o proprietário do projeto, que faz login com a chave do arquivo .flock do dispositivo. Acesso SSH mostra o nome do usuário e como extrair a chave do arquivo.

O que acontece na máquina

  1. O binário. A máquina recebe o binário que roda aqui quando tem a mesma plataforma. Caso contrário, o ironflock baixa o binário para a plataforma da máquina de <download base>/cli/<os>/<arch>/<version>/ironflock e o verifica aqui antes de usá-lo: a sua assinatura (.asc) com a chave de release da IronFlock que este binário traz, e as suas informações de build, que precisam indicar o programa ironflock, a plataforma da máquina e a mesma revisão de código-fonte deste binário. O download é guardado na sua pasta de cache (~/.cache/ironflock no Linux, ~/Library/Caches/ironflock em um Mac) e verificado novamente a cada uso.
  2. A pasta. O binário vai para uma pasta nova que só o usuário de login pode ler (mktemp -d), em ${TMPDIR:-/tmp} na máquina, ou na pasta pessoal do usuário de login quando um programa não pode rodar na pasta temporária (um sistema de arquivos montado com noexec). Quando ele não pode rodar em nenhuma das duas, o comando termina com o código de saída 3 e indica as duas. Antes de executar qualquer coisa da pasta, a máquina verifica se a pasta e o binário pertencem ao usuário de login e não são links simbólicos, e se o binário é da versão e da plataforma esperadas.
  3. Os arquivos. Os arquivos indicados nas flags do comando vão junto (veja Arquivos indicados em flags).
  4. O comando. Ele roda como o usuário de login, ou via sudo (veja Root na outra máquina).
  5. A limpeza. Quando o comando termina, a máquina remove a pasta com o binário e as cópias: também depois de uma falha, de uma conexão perdida e dos sinais HUP, INT, QUIT, TERM, PIPE, USR1 e USR2.

O --host não deixa nenhum ironflock na máquina, também depois de agent install. Continue a partir daqui com --host, ou instale o ironflock na máquina com a linha de instalação, que sem argumentos apenas o instala. O self-update com --host atualiza esse /usr/local/bin/ironflock da máquina, não o binário da execução (ironflock self-update).

Uma pasta ironflock.* fica na máquina em dois casos:

  • Depois de uma queda de energia ou de um kill -9 na máquina enquanto o comando roda. A pasta contém o binário e as cópias.
  • Quando a conexão se perde depois da cópia e antes de o comando começar, ou quando a execução é interrompida aqui enquanto a etapa que inicia o comando se conecta. A pasta contém o binário e também as cópias, quando você executou o comando em um terminal.

A cópia de um arquivo .flock contém o segredo do dispositivo, a de um arquivo de senha de WiFi, a senha do WiFi, e a de um arquivo de senha de proxy, a senha do proxy. Só o usuário de login e o root podem lê-las. Remova uma pasta assim manualmente.

Arquivos indicados em flags

FlagComandos
-c, --config (o arquivo .flock)agent install, agent configure, doctor, os flash, os install, os reset
--wifi-password-fileos flash
--proxy-password-fileagent install, agent configure, os install

Cada arquivo é lido aqui, e um caminho relativo é relativo à pasta de trabalho desta máquina. Um arquivo de senha de WiFi ou de proxy pode ser um pipe (/dev/stdin), que é lido uma vez. O conteúdo vai para a máquina pela entrada padrão da conexão SSH. Nem o arquivo nem o seu tamanho ficam em uma linha de comando, aqui ou na máquina, onde todo usuário pode ler as linhas de comando. A máquina compara cada tamanho com o maior que o comando aceita e, depois, verifica se tudo chegou.

Na máquina, a cópia é <folder>/1/device.flock: em uma pasta própria (modo 0700) dentro da pasta da execução, com o nome que tem aqui e modo 0600. O comando lá recebe esse caminho, por exemplo --config=/tmp/ironflock.k3Xf9QpL2m/1/device.flock, de modo que um erro sobre o conteúdo do arquivo indica a cópia.

Estes casos encerram o comando nesta máquina:

  • Código de saída 1 — um arquivo que não é um arquivo regular (um arquivo de senha de WiFi ou de proxy também pode ser um pipe), ou um arquivo maior do que o comando aceita: 5 MiB para um arquivo .flock, 4096 bytes para um arquivo de senha de WiFi ou de proxy.
  • Código de saída 2 — um nome de arquivo com um caractere de controle.
  • Código de saída 2 — --image-file e --output de os flash. O --host não copia nenhuma imagem para a máquina e não traz nenhum arquivo de volta. Execute esse comando aqui sem --host, ou na própria máquina. Sem --image-file, a própria máquina baixa a imagem.

Root na outra máquina

  • Um comando que precisa de root roda via sudo quando o usuário de login não é root. Um sudo sem senha e sem terminal funciona em qualquer situação.
  • version, os images e os drives rodam como o usuário de login.
  • status e doctor leem mais com root. Eles rodam como root quando o usuário de login é root ou pode usar sudo sem senha e sem terminal. Caso contrário, rodam como o usuário de login, sem perguntar, e a dica deles indica o comando com --host root@machine.
  • agent logs lê um arquivo do root e roda via sudo.
  • Quando o sudo pede uma senha, ou a configuração dele exige um terminal (requiretty), ele precisa de um terminal na máquina. Para isso, é preciso um terminal no stdin, no stdout e no stderr aqui. Sem ele, o comando termina com o código de saída 3 antes que algo seja copiado. Nesse caso, faça login como root, permita que o usuário de login use sudo sem senha e sem terminal, ou execute o comando em um terminal sem redirecionamento.
  • Um sudo que recusa quando o comando está prestes a rodar também termina com o código de saída 3.

O que o comando lá diz para executar em seguida indica um comando para este lado, sem sudo, já que o --host obtém root sozinho: ironflock --host pi@device-17 agent start.

Terminal, perguntas e saída

  • Com um terminal aqui, o comando também recebe um na máquina, para poder fazer as suas perguntas. O stdout e o stderr dele voltam então juntos, no seu terminal.
  • Sem terminal aqui (um pipe, --json para um programa, CI), o stdout e o stderr ficam separados, e o comando não pergunta nada: --yes confirma. O que os arquivos do usuário de login na máquina imprimem, como um .bashrc que faz echo, é descartado do stdout, para que o stdout traga apenas o resultado do comando.
  • --yes, --json e as flags do comando vão junto. A base de download vai junto como --download-base: a flag, ou o IRONFLOCK_DL_BASE desta máquina. Uma base de download com credenciais (https://user:[email protected]/dl) termina com o código de saída 2 antes que qualquer máquina seja alcançada, pois na máquina ela ficaria em uma linha de comando que todo usuário ali pode ler.
  • --help, help, um grupo sem comando e completion são respondidos aqui: o que imprimem é o mesmo em toda máquina.
  • O que a máquina envia no stdout antes de o comando rodar (as suas respostas e o texto dos arquivos do usuário de login) é lido até 64 KiB. Mais do que isso encerra a execução com o código de saída 1 antes que o comando rode lá.

Ctrl-C e conexões perdidas

O Ctrl-C aqui chega ao comando lá como chegaria um Ctrl-C no próprio terminal dele, e uma conexão perdida chega a ele como um hangup, com e sem terminal aqui. Depois do Ctrl-C, o --host espera até 15 segundos para que a máquina diga que o comando lá terminou, com o seu código de saída (130 quando o Ctrl-C o encerrou). Quando a máquina não diz isso a tempo, ou a conexão termina antes, o --host termina com 130 e diz que o comando ainda pode estar rodando lá. Uma execução cujo comando ainda não tinha começado quando veio o Ctrl-C não o inicia.

O os install com --host tem regras próprias: depois que a sua pergunta é respondida, uma conexão perdida não o interrompe mais, e o comando espera aqui até que a máquina volte como IronFlockOS; veja ironflock os install.

Códigos de saída e erros

Os códigos de saída são os do comando (Códigos de saída). O --host acrescenta estes:

CódigoSignificado
255o ssh falhou: sem conexão, login ou chave de host recusados, uma conexão que caiu
3a máquina não pode executar o comando: outro sistema ou CPU, um shell de login que não é um shell POSIX, nenhuma pasta onde um programa possa rodar, um sudo que não permite o usuário de login
130uma interrupção, aqui (Ctrl-C) ou do comando na máquina (um hangup, uma interrupção ou um sinal de término lá)

Uma falha do próprio --host é um erro como qualquer outro: uma linha Error: no stderr ou, com --json, o objeto de erro no stdout. A única exceção: quando a saída do comando já tinha chegado, essa saída é o resultado, e o erro é apenas a linha Error: no stderr, também com --json.

Várias máquinas

O --host informado mais de uma vez, ou --hosts <file>, executa o comando em cada máquina, como o --host o executa em uma. As máquinas de --host vêm primeiro, depois as do arquivo. Um arquivo de hosts com uma só máquina roda como rodam várias máquinas.

$ 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)

O arquivo de hosts

O arquivo indica um [user@]machine por linha. Um # inicia um comentário, que vai até o fim da linha, e linhas vazias são ignoradas. O arquivo também pode ser um pipe, como --hosts <(...), e tem no máximo 1 MiB.

# Linha de embalagem 3 pi@device-17 pi@device-18 pi@device-19 # the spare unit
# Mostrar o estado de cada máquina do arquivo, 20 por vez ironflock --hosts line3.txt --parallel 20 status # Atualizar o agente em cada máquina do arquivo; o agent update pergunta, então --yes é necessário ironflock --hosts line3.txt agent update --yes # Dar aos dispositivos de um local uma única conta de proxy; a senha vem de um arquivo ironflock --hosts site-a.txt agent configure --https-proxy http://[email protected]:3128 --proxy-password-file proxy-password.txt --yes

Estes casos terminam com o código de saída 2 antes que algo rode, e o erro indica o arquivo e a linha:

  • uma linha com mais de uma palavra;
  • uma máquina que o --host recusaria;
  • uma máquina indicada duas vezes. Também conta como duas vezes a mesma máquina com dois usuários, pois o comando rodaria nela duas vezes ao mesmo tempo. Um endereço e um nome da mesma máquina não são reconhecidos como iguais.

Execuções paralelas

No máximo --parallel máquinas rodam ao mesmo tempo: 10 sem a flag, de 1 a 100. Elas começam na ordem informada, e a próxima começa quando uma termina. --parallel sem várias máquinas termina com o código de saída 2.

SSH sem perguntas

Nenhuma máquina recebe um terminal, e o ssh não pergunta nada (BatchMode=yes). Estes casos falham nessa máquina com o código de saída 255 e as palavras do próprio ssh, e as outras máquinas continuam:

  • um usuário de login que exige senha;
  • uma chave cuja senha não está no agente SSH;
  • uma chave de host que o ssh ainda não conhece.

Conecte-se uma vez com o ssh para aceitar uma nova chave de host, ou defina StrictHostKeyChecking accept-new para essas máquinas no ~/.ssh/config, como no exemplo acima. O ssh desiste de conectar após 30 segundos (ConnectTimeout=30, independentemente do que diga o ~/.ssh/config), então uma máquina desligada é informada nesse momento. Um sudo lá que exige senha ou terminal termina com o código de saída 3 nessa máquina; status e doctor rodam então como o usuário de login.

Saída de várias máquinas

  • Cada linha que uma máquina escreve chega inteira, com <host>: na frente: o stdout dela no stdout, o stderr dela no stderr. As linhas das máquinas se misturam na ordem em que chegam.
  • Uma última linha sem quebra de linha recebe uma. Uma linha com mais de 64 KiB chega em partes de 64 KiB, cada uma com o host na frente.
  • Caracteres de controle além do tab chegam escapados (\x1b, \r), nas linhas e no resumo, para que nenhuma máquina mova o cursor ou escreva por cima da linha de outra máquina. Um retorno de carro no fim de uma linha é descartado.
  • Uma execução que falha aqui acrescenta a sua linha Error: com o host na frente, como o --host a escreve para uma máquina.
  • No fim, o stderr recebe um resumo: o código de saída e quantas máquinas terminaram de cada forma, depois uma linha para cada máquina na ordem informada. A linha diz done, ou o código de saída, o seu tipo e o motivo: o erro aqui (para 255, com as palavras do próprio ssh) ou a linha Error: do comando. Uma máquina que não confirmou o fim do seu comando depois do Ctrl-C aparece como interrupted, may still run.

Código de saída com várias máquinas

O comando termina com o pior código de saída entre as máquinas: 0 quando todas as máquinas terminaram com 0. Caso contrário, a partir do pior:

  1. 130 — interrompido, aqui ou em uma máquina.
  2. 1 — o comando falhou em uma máquina, o que pode tê-la deixado parcialmente alterada, seja qual for o código com que terminou lá.
  3. 255 — uma máquina não foi alcançada, ou a conexão caiu.
  4. 3 — uma máquina não estava no estado de que o comando precisa, ou não pode executá-lo.
  5. 4 — uma pergunta foi respondida com não.

O código de saída 2 é uma linha de comando incorreta aqui, como em todo comando.

Linhas JSON

Com --json, o stdout recebe uma linha para cada máquina quando o comando dela termina, na ordem em que terminam, e nada mais. Um programa lê as linhas à medida que chegam.

{"host":"pi@vm1","state":"done","exit_code":0,"result":{...}}
CampoSignificado
hosta máquina como o --host ou o arquivo de hosts a indica
statedone: o comando terminou com 0. failed: terminou com outro código lá, ou a execução falhou aqui. interrupted: a execução foi interrompida, e a máquina disse que o comando terminou. may_still_run: a execução foi interrompida aqui, e a máquina não disse, dentro do tempo de espera, que o comando terminou. not_started: a execução foi interrompida antes de chegar a vez da máquina; nada rodou lá.
exit_codecomo o comando terminou na máquina, ou como a execução terminou aqui: 255 quando a máquina não foi alcançada ou a conexão caiu, 3 quando a máquina não pode executar o comando, 130 quando a execução foi interrompida. Outro código, como 137 depois de um kill, é o da máquina, sem alteração, com o estado failed.
resulto que o comando escreveu com --json, como a página do comando o descreve. Para agent logs, o array das suas linhas; no máximo 16 MiB são mantidos. Um comando que escreve o seu resultado e termina com 1 (doctor, agent update) também o tem.
errorpor que o comando não terminou com 0, com kind e message: o erro da execução aqui (tipo ssh para uma máquina que não foi alcançada, com a mensagem terminando com as palavras do ssh entre parênteses), senão o objeto de erro do comando, senão um objeto montado a partir do código de saída dele e da sua última linha no stderr
outputo que o comando escreveu no stdout que não é JSON; ou, quando escreveu mais de 16 MiB, uma frase que diz isso

O stderr tem as linhas das máquinas, entre elas a linha Error: própria de um comando que a escreve com --json (agent update), e o resumo. Os erros das execuções aqui estão apenas nas linhas do stdout.

# As máquinas em que o status não terminou com 0 ironflock --hosts line3.txt status --json 2>/dev/null | jq -r 'select(.state != "done") | .host'

Ctrl-C em várias máquinas

Ctrl-C, SIGTERM ou SIGHUP encerra as execuções como encerra uma execução em uma máquina: cada comando lá o recebe, e cada máquina tem 15 segundos para confirmar que o seu comando terminou. Uma máquina que não confirma fica como may_still_run. As máquinas cuja vez ainda não chegou não executam nada. O resumo as lista como not started, e o comando termina com 130.

Quais comandos rodam em várias máquinas

ComandosEm várias máquinas
version, status, doctor, os images, os drives, agent restart, agent startsempre
agent logssempre; com --follow, não com --json, pois a linha de uma máquina chega quando o comando dela termina, e o --follow não termina
agent stop, agent update, agent configure, agent remove, os update, os rollback, self-updatecom --yes: eles perguntam, e em várias máquinas ao mesmo tempo ninguém é perguntado
completionrespondido aqui e em nenhuma máquina, como o --help
agent install, os install, os flash, os resetnunca: o arquivo .flock deles indica um único dispositivo, ou eles apagam um disco, uma unidade ou os dados de um dispositivo depois de uma pergunta que indica a única máquina

Uma flag que indica um arquivo nesta máquina é recusada com várias máquinas, pois o arquivo iria para todas elas: o -c de doctor e de agent configure indica um único dispositivo. A exceção é o --proxy-password-file de agent configure, para uma única conta de proxy para as máquinas de um local.

Uma linha de comando que não roda em várias máquinas termina com o código de saída 2 e diz por quê, antes que o arquivo de hosts seja lido e antes que qualquer máquina seja alcançada.

Last updated on