Як аналізуються аргументи віддаленого командного рядка ssh


11

Я бачив запитання та відповіді про те, що потрібно подвоїти аргументи для віддалених команд ssh. Моє запитання: саме де і коли відбувається другий аналіз?

Якщо я запускаю наступне:

$ ssh otherhost pstree -a -p

У висновку я бачу таке:

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

Батьківський процес для віддаленої команди ( pstree) полягає в тому sshd, що там, здається, немає жодної оболонки, яка б розбирала аргументи командного рядка на віддалену команду, тому, здається, не потрібне подвійне цитування чи скасування ( але це точно є). Якщо замість цього я сш там спочатку і отримую оболонку входу, а потім запускаю, pstree -a -pу виході я бачу наступне:

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

Так чітко є bashоболонка, яка б виконувала розбір командного рядка в цьому випадку. Але у випадку, коли я використовую віддалену команду безпосередньо, не здається оболонки, тож чому потрібно подвійне цитування?

Відповіді:


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'

Перші два ідентичні: клієнт надсилає абсолютно однакові дані на сервер. Третій надсилає команду shell, яка перемагає оптимізацію виконання оболонки.


2
га! не можу повірити, що ти побив мене, щоб відповісти на моє власне питання. Я зрозумів це на півдорозі через розміщення запитання і подумав, що я повинен просто перейти із запитанням і відповісти на нього сам.
onlynone

10

Я думаю, я це зрозумів:

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

Аргументи pstreeповинні: показувати аргументи командного рядка, показувати pids та показувати лише батьківські процеси даного pid. Це '$$'спеціальна змінна оболонки, яку bash замінить своїм власним pid, коли bash оцінює аргументи командного рядка. Це цитується один раз, щоб запобігти його інтерпретації моєю місцевою оболонкою. Але це не вдвічі цитується або уникається, щоб дозволити інтерпретувати його віддаленою оболонкою.

Як ми бачимо, його замінюють 12001таким чином, це під оболонки. З результату ми також бачимо, 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.


Так ! + 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 .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.