Назад | Перейти на главную страницу

В чем преимущество отсутствия терминала в ssh?

Время от времени я буду делать что-то вроде

ssh user@host sudo thing

и мне напоминают, что ssh по умолчанию не выделяет псевдотерминал. Почему нет? Какие преимущества я потеряю, если буду использовать псевдоним ssh к ssh -t?

Основное отличие - это концепция интерактивность. Это похоже на выполнение команд локально внутри сценария, а не на ввод их самостоятельно. Он отличается тем, что удаленная команда должна выбирать значение по умолчанию, а неинтерактивный режим является наиболее безопасным. (и обычно самый честный)

STDIN

  • Если выделен PTY, приложения могут обнаружить это и знать, что можно безопасно запрашивать у пользователя дополнительный ввод, не нарушая работу. Есть много программ, которые пропускают этап запроса пользователя на ввод, если терминал отсутствует, и это хорошо. В противном случае это привело бы к ненужному зависанию скриптов.
  • Ваш ввод будет отправлен на удаленный сервер на время выполнения команды. Сюда входят управляющие последовательности. Хотя Ctrl-c break обычно приводит к немедленному прерыванию цикла в команде ssh, вместо этого ваши управляющие последовательности будут отправлены на удаленный сервер. Это приводит к необходимости "забивать" нажатие клавиши, чтобы гарантировать, что оно поступит, когда управление листья команду ssh, но до начала следующей команды ssh.

Я бы предостерегал от использования ssh -t в автоматических скриптах, таких как crons. Неинтерактивная оболочка, запрашивающая у удаленной команды интерактивное поведение при вводе, вызывает всевозможные проблемы.

Вы также можете проверить наличие терминала в собственных сценариях оболочки. Чтобы протестировать STDIN с более новыми версиями bash:

# fd 0 is STDIN
[ -t 0 ]; echo $?

STDOUT

  • При наложении ssh к ssh -t, вы можете рассчитывать на дополнительный возврат каретки в конце строки. Возможно, вам это не видно, но оно есть; он будет отображаться как ^M когда подключен к cat -e. Затем вы должны приложить дополнительные усилия, чтобы этот управляющий код не был назначен вашим переменным, особенно если вы собираетесь вставить этот вывод в базу данных.
  • Также существует риск того, что программы будут считать, что они могут отображать выходные данные, не подходящие для перенаправления файлов. Обычно, если вы перенаправляете STDOUT в файл, программа распознает, что ваш STDOUT не является терминалом, и пропускает любые цветовые коды. Если перенаправление STDOUT происходит от вывода клиент ssh и если существует PTY, связанный с удаленным концом клиента, удаленные программы не могут сделать такое различие, и вы получите мусор терминала в своем выходном файле. Перенаправление вывода в файл на удаленный конец соединения должно работать должным образом.

Вот тот же тест bash, что и раньше, но для STDOUT:

# fd 1 is STDOUT
[ -t 1 ]; echo $?

Хотя эти проблемы можно обойти, вы неизбежно забудете создавать сценарии на их основе. Все мы в какой-то момент понимаем. Члены вашей команды также могут не осознавать / помнить, что этот псевдоним существует, что, в свою очередь, создаст вам проблемы, когда Oни напишите сценарии, использующие ваш псевдоним.

Сглаживание ssh к ssh -t в значительной степени тот случай, когда вы нарушите принцип дизайна наименьший сюрприз; люди будут сталкиваться с проблемами, которых они не ожидают, и могут не понимать, что их вызывает.

Управляющие / управляющие символы SSH и передача двоичных файлов

Одно из преимуществ, которое не было упомянуто в других ответах, заключается в том, что при работе без псевдотерминала, SSH escape-символы Такие как ~C являются не поддерживается; это делает безопасным для программ передачу двоичных файлов, которые могут содержать эти и другие управляющие символы.

Без псевдотерминала символы передаются на удаленный хост «как есть»:

$ printf "one\ntwo\n~Cthree\n" | ssh user@host tee 1
one
two
~Cthree

Если вы попытаетесь принудительно выделить псевдотерминал, включение ~C вызывает ssh> выводится приглашение, позволяющее пользователю ввести команду SSH, прерывая передачу.

$ printf "one\ntwo\n~Cthree\n" | ssh -tt user@host tee 2

ssh>
one
two
three
one
two
three

А ~. последовательность хуже, так как в результате не передаются данные:

$ printf "one\ntwo\n~.three\n" | ssh -tt user@host tee 2
Connection to host closed.

Программное управление потоком

Программные символы управления потоком (XON / XOFF) также могут обрабатываться специально при назначении псевдотерминала.

$ printf "one\ntwo\n^Sthree\n" | ssh user@host tee 1
one
two
three

$ ssh user@host cat -vet 1
one$
two$
^Sthree$

С псевдотерминалом Ctrl-S символ интерпретируется как сигнал для приостановки входного потока, поэтому данные не могут быть отправлены после того, как этот символ встречается (если за ним не следует Ctrl-Q позже во входном потоке).

