Como os argumentos da linha de comando remoto ssh são analisados

11

Eu já vi as perguntas e respostas sobre a necessidade de escape duplo dos argumentos para os comandos ssh remotos. Minha pergunta é: exatamente onde e quando é feita a segunda análise?

Se eu executar o seguinte:

$ ssh otherhost pstree -a -p

Vejo o seguinte na saída:

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p

O processo pai do comando remoto ( pstree) é sshdque não parece haver nenhum shell que analise os argumentos da linha de comando no comando remoto, portanto, não parece que seja necessário aspas duplas ou escape ( mas definitivamente é). Se, em vez disso, eu colocar o ssh lá primeiro e obter um shell de login e, em seguida, execute pstree -a -p, vejo o seguinte na saída:

  ├─sshd,3736
     └─sshd,3733
         └─sshd,3735
             └─bash,3737
                 └─pstree,4130 -a -p

Então, claramente, existe um bashshell que faria a análise da linha de comando nesse caso. Mas no caso em que eu uso diretamente um comando remoto, não parece haver um shell, então por que é necessário citar duas vezes?

onlynone
fonte

Respostas:

22

Sempre há um shell remoto. No protocolo SSH, o cliente envia ao servidor uma sequência para executar. O cliente de linha de comando SSH pega seus argumentos de linha de comando e os concatena com um espaço entre os argumentos. O servidor pega essa string, executa o shell de login do usuário e passa essa string.

É impossível ignorar o shell remoto. O protocolo não tem nada como enviar uma matriz de seqüências de caracteres que podem ser analisadas como uma matriz argv no servidor. E o servidor SSH não ignorará o shell remoto, porque isso poderia ser uma restrição de segurança: usar um programa restrito como o shell do usuário é uma maneira de fornecer uma conta restrita que só pode executar determinados comandos (por exemplo, uma conta somente rsync ou uma conta somente git).

Você pode não ver o shell pstreeporque já pode ter desaparecido. Muitos shells têm uma otimização em que, se detectarem que estão prestes a executar "execute este comando externo, espere que ele seja concluído e saia com o status do comando", o shell executará " execvedeste comando externo". É isso que está acontecendo no seu primeiro exemplo. Compare os três comandos a seguir:

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'

Os dois primeiros são idênticos: o cliente envia exatamente os mesmos dados para o servidor. O terceiro envia um comando do shell que derrota a otimização de exec do shell.

Gilles 'SO- parar de ser mau'
fonte
2
ha! Não posso acreditar que você me venceu em responder minha própria pergunta. Eu descobri isso no meio da postagem da pergunta e achei que deveria continuar perguntando e respondendo.
onlynone
10

Eu acho que descobri:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001

Os argumentos para pstreesão: mostrar argumentos da linha de comando, mostrar pids e mostrar apenas os processos pai do pid especificado. A '$$'é uma variável de shell especial que o bash substituirá por seu próprio pid quando o bash avaliar os argumentos da linha de comando. É citado uma vez para impedir que seja interpretado pelo meu shell local. Mas não é duplamente citado ou escapado para permitir que ele seja interpretado pelo shell remoto.

Como podemos ver, é substituído 12001por esse é o pid do shell. Também podemos ver pela saída: pstree,12001que o processo com um pid de 12001 é o próprio pstree. Então pstreeé a concha?

O que eu entendo está acontecendo lá é que bashestá sendo invocado e está analisando os argumentos da linha de comando, mas depois ele invoca execpara substituir a si próprio pelo comando que está sendo executado.

Parece que só faz isso no caso de um único comando remoto:

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi

Nesse caso, estou solicitando a execução de dois comandos: pstreeseguido por echo. E podemos ver aqui que bash, de fato, aparece na árvore de processos como pai de pstree.

onlynone
fonte
Sim ! + 1. Ele exemplifica o que Gilles colocou formalmente primeiro e o segundo exemplificado. Talvez dar crédito a ele por sua resposta inicial esteja em ordem?
Cbhihe
0

Apoiando o que as outras respostas disseram, procurei o código que chama comandos no controle remoto, https://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660 ...

1660    /*
1661     * Execute the command using the user's shell.  This uses the -c
1662     * option to execute the command.
1663     */
1664    argv[0] = (char *) shell0;
1665    argv[1] = "-c";
1666    argv[2] = (char *) command;
1667    argv[3] = NULL;
1668    execve(shell, argv, env);
1669    perror(shell);
1670    exit(1);

... que, como você pode ver, invoca incondicionalmente shellcom o primeiro argumento -ce o segundo argumento command. Anteriormente, a shellvariável foi definida como o shell de login do usuário, conforme registrado em /etc/passwd. commandé um argumento para essa função e, em última análise, é definido como uma string lida literalmente fora do fio (consulte session_exec_reqo mesmo arquivo ). Portanto, o servidor não interpreta o comando, mas um shell é sempre chamado no controle remoto.

No entanto, a parte relevante da especificação do protocolo SSH é que não parecem exigir esse comportamento; apenas diz

 byte      SSH_MSG_CHANNEL_REQUEST
 uint32    recipient channel
 string    "exec"
 boolean   want reply
 string    command

Esta mensagem solicitará que o servidor inicie a execução do comando fornecido. A string 'command' pode conter um caminho. Devem ser tomadas precauções normais para impedir a execução de comandos não autorizados.

Provavelmente, porque nem todos os sistemas operacionais têm o conceito de shell de linha de comando. Por exemplo, não seria uma loucura para um servidor ssh Classic MacOS alimentar cadeias de comando "exec" para o intérprete AppleScript .

zwol
fonte