Знакомая картина: инженер едет в поезде, отвечает в переписке и правит конфиг на сервере, поезд ныряет в тоннель, Wi-Fi сменяется мобильным интернетом, и ssh-сессия замирает. После переподключения пропадает полусоставленная команда, а то и весь контекст работы. Mosh, мобильная оболочка из проекта MIT, появившаяся в 2012 году, спроектирована именно для таких условий: она переживает смену адреса, обрывы и долгие паузы, а набор текста остаётся отзывчивым даже на плохой связи. Ниже устройство инструмента, настройка портов на межсетевом экране и ловушки, которые всплывают при первом запуске.

Обрыв сессии ssh при смене сети и переходе между подключениями

Сессия ssh привязана к TCP-соединению, а оно живёт, пока неизменна пара адресов и портов. Телефон сменил вышку, ноутбук пересел с Wi-Fi на мобильный интернет, адрес клиента поменялся, и сервер считает соединение умершим, сколько ни жди. Сон ноутбука добавляет свою порцию проблем: после пробуждения TCP-соединение часто уже мёртв, и терминал узнаёт об этом минуту спустя. Восстановления в протоколе нет: терминал рисует замороженный экран, набор идёт в никуда, и остаётся закрывать вкладку и начинать заново. Хуже того, вместе с сессией теряется окружение: переменные, текущий каталог, история набранного, запущенные редакторы. После каждого обрыва пользователь собирает рабочую обстановку заново, и в дороге это занимает больше времени, чем сама правка.

К тому же терминал ssh показывает ввод только после ответа сервера. На связи с задержкой в пару сотен миллисекунд набор превращается в испытание терпения: каждый опечатанный символ стирается со своей задержкой, а длинная команда выглядит как эхо в горах. Обрыв сети ставит терминал на паузу без предупреждения, и пользователь узнаёт о проблеме, когда строки перестают появляться.

Mosh устроен иначе: состояние экрана синхронизируется как целое через собственный протокол поверх UDP, а не как непрерывный поток байтов через TCP. Протокол устойчив к потерям пакетов, не копит пакеты в сетевом буфере и подстраивает частоту обновлений под качество связи. Практическое следствие: ctrl+c срабатывает мгновенно, даже когда канал еле дышит, и разогнавшийся процесс снимается без мучений. Для мобильной работы это не мелочь, а главное: терминал перестаёт наказывать за плохую сеть, и рутина правки конфигов из поезда превращается в обычное дело.

Рукопожатие по ssh и рабочий канал по UDP

Авторизация остаётся прежней: клиент mosh сначала логинится на сервер по ssh с теми же ключами или паролем, читает привычный ~/.ssh/config и запускает на сервере mosh-server. Псевдонимы хостов из ssh-конфига работают без переделки: mosh myserver подключается туда же, куда и ssh myserver. Дальше ssh уходит в сторону: серверная часть выбирает UDP-порт из диапазона и передаёт клиенту ключ подключения, которым шифруется сеанс с алгоритмом AES-128, и общение идёт через собственный протокол синхронизации состояния. Серверный процесс живёт от имени обычного пользователя, не требует прав root и завершается вместе с сессией, поэтому отдельная служба на сервере не нужна, как и не нужно ничего менять в правах. В простое трафик почти нулевой: стороны обмениваются только изменениями экрана, и открытая на всю ночь сессия не расходует ни сеть, ни батарею.

Роуминг встроен в сам протокол: сервер принимает пакеты клиента с любого адреса и продолжает синхронизацию. Ноутбук уснул и проснулся, телефон переключился между сетями, соединение подхватывается автоматически. При потере связи клиент честно предупреждает, а после её восстановления договаривается заново, и экран сходится с серверным состоянием. По умолчанию клиент и сервер договариваются о порте в диапазоне UDP 60000-61000, и каждая параллельная сессия занимает свой порт из диапазона.

Важно понимать границу возможностей: роуминг спасает при смене сети, но не при остановке серверного процесса. Если перезапустить машину или снять процесс сессии, терминал не вернётся сам, и здесь без tmux не обойтись: он живёт на сервере отдельно и переживает любые перезапуски клиента.

