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

11

Eu vi as perguntas e as respostas sobre a necessidade de escapar duas vezes dos argumentos para os comandos ssh remotos. Minha pergunta é: Exatamente onde e quando a segunda análise é feita?

Se eu executar o seguinte:

$ ssh otherhost pstree -a -p

Eu vejo o seguinte na saída:

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

O processo pai para o comando remoto ( pstree ) é sshd , não parece haver nenhum shell lá que analise os argumentos da linha de comando para o comando remoto, então não parece se citar duplo ou fugir seria necessário (mas é definitivamente). Se em vez disso eu ssh lá primeiro e obter um shell de login e, em seguida, execute pstree -a -p Eu vejo o seguinte na saída:

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

Então, claramente, há um bash shell que faria a análise da linha de comando nesse caso. Mas no caso em que eu uso um comando remoto diretamente, não parece haver um shell, então por que a duplicação de texto é necessária?

    
por onlynone 03.01.2018 / 22:07

3 respostas

21

Existe sempre um shell remoto. No protocolo SSH, o cliente envia ao servidor uma string para executar. O cliente de linha de comando SSH obtém seus argumentos de linha de comando e concatena-os com um espaço entre os argumentos. O servidor pega essa string, executa o shell de login do usuário e passa para ela aquela string.

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

Você pode não ver o shell em pstree , porque ele já pode ter desaparecido. Muitos shells têm uma otimização onde se eles detectarem que estão prestes a executar “execute este comando externo, espere que ele seja concluído e saia com o status do comando”, então o shell executa “ execve deste comando externo". Isto é o que está acontecendo no seu primeiro exemplo. Contraste os seguintes três comandos:

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 shell que anula a otimização exec do shell.

    
por 03.01.2018 / 22:14
10

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 pstree são: mostrar argumentos da linha de comando, mostrar pids e mostrar apenas processos pai do pid dado. O '$$' é uma variável de shell especial que o bash substituirá com seu próprio pid quando o bash avaliar os argumentos da linha de comando. É citado uma vez para impedir que seja interpretado pela minha shell local. Mas não é duplamente citado ou escapado para permitir que seja interpretado pelo shell remoto.

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

O que eu entendo está acontecendo é que bash está sendo invocado e está analisando os argumentos da linha de comando, mas invoca exec para se substituir 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 que dois comandos sejam executados: pstree seguido por echo . E podemos ver aqui que bash de fato aparece na árvore de processos como pai de pstree .

    
por 03.01.2018 / 22:17
0

Suportando o que as outras respostas disseram, procurei o código que chama comandos no controle remoto, link ...

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 shell com o primeiro argumento -c e o segundo argumento command . Anteriormente, a variável shell foi definida para o shell de login do usuário, conforme registrado em /etc/passwd . command é um argumento para essa função e, por fim, é definido como uma string lida literalmente fora da ligação (consulte session_exec_req no 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 não parecem exigir esse comportamento; diz apenas

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

This message will request that the server start the execution of the given command. The 'command' string may contain a path. Normal precautions MUST be taken to prevent the execution of unauthorized commands.

Isso provavelmente é porque nem todos os sistemas operacionais têm o conceito de um shell de linha de comando. Por exemplo, não seria loucura para um servidor ssh clássico do MacOS alimentar strings de comando "exec" para o AppleScript intérprete em vez disso.

    
por 04.01.2018 / 19:19