Время от времени я буду делать что-то вроде
ssh user@host sudo thing
и мне напоминают, что ssh по умолчанию не выделяет псевдотерминал. Почему нет? Какие преимущества я потеряю, если буду использовать псевдоним ssh
к ssh -t
?
Основное отличие - это концепция интерактивность. Это похоже на выполнение команд локально внутри сценария, а не на ввод их самостоятельно. Он отличается тем, что удаленная команда должна выбирать значение по умолчанию, а неинтерактивный режим является наиболее безопасным. (и обычно самый честный)
Ctrl-c
break обычно приводит к немедленному прерыванию цикла в команде ssh, вместо этого ваши управляющие последовательности будут отправлены на удаленный сервер. Это приводит к необходимости "забивать" нажатие клавиши, чтобы гарантировать, что оно поступит, когда управление листья команду ssh, но до начала следующей команды ssh.Я бы предостерегал от использования ssh -t
в автоматических скриптах, таких как crons. Неинтерактивная оболочка, запрашивающая у удаленной команды интерактивное поведение при вводе, вызывает всевозможные проблемы.
Вы также можете проверить наличие терминала в собственных сценариях оболочки. Чтобы протестировать STDIN с более новыми версиями bash:
# fd 0 is STDIN
[ -t 0 ]; echo $?
ssh
к ssh -t
, вы можете рассчитывать на дополнительный возврат каретки в конце строки. Возможно, вам это не видно, но оно есть; он будет отображаться как ^M
когда подключен к cat -e
. Затем вы должны приложить дополнительные усилия, чтобы этот управляющий код не был назначен вашим переменным, особенно если вы собираетесь вставить этот вывод в базу данных.Вот тот же тест bash, что и раньше, но для STDOUT:
# fd 1 is STDOUT
[ -t 1 ]; echo $?
Хотя эти проблемы можно обойти, вы неизбежно забудете создавать сценарии на их основе. Все мы в какой-то момент понимаем. Члены вашей команды также могут не осознавать / помнить, что этот псевдоним существует, что, в свою очередь, создаст вам проблемы, когда Oни напишите сценарии, использующие ваш псевдоним.
Сглаживание ssh
к ssh -t
в значительной степени тот случай, когда вы нарушите принцип дизайна наименьший сюрприз; люди будут сталкиваться с проблемами, которых они не ожидают, и могут не понимать, что их вызывает.
Одно из преимуществ, которое не было упомянуто в других ответах, заключается в том, что при работе без псевдотерминала, 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
Это особенно важно для таких программ, как scp
или rsync
которые используют SSH для передачи данных. это подробное описание того, как работает протокол SCP объясняет, как протокол SCP состоит из смеси текстовых протокольных сообщений и данных двоичного файла.
Стоит отметить, что даже если -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 для работы.