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

Что даёт tmate по сравнению с демонстрацией экрана

В шеринг-сессии гость получает ровно то же изображение, что и владелец, только без потерь: шрифт чёткий при любой связи, команды копируются из истории одним выделением, длинные выводы листаются. Ввод доступен обоим: правки вносит не только хозяин сессии, курсоры свободно двигаются по одному экрану, и беседа превращается в совместную работу, а не в диктовку. Есть и нюанс: одновременный ввод двух людей перемешивает команды, поэтому участники проговаривают вслух, кто что запускает. Трафик измеряется килобайтами, поэтому соединение живёт даже на слабой связи, где видеозвонок рассыпается на квадраты. И это настоящий tmux с панелями и горячими клавишами, так что привычки переносятся один в один: кто знает tmux, знает tmate.

Отдельный плюс для обучения: младший коллега видит команды целиком, включая опечатки и их исправление, а не конечный результат из чужой головы. Записи видео такое не передаёт, а живая сессия передаёт без усилий, причём без камеры и микрофона, то есть работает даже в шумном опенспейсе или при перегруженной связи.

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

Судьба публичных серверов и переход на свой узел

Долгие годы tmate работал почти без настройки: клиент сам подключался к публичным серверам проекта и сразу выдавал готовые строки приглашения для гостя. В июле 2025 года публичные серверы были отключены, о чём команда сообщила в репозитории: серверную часть переписывают на Rust, а возвращение хостингового сервиса обещают позже. Старые версии клиента после этого показывают лишь таймауты подключения, а ssh на адреса публичных узлов обрывается. Проверка текущего состояния простая: клиент без конфига не подключается, и это сигнал, что пора поднимать свой узел. Скрипты автозапуска tmate в старых наработках тоже стоит пересмотреть, чтобы они не висели в ожидании мёртвых адресов.

Практический вывод для текущей работы: узел нужно поднимать самому. Внутри корпоративного контура это даже правильнее: сессии не покидают сеть компании, сервер становится единой точкой входа для всех шерингов, а администратор видит, кто и когда подключался. Для команд с требованиями к размещению кода и терминалов внутри собственной инфраструктуры такой расклад вообще единственно возможный. Клиенту достаточно достучаться до сервера по ssh: ничего наружу открывать не требуется, и сложные сетевые сценарии с NAT проходят сами собой, потому что соединение инициирует клиент. Для небольшой команды без собственной инфраструктуры узлом становится недорогая виртуальная машина у любого хостера. Отдельный сценарий: учебный класс, где каждый студент открывает свою сессию, а преподаватель обходит read-only ссылками и видит прогресс всех сразу. Проект распространяется по свободной лицензии BSD и построен на коде tmux, так что аудит доступен обычным путём.

Подъём сервера tmate в контейнере

Официальный docker-образ поднимается одной командой:

docker run -d --name tmate-ssh-server \
  --restart unless-stopped \
  -p 2200:2200 \
  -v /etc/tmate:/etc/tmate \
  -e SSH_KEYS_PATH=/etc/tmate \
  -e SSH_HOSTNAME=tmate.example.com \
  tmate/tmate-ssh-server

Переменная SSH_KEYS_PATH обязательна: там живут ключи хоста, и каталог стоит закрепить за постоянным томом, чтобы отпечатки не менялись при пересоздании контейнера. SSH_HOSTNAME сообщает клиентам, по какому имени подключаться. Пара SSH_PORT_LISTEN и SSH_PORT_ADVERTISE разделяет порт прослушивания и объявляемый порт, что пригождается за балансировщиком. Режим HAS_WEBSOCKET=1 включает веб-терминал через отдельный сервис tmate-websocket для гостей из браузера, а настройка поддержки протокола балансировщика пригодится, когда перед сервером стоит балансировщик, передающий реальные адреса клиентов.

Контейнеру нужен capability SYS_ADMIN: он используется для изоляции сессий в отдельных namespaces, что не даёт гостям вмешиваться в чужие сессии на одном узле. Нагрузка от текстовых сессий копеечная, поэтому узлу хватает скромной виртуальной машины, а для команды из пары десятков инженеров одного экземпляра достаточно надолго. Ключи хоста можно сгенерировать заранее утилитой ssh-keygen и положить в каталог до запуска, тогда отпечатки известны ещё до первого старта. После запуска сервер печатает отпечатки в журнал, а на межсетевом экране достаточно открыть TCP-порт 2200 или свой, если переназначили: ни UDP, ни переадресации портов на клиенте не нужно. Посмотреть журнал узла можно привычным способом:

CODE1 Для веб-гостей рядом поднимается второй контейнер tmate-websocket, и тогда в клиентском конфиге включается уже упомянутый режим HAS_WEBSOCKET.

Конфиг клиента и проверка отпечатков ключей

Клиент tmate ставится из пакетов и читает конфиг ~/.tmate.conf, синтаксис совпадает с tmux:

set -g tmate-server-host "tmate.example.com"
set -g tmate-server-port 2200
set -g tmate-server-ed25519-fingerprint "SHA256:..."
set -g tmate-identity "/home/dev/.ssh/id_ed25519"

