¿Cómo se analizan los argumentos de la línea de comando remota ssh?

11

He visto las preguntas y respuestas sobre la necesidad de escapar doblemente de los argumentos a los comandos ssh remotos. Mi pregunta es: ¿exactamente dónde y cuándo se realiza el segundo análisis?

Si ejecuto lo siguiente:

$ ssh otherhost pstree -a -p

Veo lo siguiente en la salida:

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

El proceso principal para el comando remoto ( pstree) es sshdque no parece haber ningún shell allí que esté analizando los argumentos de la línea de comando al comando remoto, por lo que no parece que sea necesaria una doble cita o escape ( pero definitivamente lo es). Si, en cambio, ssh allí primero y obtengo un shell de inicio de sesión, y luego ejecuto pstree -a -p, veo lo siguiente en la salida:

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

Entonces, claramente hay un bashshell allí que analizaría la línea de comandos en ese caso. Pero en el caso en que utilizo un comando remoto directamente, no parece haber un shell, entonces, ¿por qué es necesaria una doble cita?

solo nadie
fuente

Respuestas:

22

Siempre hay un shell remoto. En el protocolo SSH, el cliente envía al servidor una cadena para ejecutar. El cliente de línea de comando SSH toma sus argumentos de línea de comando y los concatena con un espacio entre los argumentos. El servidor toma esa cadena, ejecuta el shell de inicio de sesión del usuario y le pasa esa cadena.

Es imposible evitar el shell remoto. El protocolo no tiene nada como enviar una matriz de cadenas que podrían analizarse como una matriz argv en el servidor. Y el servidor SSH no omitirá el shell remoto porque eso podría ser una restricción de seguridad: usar un programa restringido como el shell del usuario es una forma de proporcionar una cuenta restringida que solo puede ejecutar ciertos comandos (por ejemplo, una cuenta rsync o una cuenta solo para git).

Es posible que no vea el caparazón pstreeporque puede que ya no esté. Muchos shells tienen una optimización en la que si detectan que están a punto de "ejecutar este comando externo, esperar a que se complete y salir con el estado del comando", entonces el shell ejecuta " execvede este comando externo". Esto es lo que sucede en tu primer ejemplo. Contraste los siguientes tres comandos:

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

Los dos primeros son idénticos: el cliente envía exactamente los mismos datos al servidor. El tercero envía un comando de shell que derrota la optimización de ejecución del shell.

Gilles 'SO- deja de ser malvado'
fuente
2
¡decir ah! No puedo creer que me hayas vencido a responder mi propia pregunta. Lo descubrí a la mitad de la publicación de la pregunta y pensé que debería seguir preguntando y respondiendo yo mismo.
solo el
10

Creo que lo descubrí:

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

Los argumentos a pstreeson: mostrar argumentos de la línea de comandos, mostrar pids y mostrar solo los procesos principales del pid dado. The '$$'es una variable de shell especial que bash reemplazará con su propio pid cuando bash evalúa los argumentos de la línea de comandos. Se cita una vez para evitar que sea interpretado por mi shell local. Pero no se cita ni se escapa doblemente para permitir que el shell remoto lo interprete.

Como podemos ver, se reemplaza por, 12001así que ese es el pid del shell. También podemos ver en la salida: pstree,12001que el proceso con un pid de 12001 es pstree en sí. Entonces, ¿ pstreees el caparazón?

Lo que sé que está sucediendo allí es que bashse invoca y analiza los argumentos de la línea de comandos, pero luego se invoca execpara reemplazarse con el comando que se ejecuta.

Parece que solo hace esto en el caso de un solo 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

En este caso, solicito que se ejecuten dos comandos: pstreeseguido por echo. Y podemos ver aquí que bash, de hecho, aparece en el árbol de procesos como padre de pstree.

solo nadie
fuente
Sip ! + 1. Ejemplifica lo que Gilles puso más formalmente primero y ejemplificó segundo. ¿Quizás darle crédito por su pronta respuesta está en orden?
Cbhihe
0

Apoyando lo que han dicho las otras respuestas, busqué el código que invoca comandos en el control 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 puede ver, invoca incondicionalmente shellcon el primer argumento -cy el segundo argumento command. Anteriormente, la shellvariable se configuraba en el shell de inicio de sesión del usuario como se registró en /etc/passwd. commandes un argumento para esta función y, en última instancia, se establece en una cadena que se lee al pie de la letra (ver session_exec_reqen el mismo archivo ). Entonces, el servidor no interpreta el comando en absoluto, pero siempre se invoca un shell en el control remoto.

Sin embargo, la parte correspondiente de la especificación del protocolo SSH no no parece requerir este comportamiento; solo dice

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

Este mensaje solicitará que el servidor inicie la ejecución del comando dado. La cadena de 'comando' puede contener una ruta. DEBEN tomarse precauciones normales para evitar la ejecución de comandos no autorizados.

Esto probablemente se deba a que no todos los sistemas operativos tienen el concepto de un shell de línea de comandos. Por ejemplo, no habría sido una locura para un servidor ssh clásico de MacOS alimentar cadenas de comandos "exec" al intérprete de AppleScript .

zwol
fuente