SSH-перенаправление портов
Если вы ищете, как просто пробросить порты через SSH, то, скорее всего, наткнётесь на что-то вроде:
ssh -nNT -L 8000:example.com:80 user@bastion.example.com
Или вот это, если вы ещё хотите отправить ssh в фон:
ssh -NT -L 3306:db.example.com:3306 example.com &
Обе эти команды используют как минимум одну лишнюю опцию, а вторая вообще может привести к тому, что SSH не подключится, если вы используете аутентификацию по паролю. Тем не менее, подобные примеры до сих пор кочуют по разным статьям про перенаправление портов через SSH. Я и сам до недавнего времени пользовался первой вариацией, и решил, что стоит написать об этом — может, кому-то пригодится.
Правильный вариант для этого случая - это не -nNT, а просто -N, вот так:
ssh -N -L 8000:example.com:80 user@bastion.example.com
Если вы хотите отправить ssh в фон, то стоит добавить -f, а не использовать &, встроенный в оболочку. Потому что тогда вы сможете ввести пароль в ssh, если это понадобится.
По сути, в этом и вся суть статьи - так что, если вам просто нужно было это, можете дальше не читать. Но если хочется разобраться, что вообще делают эти опции и зачем они нужны, или вы не совсем понимаете, о чём речь - читайте дальше.
Что такое перенаправление портов через SSH?
ssh - мощный инструмент для удалённого доступа к серверам, с помощью которого можно выполнять команды на удалённой машине. Но кроме этого, он умеет пробрасывать порты через защищённый туннель с помощью опций -L и -R. Проще говоря, вы можете направить подключение к локальному порту на удалённый сервер, вот так:
ssh -L 8080:other.example.com:80 ssh.example.com
В этом примере вы подключаетесь к ssh.example.com, и затем ssh перенаправляет весь трафик с вашего локального порта 8080 на порт 80 сервера other.example.com, проходя через ssh.example.com. Это реально мощная штука: она позволяет "прыгнуть" за фаервол, имея снаружи только один ssh-сервер.
Работает это и в обратную сторону - с опцией -R, которая позволяет подключаться с удалённой машины к серверу, запущенному у вас локально. Например, вы поднимаете веб-сайт на своём компьютере на порту 8080, но хотите, чтобы он был доступен снаружи на example.com через порт 80. Тогда можно сделать так:
ssh -R 8080:example.com:80 example.com
Проблема с перенаправлением портов через ssh в том, что если не указать дополнительных опций, то вместе с туннелем открывается ещё и shell на удалённой машине. Это нормально, если вы собираетесь работать на сервере и заодно хотите пробросить порт, но если вы просто хотите быстро организовать проброс и не планируете использовать shell, это начинает раздражать - особенно если shell по каким-то причинам закрывается, потому что вместе с ним ssh завершит и перенаправление.
И вот здесь вступает в игру опция -N.
SSH: только проброс портов
В man-странице ssh опция -N описана так:
Do not execute a remote command. This is useful for just forwarding ports.
И этого, в общем-то, достаточно. Она говорит ssh не выполнять никаких команд на удалённой машине - только делать то, что указано в опциях -L или -R, то есть пробрасывать порты. Но по какой-то причине многие думают, что этого недостаточно и нужно добавлять ещё кучу флагов. Давайте разберёмся, что вообще делают эти «дополнительные» опции.
SSH и stdin
Опция -n управляет тем, как ssh работает со стандартным вводом. Конкретно, она говорит не читать из stdin:
Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the background. A common trick is to use this to run X11 programs on a remote machine. For example, ssh -n shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically forwarded over an encrypted channel. The ssh program will be put in the background. (This does not work if ssh needs to ask for a password or passphrase; see also the -f option.)
То есть -n нужен, когда вы запускаете ssh в фоне — например, чтобы не мешал stdin. Это полезно для запуска графических приложений по X11, когда вы хотите, чтобы ssh тихо ушёл в фон. Но если вам нужно ввести пароль - это уже не сработает.
Пароли и фоновый режим в SSH
Опция -f отправляет ssh в фоновый режим, освобождая терминал, из которого вы его запускали:
Requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background. This implies -n. The recommended way to start X11 programs at a remote site is with something like ssh -f host xterm.
Как видно из описания -n, -f по сути делает то же самое, что и -n + &, но с важным отличием - ssh сначала даст вам ввести пароль, а уже потом уйдёт в фон. Так что если нужна фоновая работа и при этом ssh должен запросить пароль - используйте -f.
SSH и псевдотерминалы
Опция -T чуть более странная, и её описание довольно короткое:
Disable pseudo-terminal allocation.
У неё есть антипод - -t, и он описан чуть подробнее:
Force pseudo-terminal allocation. This can be used to execute arbitrary 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.
Как видно из описания -t, ssh выделяет псевдотерминал на удалённой машине, не на локальной. Но - как я сам проверил - если вы используете -N, псевдотерминал и так не создаётся, потому что команды не запускаются. Так что -T просто не нужен.
Что вообще такое псевдотерминал?
Если коротко, это интерфейс в UNIX-подобных системах (например, Linux или BSD), который притворяется настоящим терминалом - отсюда и название. Программы вроде shell или текстовых меню, написанных с использованием ncurses, ожидают, что их будут запускать в «терминале» (в том числе и интерактивно). Псевдотерминал подменяет ввод, как будто он пришёл с физического устройства, и обрабатывает, скажем, Ctrl+C как сигнал прерывания (SIGINT).
Зачем всё это вообще пишут?
Честно - я не знаю, почему эти лишние опции везде копипастят как «правильные». Похоже, это что-то вроде карго-культа: кто-то однажды написал пример команды, другие начали её использовать, не вникая, что делает каждая часть. В одном из ответов на Stack Overflow, который я видел, автор считал, что -T отключает локальный псевдотерминал, - возможно, это и объясняет, почему они решили, что этот флаг нужен.
Наверное, мораль этой истории - всегда задавайтесь вопросами и читайте man-страницы, а не просто гуглите готовую команду.