Строка с отпечатком не декоративная: клиент сверяет ключ сервера и отказывается подключаться, если отпечаток другой. Это защита от подмены узла, и пренебрегать ею не стоит, особенно когда конфиг раскатывается на машины команды из общего репозитория. Для серверов со старыми RSA-ключами существует парный параметр tmate-server-rsa-fingerprint, а рядом с ed25519-отпечатком в журнале узла обычно показаны оба. tmate-identity указывает личный ключ, которым клиент аутентифицируется на сервере, поэтому учётные записи пользователей узла остаются обычными ssh-учётками. Конфиг удобно держать в личном репозитории с настройками оболочки: новая машина получает готовый доступ к шерингу сразу после раскатки. Личный ключ при этом один и тот же на всех рабочих станциях инженера, и сервер видит одну учётную запись вместо россыпи непонятных подключений.

Запуск сводится к команде tmate: сразу после старта в верхней части панели появляются строки подключения, полная и только для чтения. Без конфига клиент полезет на уже отключённые публичные узлы и повиснет на таймауте, поэтому конфиг обязателен. Если панель очистилась, вернуть строки можно командой tmate show-messages, там же видны отпечатки и токены текущей сессии. Порт сервера отвечает как обычный ssh-узел, и это удобно для проверок из скриптов мониторинга. Для гостей из браузера при настроенном tmate-websocket выдаётся веб-ссылка, и участие не требует вообще никакого клиента, кроме браузера.

Читаемая сессия и два уровня доступа для гостей

Строка с приставкой ro выдаёт гостю режим наблюдателя: он видит всё, но ничего не вводит, а собственная прокрутка истории у него работает независимо. Полная строка открывает совместный ввод. На этом строится разграничение ролей: новичку отправляют read-only ссылку и просят смотреть, опытному коллеге выдают полный токен под конкретную задачу, а наблюдателей с читаемыми правами может быть сколько угодно, к примеру вся команда на разборе инцидента. Читаемая ссылка выручает и на демонстрации заказчику: показ идёт в живом терминале, но никто посторонний не набирает команды. Участники с разными ролями подключаются одновременно, и роль каждого определяется только его собственной строкой приглашения. Ту же механику приспособили под собеседования: кандидату достаётся полная строка для решения задачи, а наблюдатели из команды сидят в read-only и видят ход мысли в реальном времени, включая паузы и сомнения.

Токены длинные и случайные, угадать их нереально, но это всё равно секреты: ссылку пересылают лично, а не в общий чат, и не оставляют в переписке после завершения работы. Сессия живёт, пока запущен процесс tmate; выход из сессии или её завершение в tmux закрывают доступ сразу для всех участников, включая наблюдателей. На одну задачу заводят одну сессию и закрывают её вместе с задачей: длинный хвост из забытых приглашений никому ещё радости не приносил.

Порядок удалённого разбора проблемы от приглашения до закрытия сессии

Типовой разбор воспроизводимого только у коллеги бага укладывается в короткую последовательность:

  1. Владелец проблемы запускает tmate и копирует read-only строку подключения;
  2. Ссылка уходит помощнику в личный канал, а не в общий чат;
  3. Помощник подключается, смотрит воспроизведение и задаёт вопросы по ходу;
  4. Для правок владелец выдаёт полный токен или вносит изменения сам под диктовку;
  5. По завершении сессию закрывают, а конфиг с отпечатками остаётся на следующий раз;
  6. Правки фиксируют в системе контроля версий отдельным коммитом, чтобы разбор остался в истории проекта.

Пункты выглядят очевидно, но именно порядок экономит время: read-only этап отсеивает половину вопросов до того, как помощнику понадобятся права на ввод. Схема сходится за минуты даже между городами, без настройки звонков и демонстраций экрана. Хороший тон на продакшене: сначала наблюдатель с read-only правами смотрит и расспрашивает, и лишь затем получает полный доступ, чтобы вносить правки осознанно. Отдельный бонус для CI: сессия tmate прямо на раннере провалившегося задания позволяет заглянуть внутрь окружения, в котором локально баг не воспроизводится, и увидеть настоящие переменные, версии пакетов и права файлов. Отладочный запуск в пайплайне включается на время разбора и выключается сразу после, а маскирование секретов в логах сборки настраивают заранее, потому что приглашение с токеном попадает в вывод задания. Завершают разбор фиксацией правок в системе контроля версий отдельным коммитом, чтобы история изменений осталась читаемой.

Безопасность сессий и корректное завершение работы

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

Завершение тоже дисциплинирует: после работы сессию закрывают командой exit или завершают её в tmux, у пайплайнов отладочный запуск убирают из конфигурации, а журналы сервера хранят как обычные журналы доступа. Узел наблюдают теми же средствами, что и другие сервисы: журналы контейнера, счётчик запущенных сессий и алерт на долгоживущие подключения помогают вовремя замечать забытое. Каталог с ключами узла бэкапят вместе с остальной конфигурацией: без него клиенты с зафиксированными отпечатками откажутся подключаться к пересозданному серверу, и отпечатки придётся менять на всех машинах команды. Скриншоты с открытым приглашением тоже проверяют перед публикацией: токен в кадре остаётся валидным, пока жива сессия. Пара минут на настройку окупается быстро: удалённая помощь перестаёт быть спектаклем у чужого экрана, а отладка вдвоём превращается в честную совместную работу с общим терминалом, где видно всё, история сохраняется и каждый может вмешаться ровно тогда, когда уместно.