Локальное эхо и предсказание ввода на плохой связи

Вторая фирменная особенность: клиент рисует нажатия сразу, не дожидаясь сервера. Набранный текст появляется мгновенно, а если предсказание не подтвердилось ответом сервера, неподтверждённый фрагмент подчёркивается, и пользователь видит, что ещё не дошло. Расхождения редки: подтверждение обычно совпадает с догадкой, и подчёркивание исчезает само. Режим выбирается ключом --predict со значениями adaptive, always и never: по умолчанию работает адаптивная схема, которая включает предсказание на заметной задержке и выключает на быстрой связи, рисуя только подтверждённый текст. Переменная окружения MOSH_PREDICTION_MODE задаёт тот же выбор для сеансов, где флаг не пробросишь. В редакторах вроде vim и emacs предсказание тоже ведёт себя прилично, а для задач, где важна абсолютная точность отображения, предсказание отключается.

На длинной задержке это чувствуется как переход с разговоров через эхо на живую речь. Предсказание работает и для удаления, и для стрелок, так что правка длинной команды идёт без томительного ожидания. Мобильные клиенты от этого выигрывают дважды: набор отзывчив, а батарея экономится, потому что по сети передаются только отличия экрана, а не полный трафик нажатий. Клиенты для мобильных платформ давно поддерживают mosh: в терминале Android он включается отдельной опцией, а на планшете и телефоне с iOS живёт в специализированных приложениях, и мобильное устройство становится полноценным пультом к серверам в экстренной ситуации.

Открытие UDP портов и облачные правила для firewall

Главная ловушка внедрения живёт не в mosh, а в сети. Типовой сервер закрыт на входе: открыт TCP 22 для ssh, остальное запрещено. Mosh же после рукопожатия работает по UDP, и по умолчанию просит диапазон 60000-61000. Если диапазон закрыт, клиент ждёт без конца, хотя ssh при этом работает, и выглядит это как загадочное зависание на первом экране. Само рукопожатие идёт на стандартный порт ssh, поэтому его оставляют без изменений и открывают только UDP-часть. Диагноз ставится быстро: ssh подключается, mosh молчит, значит закрыт UDP. Изнутри сервера слушающий порт показывает привычная проверка сокетов:

sudo ss -ulpn | grep mosh

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

Лечение зависит от инструмента:

sudo ufw allow 60000:61000/udp
sudo firewall-cmd --permanent --add-port=60000-61000/udp && sudo firewall-cmd --reload

В облачных панелях правило добавляется в группу безопасности: там часто на автомате оставляют только TCP 22, и UDP диапазон просто никто не открыл. Для систем с прямым iptables правило выглядит привычно:

sudo iptables -A INPUT -p udp --dport 60000:61000 -j ACCEPT

При жёсткой политике минимализма диапазон сужается до одного порта ключом -p на обеих сторонах: mosh -p 60001 user@host, и открывается единственный UDP-порт.

Отдельные нюансы создают NAT и придирчивые сети. Исходящий UDP обычно проходит свободно, а вот фильтры в отелях, офисах и на конференциях иногда режут высокие UDP-порты, и выручает смена порта или другая сеть. Страницы авторизации открытых сетей добавляют свой шаг: сначала вход в гостевую сеть через браузер, и только потом работает терминал. Долгие паузы в работе тоже учитываются: записи о соединениях в таблицах сетевых устройств живут ограниченное время, но протокол синхронизации переживает такое забвение и восстанавливает обмен первым же пакетом. Защита ssh при этом не меняется: аутентификация по-прежнему через ssh, поэтому настройки ключей и инструментов защиты от подбора пароля действуют как раньше, а открытый UDP диапазон не даёт прямого доступа к оболочке без пройденного ssh-рукопожатия.

Требование к локали и типичные ошибки первого запуска

Mosh работает только с кодировкой UTF-8, и первое подключение к серверу без подходящей локали заканчивается сообщением, что запрошенная локаль недоступна. Лечение рутинное:

