Как анализируются аргументы удаленной командной строки ssh

11

Я видел вопросы и ответы о необходимости двойного экранирования аргументов для удаленных команд ssh. Мой вопрос: где и когда будет выполнен второй разбор?

Если я запускаю следующее:

$ ssh otherhost pstree -a -p

Я вижу следующее в выводе:

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

Родительский процесс для удаленной команды ( pstree) состоит в том sshd, что там, похоже, нет никакой оболочки, которая бы анализировала аргументы командной строки для удаленной команды, поэтому не похоже, что двойные кавычки или экранирование будут необходимы ( но это точно так и есть). Если вместо этого я сначала ssh там и получаю оболочку входа в систему, а затем запускаю, pstree -a -pя вижу следующее в выводе:

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

Ясно, что есть bashоболочка, которая в этом случае будет выполнять синтаксический анализ командной строки. Но в случае, когда я использую удаленную команду напрямую, оболочки, похоже, не существует, так почему же нужна двойная кавычка?

onlynone
источник

Ответы:

22

Всегда есть удаленная оболочка. В протоколе SSH клиент отправляет серверу строку для выполнения. Клиент командной строки SSH принимает аргументы командной строки и объединяет их с пробелом между аргументами. Сервер берет эту строку, запускает оболочку входа пользователя и передает ей эту строку.

Невозможно обойти удаленную оболочку. Протокол не имеет ничего общего с отправкой массива строк, которые могут быть проанализированы как массив argv на сервере. И SSH-сервер не будет обходить удаленную оболочку, потому что это может быть ограничением безопасности: использование ограниченной программы в качестве оболочки пользователя является способом предоставления ограниченной учетной записи, которой разрешено выполнять только определенные команды (например, учетная запись только для rsync или аккаунт только для git).

Вы можете не увидеть оболочку, pstreeпотому что она может быть уже пропала. Многие оболочки имеют оптимизацию, при которой, если они обнаруживают, что собираются сделать «выполнить эту внешнюю команду, дождаться ее завершения и выйти со статусом команды», то execveвместо этого оболочка запускает « этой внешней команды». Это то, что происходит в вашем первом примере. Сравните следующие три команды:

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

Первые два идентичны: клиент отправляет точно такие же данные на сервер. Третий посылает команду оболочки, которая побеждает оптимизацию exec оболочки.

Жиль "ТАК - перестань быть злым"
источник
2
ха! не могу поверить, что ты избил меня до ответа на мой собственный вопрос. Я понял это на полпути после публикации вопроса и решил, что должен просто пройти через вопрос и ответить на него сам.
только
10

Я думаю, я понял это:

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

Аргументы to pstreeдолжны: показать аргументы командной строки, показать pids и показать только родительские процессы данного pid. Это '$$'специальная переменная оболочки, которую bash заменит своим собственным pid, когда bash оценивает аргументы командной строки. Он цитируется один раз, чтобы не допустить его интерпретации моей локальной оболочкой. Но он не заключен в двойные кавычки или не экранирован, чтобы его можно было интерпретировать удаленной оболочкой.

Как мы видим, он заменен на 12001так, что это pid оболочки. Мы также можем видеть из вывода: pstree,12001что процесс с pid 12001 - это сам pstree. Так pstreeэто оболочка?

Я собираюсь bashсказать, что он вызывается и анализирует аргументы командной строки, но затем вызывает execзамену на выполняемую команду.

Кажется, что это происходит только в случае одной удаленной команды:

$ 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

В этом случае я запрашиваю две команды: pstreeследуют echo. И мы можем видеть здесь, что bashна самом деле обнаруживается в дереве процессов в качестве родителя pstree.

onlynone
источник
Ага ! + 1. Это иллюстрирует то, что Жиль поставил более формально первым и примерным вторым. Возможно, следует отдать ему должное за его ранний ответ?
Cbhihe
0

Поддерживая то, что сказали другие ответы, я посмотрел код, который вызывает команды на пульте, 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);

... который, как вы можете видеть, безусловно вызывает shellпервый аргумент -cи второй аргумент command. Ранее shellпеременная была установлена ​​в оболочку входа пользователя, как записано в /etc/passwd. commandявляется аргументом этой функции, и в конечном итоге устанавливается для строки, дословно считываемой с провода (см. session_exec_reqтот же файл ). Таким образом, сервер вообще не интерпретирует команду, но на удаленном всегда вызывается оболочка.

Тем не менее, соответствующая часть спецификации протокола SSH это не по всей видимости, требует такого поведения; это только говорит

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

Это сообщение запросит, чтобы сервер запустил выполнение данной команды. Строка 'command' может содержать путь. ДОЛЖНЫ быть приняты обычные меры предосторожности для предотвращения выполнения неавторизованных команд.

Вероятно, это связано с тем, что не во всех операционных системах есть концепция оболочки командной строки. Например, для ssh-сервера Classic MacOS было бы сумасшествием передавать вместо этого строки команд exec интерпретатору AppleScript .

zwol
источник