В этом случае принудительное выделение псевдотерминала приводит к тому, что файл на удаленном конце оказывается пустым.

$ printf "one\ntwo\n^Sthree\n" | ssh -tt user@host tee 2
one
two

В этом случае принудительное выделение псевдотерминала приводит к созданию файла на удаленном конце, содержащего все три строки, но без управляющих символов:

$ printf "one\ntwo\n^Sthree^Q\n" | ssh -tt user@host tee 2
one
two
three
one
two
three

$ ssh user@host cat -vet 2
one$
two$
three$

Преобразование символов конца строки

С псевдотерминалом символы перевода строки (десятичное 10, шестнадцатеричное 0A в ASCII) также переводятся на CRLF последовательности (шестнадцатеричные 0D0A в ASCII).

Из предыдущего примера:

$ ssh -t user@host cat 1 | cat -vet
one^M$
two^M$
^Sthree^M$
Connection to host closed.

Скопируйте двоичный файл с помощью псевдотерминала

$ ssh -t anthony@remote_host 'cat /usr/bin/free' > ~/free
Connection to remote_host closed.

Скопируйте двоичный файл без использования псевдотерминала:

$ ssh anthony@remote_host 'cat /usr/bin/free' > ~/free2

Эти два файла не совпадают:

$ diff ~/free*
Binary files /home/anthony/free and /home/anthony/free2 differ

Тот, который был скопирован с помощью псевдотерминала, поврежден:

$ chmod +x ~/free*
$ ./free
Segmentation fault

а другой - нет:

$ ./free2
             total       used       free     shared    buffers     cached
Mem:       2065496    1980876      84620          0      48264    1502444
-/+ buffers/cache:     430168    1635328
Swap:      4128760        112    4128648

Передача файлов по SSH

Это особенно важно для таких программ, как scp или rsync которые используют SSH для передачи данных. это подробное описание того, как работает протокол SCP объясняет, как протокол SCP состоит из смеси текстовых протокольных сообщений и данных двоичного файла.


OpenSSH помогает защитить вас от самого себя

Стоит отметить, что даже если -t используется флаг OpenSSH ssh клиент откажется выделить псевдотерминал, если обнаружит, что его stdin поток не является терминалом:

$ echo testing | ssh -t anthony@remote_host 'echo $TERM'
Pseudo-terminal will not be allocated because stdin is not a terminal.
dumb

Вы все еще можете заставить клиента OpenSSH выделить псевдотерминал с помощью -tt:

$ echo testing | ssh -tt anthony@remote_host 'echo $TERM'
xterm

В любом случае (разумно) все равно, stdout или stderr перенаправляются:

$ ssh -t anthony@remote_host 'echo $TERM' >| ssh_output
Connection to remote_host closed.

На удаленном хосте мы имеем дело с этой настройкой:

/etc/sudoers
...
Defaults requiretty

Без sudo

$ ssh -T user@host echo -e 'foo\\nbar' | cat -e
foo$
bar$

И с sudo

$ ssh -T user@host sudo echo -e 'foo\\nbar' | cat -e
sudo: sorry, you must have a tty to run sudo

С помощью sudo мы получаем дополнительный возврат каретки

$ ssh -t user@host sudo echo -e 'foo\\nbar' | cat -e
foo^M$
      bar^M$
            Connection to localhost closed.

Решение - отключить перевести новую строку в символ возврата каретки и новой строки с участием stty -onlcr

$ ssh -t user@host stty -onlcr\; sudo echo -e 'foo\\nbar' | cat -e
foo$
    bar$
        Connection to localhost closed.

Подумайте об обратной совместимости.

Два основных режима ssh - это интерактивный вход с tty и указанная команда без tty, потому что это были точные возможности rlogin и rsh соответственно. ssh необходим для обеспечения расширенного набора rlogin/rsh функции, чтобы быть успешной в качестве замены.

Таким образом, значения по умолчанию были определены еще до появления ssh. Комбинации типа "Я хочу указать команду и получить tty "нужно было получить доступ с новыми параметрами. Радуйтесь, что по крайней мере мы иметь этот вариант сейчас, в отличие от того, когда мы использовали rsh. Мы не отказались от каких-либо полезных функций для получения зашифрованных соединений. У нас есть бонусные функции!

Из man ssh:

 -t      Force pseudo-tty allocation.  This can be used to execute arbi-
         trary screen-based programs on a remote machine, which can be
         very useful, e.g. when implementing menu services.  Multiple -t
         options force tty allocation, even if ssh has no local tty.

Это позволяет вам получить своего рода «оболочку» для удаленного сервера. Для серверов, которые делают не предоставить доступ к оболочке, но разрешить SSH (например, Github является известным примером доступа по SFTP), использование этого флага приведет к тому, что сервер отклонит ваше соединение.

В оболочке также есть все ваши переменные окружения (например, $PATH), поэтому для выполнения скриптов обычно требуется tty для работы.