sudo locale-gen en_US.UTF-8

На серверах семейства Debian локаль генерируется такой командой, в других семействах помогает утилита настройки локалей или правка файла настроек системы, а переменная LANG должна указывать на существующую локаль в окружении входа. Текущее состояние подсказывает команда locale: если в выводе пустые значения, локаль так и не настроена. Отдельный источник сюрпризов, передача переменных окружения через ssh: клиент шлёт LANG и связанные настройки, сервер может их не принимать, и тогда соглашение между сторонами стоит проверить в конфигурации ssh. Второй частый промах, это отсутствие пакета на одной из сторон: mosh нужен и на ноутбуке, и на сервере, потому что клиент запускает серверную часть через ssh. Третий, старая сборка: поддержка true color появилась в версии 1.4.0 выпуска октября 2022 года, а семантическое версионирование проект ведёт с версии 1.3.0 весны 2017 года, так что версии сторон стоит сверять перед диагностикой странностей с цветами. Общую проверку даёт mosh --version на обеих сторонах. Если же ssh подключается, а mosh молчит, первым делом проверяют UDP и локаль, и в девяти случаях из десяти причина найдена.

Чего mosh не умеет и где ssh остаётся незаменим

Mosh заменяет только интерактивную сессию. Проброса портов нет, X11 нет, переадресации агента нет, копирование файлов через scp и rsync идёт мимо mosh по обычному ssh. Туннели, автоматизация, передача больших объёмов и запуски команд без терминала остаются на ssh, и это правильно: у протоколов разные задачи, и mosh не претендует на чужие. Журналы, резервные копии и скрипты продолжают работать по ssh без изменений, а mosh берёт на себя только живое общение с сервером. К свежеподнятому серверу первым делом всё равно идут по ssh: поставить пакет mosh без ssh не получится, так что оба инструмента живут рядом и не вытесняют друг друга из набора.

Историю прокрутки и стойкость к разрывам удобнее доверить tmux или screen поверх mosh: они дают именованные сессии, панели и возврат к прежним окнам после любого обрыва. Долгие сборки и обновления тоже запускают внутри tmux, а не в голой сессии: остановка серверного процесса тогда не касается рабочей задачи. Копирование файлов при этом спокойно идёт параллельным scp по обычному ssh, пока терминал висит на mosh, и оба канала не мешают друг другу. Связка собирается одной строкой:

mosh user@host -- tmux new -A -s work

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

Порядок внедрения без сюрпризов

Внедрение укладывается в полчаса, если идти по пунктам и не пропускать проверку на реальной сети, а не только из домашнего Wi-Fi:

  1. Поставить пакет mosh на клиенте и на сервере, версии лучше держать близкими;
  2. Проверить локаль UTF-8 на сервере и сгенерировать её при необходимости;
  3. Открыть UDP диапазон 60000-61000 на межсетевом экране и в облачной группе безопасности;
  4. Зафиксировать один порт ключом -p, если политика сети требует минимального числа открытых портов;
  5. Прогнать тест смены сети с Wi-Fi на мобильный интернет и убедиться, что сессия пережила переход;
  6. Обернуть рабочие задачи в tmux, чтобы после обрыва возвращаться в прежние окна;
  7. Добавить проверку UDP портов в чек-лист нового сервера, чтобы диапазон не потерялся при переезде.

После этого мобильный интернет перестаёт быть лотереей: поезд ныряет в тоннель, сеть переключается, а терминал спокойно договаривается с сервером на новом адресе. Псевдонимы из ssh-конфига работают и здесь, поэтому отдельного файла настроек для mosh не заводят. Полезно прогнать и стресс-тест: включить режим экономии трафика в системе, походить с ноутбуком по разным точкам доступа и убедиться, что сессия держится. Для инженера, который работает из дороги, это разница между нервной переподключалкой и спокойной работой, и именно за неё mosh держат в основном наборе инструментов наряду с ssh. Попробовать стоит любому, кто хотя бы раз потерял полусобранную команду где-то между двумя станциями метро.