Привет, Хабр! Представим ситуацию: вы настраиваете сервер, работаете с критически важными процессами или изучаете логи - и внезапно SSH-соединение обрывается. Все не сохранённые изменения улетучиваются, прогресс откатывается, а вам приходится переподключаться и начинать всё заново.

В данной статье представлен скрипт-реаниматор для SSH, который:

  • Восстанавливает соединение при любых обрывах

  • Сохраняет рабочую сессию и историю команд

  • Поддерживает tmux/screen для полного сохранения контекста

  • Может отправлять уведомления о проблемах в Telegram

  • Решение представлено для Bash и PowerShell

Введение

SSH-реаниматор фактически является "обёрткой" вокруг SSH-клиента, которая:

  1. Постоянно мониторит соединение

  2. При обрыве делает паузу, пытается перезагрузиться и восстанавливает предыдущее окружение

  3. Логирует все события, если потребуется анализ

Например: вы запускаете бэкап на 3 часа. Без реаниматора в случае обрыва Вам придётся начинать заново, но не в стиуации с реаниматором. Он восстановит создание бэкапа с прогрессом, на котором произошёл обрыв.

Важное уточнение: скрипт не является заменой полноценного решения в стиле Zabbix, Nagios и тд. Он лишь даёт простой и быстрый способ сделать рутинные подключения устойчивыми к сбоям. Может помочь, к примеру, в ситуации с какой-нибудь простенькой VPS, где Zabbix (и прочие) являются чересчур избыточными решениями.

Принцип работы SSH-реаниматора

1. Детектирование обрыва соединения

SSH использует 2 ключевых параметра для мониторинга активности соединения:

ServerAliveInterval (по умолчанию значение "0" - отключено)

  • Определяет интервал в секундах между keepalive-пакетами

  • Со стороны клиента отправляются пакеты для проверки активности сервера

ServerAliveCountMax (по умолчанию значение "3")

  • Кол-во неудачных попыток проверки перед разрывом

  • Умножается на ServerAliveInterval для получения общего таймаута

Посмотрим подробнее пример расчёта:

ssh -o ServerAliveInterval=20 -o ServerAliveCountMax=4 user@host
  • 20 x 4 = 80 секунд без ответа

2. Механизм бесконечного переподключения

Скрипт, который будет описан ниже, использует цикл while true для постоянного поддержания соединения:

while true; do
  ssh -o ServerAliveInterval=30 user@host
  if [ $? -eq 0 ]; then
    break # Корректный выход
  else
    sleep 5 # Пауза перед повторной попыткой
  fi
done

Ключевые моменты:

  • Проверка кода возврата SSH (0 - успех, иные - ошибка)

  • Задержка между попытками для избежания цикла быстрых переподключений

Bash-скрипт

#!/bin/bash

SERVER="user@example.com"
SSH_OPTS="-o ServerAliveInterval=30 -o ServerAliveCountMax=3"
DELAY=5

while true; do
    echo "[$(date +'%H:%M:%S')] Подключение к $SERVER..."
    ssh $SSH_OPTS $SERVER
    
    if [ $? -eq 0 ]; then
        echo "[$(date +'%H:%M:%S')] Сессия завершена"
        break
    else
        echo "[$(date +'%H:%M:%S')] Ошибка соединения. Повтор через $DELAY сек..."
        sleep $DELAY
    fi
done

Bash-скрипт с восстановлением tmux

#!/bin/bash

SERVER="user@example.com"
SESSION="remote_work"
SSH_OPTS="-o ServerAliveInterval=30 -o ServerAliveCountMax=3"

while true; do
    echo "[$(date +'%H:%M:%S')] Подключение к сессии $SESSION на $SERVER..."
    ssh -t $SSH_OPTS $SERVER "tmux attach -t $SESSION || tmux new -s $SESSION"
    
    if [ $? -eq 0 ]; then
        break
    else
        sleep 5
    fi
done

PowerShell-скрипт

$SERVER = "user@example.com"
$SSH_OPTS = "-o ServerAliveInterval=30 -o ServerAliveCountMax=3"
$DELAY = 5

while ($true) {
    Write-Host "[$(Get-Date -Format 'HH:mm:ss')] Подключение к $SERVER..."
    ssh $SSH_OPTS $SERVER
    
    if ($LASTEXITCODE -eq 0) {
        break
    } else {
        Start-Sleep -Seconds $DELAY
    }
}

Как использовать

  1. Сохранить скрипт:

    nano ssh_reconnect.sh
    chmod +x ssh_reconnect.sh
  2. Запустить с логированием:

    ./ssh_reconnect.sh >> ssh_log.txt 2>&1
  3. Запустить в фоновом режиме:

    nohup ./ssh_reconnect.sh > /dev/null 2>&1 &

Также, ещё пара полезных функций:

  1. Telegram-уведомления:

