Eu vi as perguntas e respostas sobre a necessidade de escape duplo dos argumentos para 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 que analise os argumentos da linha de comando para o comando remoto, portanto, não parece que aspas duplas ou escape seriam necessárias ( mas com certeza é). Se, em vez disso, eu ssh lá primeiro e obter um shell de login e, em seguida, executar pstree -a -p
, 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 aspas duplas são necessárias?
Há sempre um shell remoto. No protocolo SSH, o cliente envia ao servidor uma string 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. (Mais precisamente: o servidor executa o programa que está registrado como o shell do usuário no banco de dados do usuário, passando dois argumentos de linha de comando:
-c
e a string enviada pelo cliente. O shell não é invocado como um shell de login: o servidor não defina o argumento zero para uma string começando com-
.)É impossível ignorar o shell remoto. O protocolo não tem nada como enviar um array de strings que pode ser analisado como um array argv no servidor. E o servidor SSH não ignorará o shell remoto porque isso pode ser uma restrição de segurança: usar um programa restrito como shell do usuário é uma forma de fornecer uma conta restrita que só tem permissão para executar determinados comandos (por exemplo, uma conta somente rsync ou uma conta somente git).
Você pode não ver o shell
pstree
porque ele pode já ter desaparecido. Muitos shells têm uma otimização em que, se detectarem que estão prestes a “executar este comando externo, aguardar a conclusão e sair com o status do comando”, o shell executará “execve
deste comando externo”. Isso é o que está acontecendo no seu primeiro exemplo. Compare os três comandos a seguir: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 de execução do shell.
Acho que entendi:
Os argumentos para
pstree
são: mostrar argumentos de linha de comando, mostrar pids e mostrar apenas os processos pais do pid fornecido. O'$$'
é uma variável especial do shell 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 é citado duplamente 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 é o próprio pstree. Apstree
casca é assim?O que eu entendo que está acontecendo é que
bash
está sendo invocado e está analisando os argumentos da linha de comando, mas então invocaexec
para se substituir pelo comando que está sendo executado.Parece que só faz isso no caso de um único comando remoto:
Nesse caso, estou solicitando a execução de dois comandos:
pstree
seguido porecho
. E podemos ver aqui quebash
de fato aparece na árvore do processo como pai depstree
.Apoiando o que as outras respostas disseram, pesquisei o código que invoca comandos no controle remoto, https://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660.. .
... que, como você pode ver, invoca incondicionalmente
shell
com primeiro argumento-c
e segundo argumentocommand
. Anteriormente, ashell
variável era definida para o shell de login do usuário conforme registrado em/etc/passwd
.command
é um argumento para esta função e, finalmente, é definido como uma string lida literalmente na rede (consultesession_exec_req
no mesmo arquivo ). Portanto, o servidor não interpreta o comando, mas um shell é sempre invocado no controle remoto.No entanto, a parte relevante da especificação do protocolo SSH não parece exigir esse comportamento ; só diz
Isso provavelmente ocorre porque nem todos os sistemas operacionais têm o conceito de um shell de linha de comando. Por exemplo, não teria sido uma loucura para um servidor ssh Classic MacOS alimentar strings de comando "exec" para o intérprete AppleScript .