Короткий ответ

Если TCP/22 устанавливается и вы даже успеваете сделать uptime, а потом сессия умирает — это не «sshd не запущен». Классика: idle NAT/firewall (state timeout 300–900 с), ClientAliveInterval/ClientAliveCountMax на сервере, ServerAliveInterval на клиенте, промежуточный балансировщик. Реже — MaxStartups, LoginGraceTime на этапе баннера, или разрыв из‑за смены IP клиента. Сначала замерьте через сколько секунд и при каком простое, потом крутите keepalive, не Restart=always на ssh.

Хост host.example (10.0.20.10), пользователь admin.

Симптомы и как отличить

  • сессия живёт, пока печатаете, умирает после кофе;
  • scp больших файлов обрывается, интерактив работает — MTU/NAT ALG, не idle;
  • сразу после пароля/ключа — это не принимает подключение / AllowUsers;
  • все протоколы к хосту рвутся — канал, не sshd.
СообщениеЧастая причина
Broken pipe спустя ~5–15 мин idleNAT/stateful FW
disconnect, reason 2, TimeoutClientAlive на сервере
reset mid-transferMTU, conntrack
No route to host внезапноклиент сменил Wi‑Fi/VPN

Возможные причины

  1. Межсетевой экран режет idle TCP (типично 3600 с и меньше).
  2. На сервере ClientAliveCountMax 0 при ненулевом интервале ведёт себя иначе в разных версиях OpenSSH — читайте sshd -T, не форум.
  3. Клиент без ServerAliveInterval за NAT.
  4. TCPKeepAlive yes есть, но keepalive ядра tcp_keepalive_time=7200 — слишком редко для NAT на 30 минут.
  5. MaxSessions/MaxStartups при множестве ControlMaster.
  6. unattended-upgrades перезапускает sshd (обычно не рвёт уже открытые, но socket activation на 24.04 имеет нюансы).
  7. Смена времени на часы ломает Kerberos GSSAPI — NTP.

Диагностика

На клиенте точное время жизни:

date -u
ssh -vvv admin@10.0.20.10
# в другом окне
sleep 300

Ищите в -vvv строки Timeout, wait time, Sending keepalive, packet_write_wait.

На сервере эффективные параметры:

sudo sshd -T | grep -Ei 'clientalive|tcpkeepalive|logingracetime|maxstartups|maxsessions|maxauthtries|usedns'

Журнал разрыва:

sudo journalctl -u ssh -u ssh.socket --since '1 hour ago' --no-pager
sudo grep sshd /var/log/auth.log | tail -n 40

Timeout, idle vs kex_exchange_identification — разные фазы.

Conntrack, если nft/iptables на этом же хосте:

sudo conntrack -L -p tcp --dport 22 2>/dev/null | head
sudo nft list ruleset | grep -i timeout

UFW сам по себе редко рвёт установленные, но REJECT новых при политике — другое. Проверьте, нет ли ufw limit ssh + шквал реконнектов.

Сеть и DNS обратного запроса: UseDNS yes (сейчас по умолчанию обычно no) может задерживать логин, не idle. Если логин долгий — DNS.

Дамп на 20 секунд вокруг обрыва (на jump-хосте):

sudo tcpdump -ni any host 10.0.20.10 and tcp port 22 -w /tmp/ssh.pcap

Ищите RST от промежуточного IP, не от 10.0.20.10.

Решение

Сценарий A. Idle NAT

На клиенте (ваш ноутбук), не обязательно на всех серверах:

# ~/.ssh/config
Host host.example
  HostName 10.0.20.10
  User admin
  ServerAliveInterval 30
  ServerAliveCountMax 3

На сервере, если политика позволяет будить сессии:

# /etc/ssh/sshd_config.d/60-keepalive.conf
ClientAliveInterval 30
ClientAliveCountMax 3
sudo sshd -t && sudo systemctl reload ssh

Reload не должен рвать существующие сессии OpenSSH.

Сценарий B. Ядро TCP keepalive слишком редкое

Если обязаны использовать TCPKeepAlive:

sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes

Снижение tcp_keepalive_time — системный эффект на все TCP, документируйте.

Сценарий C. Обрыв больших копий

Проверьте MTU:

ip -br addr
ping -M do -s 1472 10.0.20.10

PMTU чёрные дыры лечат clamp на роутере, не keepalive.

Сценарий D. Лимиты MaxStartups

В логе Maximum startup connections. Поднимите аккуратно или уберите шквал скриптов с новыми TCP вместо ControlMaster.

sudo sshd -T | grep maxstartups

Сценарий E. VPN клиент рвёт при простое

Это idle VPN, не sshd. Держите VPN keepalive отдельно; SSH keepalive не спасёт, если tun0 умер.

Как замерить idle без догадок «это NAT»

На сервере в уже открытой сессии:

date +%s
# на клиенте оставьте ssh без ввода
# когда умрёт, снова date и вычислите дельту

Если дельта стабильно 300, 600 или 1800 секунд — ищите таймаут state table (часто 3600, но middlebox бывает 300). Если дельта ~ ClientAliveInterval * ClientAliveCountMax — это sshd. Сверьте sshd -T.

Для ControlMaster на ноутбуке админа обрыв multiplex-сокета рвёт все окна сразу — выглядит как «сервер выкинул всех». Смотрите ~/.ssh/config ControlPath.

Политика IPQoS af21 cs1 в новых OpenSSH иногда режется домашними AP. Эксперимент только в клиентском Host host.example: IPQoS none. Не ставьте это в sshd глобально без замера tcpdump DSCP.

Балансировщики L4 (NLB) с idle 350 с убивают SSH независимо от ClientAlive, если keepalive реже. Ставьте интервал заметно короче idle NLB (например 30 с при idle 350).

Как проверить, что проблема устранена

Оставьте сессию без ввода на время, большее прежнего обрыва + 5 минут:

ssh admin@10.0.20.10 'sleep 1200 && echo still-alive'

Повторите с вашего NAT. still-alive должен печататься. Параллельно интерактивная сессия.

Если не помогло

  • Рвётся ровно в 3600 с — ищите timeout балансировщика/облачного NLB.
  • Рвётся только с одного провайдера: CGNAT.
  • После reload ssh сразу всех выкинуло: вы сделали restart сокета на 24.04 — смотрите unit.
  • GSSAPI/Kerberos: часы, не keepalive.

Профилактика

  • Единый фрагмент keepalive в sshd_config.d для jump-хостов.
  • Мониторинг длительности сессий не нужен; нужен runbook NAT timeout.
  • ControlMaster на админских ноутбуках снижает шквал handshake.
  • Не фильтровать ESTABLISHED случайным «переприменить ufw --force».

FAQ

ServerAlive на клиенте или ClientAlive на сервере?

Любой из двух обычно достаточен против NAT. Серверный ClientAlive ещё закрывает мёртвые клиенты. Иметь оба с разумными 30/3 нормально.

TCPKeepAlive vs ClientAlive?

TCPKeepAlive — пустые ACK ядра, часто редкие. ClientAlive — SSH-протокол, предсказуемее через NAT.

Почему tmux спасает?

Процесс на сервере живёт после обрыва TCP. Это не фикс NAT, это страховка работы. Всё равно чините keepalive.

autossh нужен?

Для туннелей — да. Для интерактива достаточно ServerAlive.

OpenSSH и IPQoS ломают Wi‑Fi?

Редко на старых WMM. Симптом — обрыв сразу, не idle. Проверка: IPQoS none в клиентском конфиге как эксперимент, не как глобальный hardening.