ServerAliveInterval в SSH через VPN: какая сторона отправляет пробу?
ServerAliveInterval — клиентский таймер, который считает неотвеченные пробы, а не секунды. Смотрите, какая сторона их отправляет, почему побеждает порядок блоков Host и как это проверить.
Большинство советов о том, как удержать SSH-сеанс живым, останавливаются на «добавьте ServerAliveInterval 30» и упускают полезные детали. Ключевое слово — таймер на стороне клиента, который срабатывает только когда канал молчит, считает неотвеченные пробы, а не секунды, и живёт в файле, где побеждает первое совпавшее значение. Ошибитесь в этих трёх свойствах, и здоровая VPN-сессия всё равно оборвётся, а мёртвая будет висеть.
Два направления принадлежат разным демонам
ServerAliveInterval читает ваш клиент ssh. ClientAliveInterval читает sshd на дальней стороне и опрашивает в обратном направлении, от сервера к клиенту. Это не синонимы, и они не взаимодействуют, поэтому настройка одного не влияет на поведение другого конца. Их путаница — обычная причина того, что исправление будто бы работает на одном хосте и не работает на следующем.
Оба принимают ноль. При нуле клиент не отправляет вообще ничего, а на стороне сервера ClientAliveCountMax равный нулю отключает разрыв, который иначе последовал бы за неотвеченными проверками.
Что доказывает проба, а что нет
После того как заданный интервал прошёл без данных от сервера, клиент отправляет небольшой запрос по существующему зашифрованному каналу и ждёт ответа. ServerAliveCountMax задаёт, сколько из них могут остаться без ответа, прежде чем сеанс будет разорван. При интервале 60 и числе 3 клиент сдаётся примерно после трёх минут тишины, и эта цифра — свойство вашей конфигурации, а не сетевой стандарт. Оставьте интервал на значении по умолчанию 0, и проба не будет отправлена никогда, поэтому стандартный клиент может бесконечно сидеть на мёртвом соединении.
Поскольку обмен идёт внутри транспорта SSH, ответ доказывает, что процесс SSH на другой стороне всё ещё читает и пишет. О программе, которую вы запустили внутри этого сеанса, он не говорит ничего: клиент базы данных, перенаправленный порт или долго работающий скрипт могут зависнуть, пока транспорт бодро отвечает на пробы.
TCPKeepAlive — отдельный механизм на уровень ниже. Им занимается ядро, он не шифруется, и сторона, у которой завис демон SSH, всё равно может его удовлетворить. Поэтому проба на уровне SSH — более значимый сигнал, когда вам важен сеанс, а не сокет.
Порядок решает, какое значение выживет
OpenSSH применяет первое полученное значение для каждого параметра, поэтому конкретный блок Host должен стоять перед любым более широким блоком, который тоже подошёл бы. Ставьте значения по умолчанию с подстановочными знаками в конце файла, а не в начале, и помните, что блок Match ниже не может отменить уже заданное значение. Справочник по конфигурации клиента OpenSSH прямо формулирует правило первого значения, и его стоит прочитать один раз перед перестройкой общей конфигурации.
Параметры командной строки побеждают файл, что удешевляет временный эксперимент:
ssh -o ServerAliveInterval=15 -o ServerAliveCountMax=4 office-shellЧтобы увидеть, что клиент действительно разрешил, а не то, что вы думаете, что написали, попросите его вывести действующую конфигурацию. В выводе есть объединённые значения для этого имени хоста, и это самый быстрый способ поймать заблудившийся более ранний блок:
ssh -G office-shell | grep -i aliveЧто делают keepalive через туннель
Периодический трафик не даёт истечь привязке NAT или межсетевого экрана с сохранением состояния, что является настоящим преимуществом для VPN, чей шлюз сбрасывает простаивающие потоки через несколько минут. Пробы, приходящие каждые 60 секунд, обновляют эту привязку, не неся никаких прикладных данных.
Они не могут восстановить путь, который исчез. Когда интерфейс туннеля пропадает, пробы терпят неудачу, как и всё остальное, и сеанс заканчивается, как только счётчик исчерпан. Обнаружение живости и восстановление пути — разные задачи, и только первая настраивается в ssh_config. То, что интервал и счётчик действительно дают, — это скорость, с которой клиент замечает, и именно это пользователь ощущает как зависание.
Мультиплексированные соединения добавляют складку. Когда задействован ControlMaster, пробы принадлежат главному соединению, и новый сеанс через существующее главное наследует то, о чём это главное договорилось. Если главное mux зависло, открытие другого окна не проверит путь заново.
Подтверждение настройки от начала до конца
На сервере выведите действующую конфигурацию демона, а не читайте файл и не гадайте, какой include победил:
sshd -T | grep -i clientaliveЗатем понаблюдайте за реальным сеансом с включённым отладочным выводом. Клиент печатает строку тайм-аута, когда счётчик исчерпан, а метки времени вокруг неё показывают, была ли тишина непрерывной или пробы вообще не отправлялись. Сеанс, который умирает без трафика проб, обычно держит ключевое слово в блоке, который так и не совпал.
Стоит добавить две проверки для любого хоста, до которого вы добираетесь только через туннель: убедитесь, что разрешённый интервал — именно тот, который вы задумали, и что этот интервал, умноженный на счётчик, короче тайм-аута простоя на шлюзе VPN. Когда второе число больше, шлюз сначала молча сбрасывает поток, и проба SSH так и не получает шанса ничего сообщить.