TELEGRAM_API="https://api.telegram.org/bot{TOKEN}/sendMessage"
CHAT_ID="12345"
MESSAGE="Ошибка SSH соединения с $SERVER"

curl -s -X POST $TELEGRAM_API -d chat_id=$CHAT_ID -d text="$MESSAGE"
  1. Автозапуск через crontab:

    @reboot /path/to/ssh_reconnect.sh
  2. Сохранение истории команд:

    ssh $SERVER "cat >> ~/.persistent_history"

Рекомендации

  1. Для критически важных сессий используйте tmux или screen

  2. Настройте оптимальные значения ServerAliveInterval для вашего соединения

  3. Для долгоживущих сессий рассмотрите возможность использования terminal multiplexers

  4. Включите логирование для анализа причин разрывов

Заключение

Представленные в статье скрипты помогут обеспечить надёжное поддержание SSH-соединения, восстановить сессию после обрывов и сохранить рабочее окружение. Решение можно допиливать и адаптировать под различные требования, расширять функционал.

P.S. Я веду свою группу в Телеграмм, буду рад видеть всех, кому интересен процесс написания скриптов и автоматизация в мире IT.

Комментарии (26)


  1. ferosod
    15.07.2025 13:39

    Важное уточнение: скрипт не является заменой полноценного решения в стиле Zabbix, Nagios и тд.

    А разве заббикс позволяет каким либо образом сохранять контекст после обрыва ssh? Это же система мониторинга просто


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Это было больше про указанную ниже функцию мониторинга с уведомлением в Telegram. Решил на всякий случай уточнить, так как часто читаю комментарии в стиле: "есть же Zabbix" и "зачем городить, всё давно придумано". Возможно в "уточнении" выразился некорректно, прошу прощения.


  1. Nill-Ringil
    15.07.2025 13:39

    1. amurchick
      15.07.2025 13:39

      Ага, я с mosh уже лет 10 как забыл, что такое обрывы сессий.


      1. Nill-Ringil
        15.07.2025 13:39

        Долго его использовал, последние пару лет как-то ушел от него, но решил, что он тут просто один вместо всей статьи может быть


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Согласен, есть mosh, с данным решением я не спорил, но он не даёт гибкости. Писать скрипты - как хобби. Я выливаю всё, что делаю, в статьи на Хабр, кому-то моё решение может помочь, быть полезным и тд.

      Mosh - "монолит", который сложнее адаптировать под специфические задачи.

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


      1. Nill-Ringil
        15.07.2025 13:39

        Я из того поколения в котором каждый админ писал свой биллинг, каждый делал свой дистрибутив OS на базе ядра Linux.

        И я знаю, что не нужно плодить сущности без надобности

        Есть mosh, есть `autossh -t domain.tld "screen -RDA"`. И не нужно плодить бесполезные портянки. Ты просто тратишь свое время, а время это единственное, что мы никогда не можем вернуть.

        Используй готовые решения и не мучайся, а время трать на что-то более полезное, мой тебе совет с высоты возраста и опыта


  1. MrSmitix
    15.07.2025 13:39

    Что только люди не придумают что б не использовать mosh


    1. crawlingroof
      15.07.2025 13:39

      screen


      1. AcidCult
        15.07.2025 13:39

        tmux


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Согласен, есть mosh, с данным решением я не спорил, но он не даёт гибкости. Писать скрипты - как хобби. Я выливаю всё, что делаю, в статьи на Хабр, кому-то моё решение может помочь, быть полезным и тд.

      Mosh - "монолит", который сложнее адаптировать под специфические задачи.

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


  1. Johan_Palych
    15.07.2025 13:39

    Посылать сигнал я-живой через 30 сек:
    ssh -o ServerAliveInterval=30 user@some.host.com
    Или так:

    На сервере в sshd_config:
    ClientAliveCountMax 99999
    ClientAliveInterval 30
    На клиенте в ~/.ssh/config или глобально в ssh_config:
    ServerAliveInterval 30
    ServerAliveCountMax 99999


  1. sirmax123
    15.07.2025 13:39

    А как это работает с двухфакторкой? Наверно никак? (Как и другие варианты)


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Честно сказать, не тестировал. Вероятнее всего, в этом плане скрипты - не идеальное решение.

      Предположу, что можно попробовать настроить вход не по двухфакторной, а для конкретного SSH-ключа, если политика безопасности позволяет.


  1. CaptainFlint
    15.07.2025 13:39

    Не уловил, где он "восстанавливает предыдущее окружение". Я уж не говорю про "восстановит создание бэкапа". Рестартануть SSH я и сам могу, "вверх-Enter". Главная беда обычно в том, что запущенная в SSH-сессии интерактивная программа к этому моменту уже рухнула по SIGHUP, значения всяких переменных потерялись, текущий путь забылся, вся история команд сдохла. Рестарт SSH ничего этого не вернёт.

    Я себе для таких задач EternalTerminal поднимаю, он удерживает контекст практически всегда. Но, к сожалению, он должен устанавливаться в том числе и на сервер, что не всегда возможно.


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Решения в статье действительно не идеальны. Мой посыл оказался неудачен, бывает. Постараюсь подробнее погружаться в тему в дальнейшем.

      Я себе для таких задач EternalTerminal поднимаю, он удерживает контекст практически всегда. Но, к сожалению, он должен устанавливаться в том числе и на сервер, что не всегда возможно.

      Изучу, погляжу, спасибо! И прошу прощения за потраченное время на не самую удачную статью. В своих решениях я использую принцип "адаптивности", с чем скрипты справляются отлично. Понадобилось логирование - дописал, необходим мониторинг с уведомлениями - сделал. При написании не подразумевал, что в большинстве ситуаций при обрывах используют mosh\screen\EternalTerminal.


  1. jackgrebe
    15.07.2025 13:39

    % apt info autossh
    Package: autossh
    Version: 1.4g-1+b1
    Priority: optional
    Section: net
    Source: autossh (1.4g-1)
    Maintainer: Axel Beckert abe@debian.org
    Installed-Size: 94.2 kB
    Depends: openssh-client | ssh-client, libc6 (>= 2.14)
    Enhances: openssh-client, ssh-client
    Homepage: https://www.harding.motd.ca/autossh/
    Tag: implemented-in::c, interface::daemon, network::hiavailability,
    network::server, protocol::ssh, role::program, use::login, use::monitor
    Download-Size: 35.4 kB
    Description: Automatically restart SSH sessions and tunnels
    autossh is a program to start an instance of ssh and monitor it, restarting it
    as necessary should it die or stop passing traffic. The idea is from rstunnel
    (Reliable SSH Tunnel), but implemented in C. Connection monitoring is done
    using a loop of port forwardings. It backs off on the rate of connection
    attempts when experiencing rapid failures such as connection refused.


    1. Nill-Ringil
      15.07.2025 13:39

      autossh без терминального мультиплексора не будет восстанавливать сессию

      А вот скажем с screen будет и восстанавливать, я выше уже приводил вариант `autossh -t domain.tld "screen -RDA"` или примерно аналогично с tmux `autossh -t domain.tld "tmux attach||tmux"`


  1. grucshetsky_aleksei
    15.07.2025 13:39

    mosh, screen, tmux. Для кого вообще эти утилиты написаны были? Нет, надо скриптами извращаться


    1. CaptainFlint
      15.07.2025 13:39

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


      1. adsha
        15.07.2025 13:39

        Что мешает в графическом терминале тупо запустить tmux? Это и отвязка от зависимости качества соединения с инициатором при копировании, и сохранение контекста сессии. При обрыве достаточно `tmux a -t0` при условии, что сессия поднималась тупо командой tmux


        1. CaptainFlint
          15.07.2025 13:39

          Мешает невозможность нормальной прокрутки содержимого. Возможно, я просто не умею его готовить, но когда я пытался нагуглить решения, нашёл только использование встроенного буфера самого tmux'а с довольно неудобными сочетаниями клавиш и невозможностью задействовать стандартную прокрутку из графического интерфейса моей операционки (со стандартными клавишами, колёсиком мыши и пр.).


  1. TimberWolfGBL
    15.07.2025 13:39

    А что, screen отменили?


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Ни screen, ни mosh не отменяли. Моё решение рассчитано на дополнительный функционал, который каждый потенциально может адаптировать под себя. До публикации мне казалось, что тема достаточно интересная. Статья вышла неудачная, признаю. Прошу прощения.


  1. DungeonLords
    15.07.2025 13:39

    Оффтоп. На удаленном сервере у меня в автозапуске создание tmux сессии

    tmux new-session -s q -d 'export LC_ALL=en_US.utf8 &&'
    

    При подключении к серверу по ssh сразу попадаю в открытую tmux сессию с помощью

    ssh q@193.168.02.63 -o StrictHostKeyChecking=no -o BatchMode=yes 2>&1 -t tmux a | tee ssh-session.log
    

    Кто мне подскажет как логировать в файл без этих жутких служебных CSI последовательностей?


    1. eternaladm Автор
      15.07.2025 13:39

      Спасибо за комментарий! Предположу, может попробовать ansifilter? Что-то в стиле:

      ssh q@193.168.02.63 -o StrictHostKeyChecking=no -o BatchMode=yes -t "tmux a" | ansifilter -H | tee ssh-session.log

      И, насколько мне известно, есть tmux pipe-pane,но про него подсказать ничего не могу, к сожалению, только наслышан. Вроде он логирует только после подключения.