Ubuntu 24.04: SSH-сеанси перериваються випадково — налаштування keepalive, що дійсно допомагають

Було корисно?

Ви перебуваєте посеред зміни в продакшені. Виконуєте ще одну команду, чекаєте виводу… і термінал зависає. Через кілька секунд: «Broken pipe» або «Connection reset». Ваш SSH-сеанс не «випадково» загинув. Хтось у мережевому шляху вирішив, що ваш тихий TCP-потік виглядає як мертвий вантаж.

Ubuntu 24.04 тут не має якоїсь особливої проклятості, але вона сучасна (стандарти OpenSSH за замовчуванням, systemd, поширений NAT, агресивні фаєрволи), тож стара порада на кшталт “set TCPKeepAlive yes” часто виявляється неефективною. Працюймо практично: що перевірити, що змінити і чого не чіпати, якщо хочете, щоб SSH-сеанси виживали в корпоративних мережах, балансувальниках у хмарі та домашніх роутерах, що видають себе за корпоративне обладнання.

Як насправді помирають SSH-сеанси

SSH — це «просто TCP» з великою кількістю криптографії та трохи надії. Коли ваш сеанс обривається, сталося одне з наступного:

  • Таймаут простоя посеред шляху: фаєрвол/NAT/балансувальник вирішує, що простий TCP-потік можна викинути, і видаляє відображення.
  • Флап шляху: пересування по Wi‑Fi, перевстановлення VPN, передача між стільниковими сотами, збої провайдера. TCP-з’єднання не може відновитися.
  • Одна зі сторін здається: клієнт або сервер вирішують, що інший вузол мертвий, і закривають сокет.
  • Видалення через ресурси або політику: sshd перезапускається, хост реботується, агент безпеки завершує довготривалі сеанси або політика PAM/systemd/logind приводить до завершення сесій.
  • PMTU/фрагментація і дивності: пакети потрапляють у чорну діру, keepalive-пакети не проходять, повторні передачі накопичуються, і тоді додаток таймаутиться.

«Keepalive» — це не одна функція. Це сімейство таймерів і запитів на різних шарах:

  • Keepalive протоколу SSH (OpenSSH): зашифровані повідомлення на рівні додатку, які циркулюють як реальний трафік і можуть виявити мертвого пірa.
  • TCP keepalive (ядро): незашифровані TCP-запити, які можуть проходити через NAT/фаєрволи не так, як ви очікуєте.
  • Таймаути старіння сесій у NAT/фаєрволі: пристрій посередині вирішує, як довго «проста» сесія дозволена.

Коли хтось каже «SSH випадково відвалюється», перекладіть це як: «щось обриває мій простий TCP-потік швидше, ніж я очікував». Ваше завдання — знайти, що саме, а потім вибрати найпростіший і найнезначніший спосіб тримати потік непростим або швидко відновлювати його.

Ще одне уточнення реальності: keepalive — це не заміна стійкого робочого процесу. Вони зменшують біль. Вони не роблять TCP безсмертним.

Цікаві факти та історичний контекст (корисна довідка)

  1. SSH з’явився як реакція на незахищені віддалені входи. Раніше віддалений доступ працював через протоколи в відкритому тексті; підняття SSH було радше питанням розумності, ніж тільки безпеки.
  2. OpenSSH не винайшов keepalive, але впорядкував їх. Механізм на рівні протоколу по суті «надішли щось легітимне і чекай підтвердження, що пір живий».
  3. Стандарти за замовчуванням для TCP keepalive відомі як непрактичні для людей. Багато систем історично чекали приблизно 2 години перед першим опитуванням, що добре для довготривалих сокетів баз даних і жахливо для перерви на каву.
  4. NAT-пристрої не є нейтральними спостерігачами. Вони тримають стан для кожного потоку, і цей стан з часом втрачається. Побутова техніка часто агресивніша, ніж корпоративні фаєрволи.
  5. «Broken pipe» — це оболонка, що повідомляє про SIGPIPE. Підлеглий сокет помер; наступний запис зазнає помилки; ваш термінал повідомляє про це своїм драматичним способом.
  6. Станові фаєрволи популяризували «таймаути простоя» як механізм безпеки. Відстеження кожного потоку вічно коштує пам’яті; таймаути — грубий збирач сміття.
  7. Деякі балансувальники навантаження мають окремі таймаути для TCP і HTTP. Якщо ви пропускаєте SSH через пристрій, оптимізований під HTTP, він може трактувати ваш довготривалий TCP-потік як підозрілу меблю.
  8. SSH-мультиплексування (ControlMaster) може приховувати обриви. Ваші «нові» сеанси можуть бути просто новими каналами на старому TCP-сокеті, який ось-ось помре.
  9. IPv6 змінює історію з проміжними пристроями, але не усуває її. Менше NAT не означає відсутності станового фільтрування, а корпоративні мережі досі завершують прості потоки за таймаутом.

Швидкий план діагностики

По-перше: визначте, чи сервер або мережа його вбили

  • На клієнті, відтворіть з підвищеною вербозністю і подивіться на часи та хто ініціював закриття.
  • На сервері, зіставте логи sshd навколо мітки часу розриву.
  • Якщо нічого в логах, припустіть, що проміжний пристрій видалив стан і ваш наступний пакет потрапив у порожнечу.

По-друге: ідентифікуйте «таймаут простоя» проти «флапу шляху»

  • Якщо воно вмирає після передбачуваного вікна простою (10 хв, 30 хв, 60 хв), це політика таймауту десь у шляху.
  • Якщо воно вмирає під час руху (включення/виключення VPN, роумінг Wi‑Fi), це нестабільність шляху; keepalive не вирішить усе, але може скоротити час виявлення.

По-третє: виберіть найдешевше рішення

  1. Клієнтські ServerAlive*, якщо вам потрібно лише стабільність власних сеансів і ви не можете змінювати сервери.
  2. Серверні ClientAlive*, якщо ви керуєте серверами і хочете послідовної поведінки для всіх користувачів.
  3. Ядрові TCP keepalive, якщо вам також потрібні стабільні не-SSH сервіси або ваше середовище блокує SSH-рівневі опитування (рідкісна, але реальна ситуація).

По-четверте: підтвердіть межу таймауту шляху

Не вгадуйте. Виміряйте. Якщо мережа вбиває прості потоки приблизно через ~900 секунд, відправка keepalive кожні 60 секунд підходить; кожні 5 секунд — театральність з додатковим навантаженням.

Практичні завдання: 12+ реальних перевірок з командами, значенням виводу, рішеннями

Ці завдання навмисне в стилі «оператора»: запусти команду, інтерпретуй її, прийми рішення. Фіксуйте мітки часу. Не покладайтесь на відчуття.

Завдання 1: Захопіть фейлуючий сеанс з вербозним SSH

cr0x@server:~$ ssh -vvv ops@db-prod-1
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024
debug1: Server host key: ssh-ed25519 SHA256:...
debug1: Authenticating to db-prod-1:22 as 'ops'
...
debug1: client_input_channel_req: channel 0 rtype keepalive@openssh.com reply 1
...
Write failed: Broken pipe

Що це означає: Якщо ви бачите регулярні повідомлення keepalive@openssh.com і воно все одно обривається, ваш інтервал keepalive може бути занадто довгим для шляху, або шлях флапає (не лише старіння стану).

Рішення: Якщо обриви відбуваються без будь-яких записів про keepalive, увімкніть ServerAliveInterval на клієнті. Якщо keepalive присутні, але обриви тривають, скоротіть інтервал і дослідіть події мережі (VPN/Wi‑Fi) та серверні журнали.

Завдання 2: Перевірте, чи використовуєте ви SSH-мультиплексування (і випадково довіряєте застарілому сокету)

cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'controlmaster|controlpath|controlpersist'
controlmaster auto
controlpath /home/cr0x/.ssh/cm-%r@%h:%p
controlpersist 10m

Що це означає: Ваші «нові» SSH-з’єднання можуть повторно використовувати одне базове TCP-з’єднання. Якщо цей TCP-потік тихо вбивають, ви побачите дивні збої у кількох вкладках.

Рішення: Для нестабільних мереж розгляньте зменшення ControlPersist або вимкнення мультиплексування для цього хосту, щоб зробити помилки більш явними і зменшити площу ураження.

Завдання 3: Побачте серверні причини розриву в логах (journalctl)

cr0x@server:~$ sudo journalctl -u ssh -S "2025-12-29 09:00" -U "2025-12-29 10:00" --no-pager | tail -n 20
Dec 29 09:41:12 db-prod-1 sshd[21877]: Connection closed by 10.20.4.18 port 53122 [preauth]
Dec 29 09:41:18 db-prod-1 sshd[21903]: Received disconnect from 10.20.4.18 port 53144:11: disconnected by user
Dec 29 09:52:07 db-prod-1 sshd[22011]: Timeout, client not responding from 10.20.4.18 port 53410
Dec 29 09:52:07 db-prod-1 sshd[22011]: Disconnecting: Timeout, client not responding

Що це означає: «Connection closed by» може бути ініційовано клієнтом або клієнт реагує на мертвий шлях. «Timeout, client not responding» вказує на спрацювання серверного keepalive (ClientAlive*) або мертвий шлях до клієнта.

Рішення: Якщо сервер таймаує клієнтів, налаштуйте ClientAliveInterval/ClientAliveCountMax. Якщо клієнт «закрив», зосередьтесь на проміжних пристроях і клієнтських налаштуваннях.

Завдання 4: Підтвердіть ефективну конфігурацію sshd (не довіряйтесь файлу)

cr0x@server:~$ sudo sshd -T | egrep -i 'clientalive|tcpkeepalive|kex|loglevel'
clientaliveinterval 0
clientalivecountmax 3
tcpkeepalive yes
loglevel INFO

Що це означає: ClientAliveInterval 0 означає, що сервер не надсилатиме SSH-рівневих keepalive. Багато людей припускають, що TCPKeepAlive yes достатньо. Часто це не так.

Рішення: Якщо ви адмініструєте сервер, встановіть ClientAliveInterval на помірне значення (приклад далі). Якщо не адмініструєте — використайте клієнтський ServerAliveInterval.

Завдання 5: Підтвердіть ефективну конфігурацію клієнта SSH (включно з Match блоками)

cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'serveralive|tcpkeepalive|ipqos'
serveraliveinterval 0
serveralivecountmax 3
tcpkeepalive yes
ipqos lowdelay throughput

Що це означає: На клієнті не ввімкнено keepalive. Якщо мережа видаляє прості потоки, ви ризикуєте.

Рішення: Додайте на рівні хоста або глобально ServerAliveInterval (і розумний ServerAliveCountMax).

Завдання 6: Виміряйте вікно відмови контрольованим тестом простою

cr0x@server:~$ date; ssh -o ServerAliveInterval=0 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 3600'
Mon Dec 29 10:02:01 UTC 2025
connected
Write failed: Broken pipe

Що це означає: Без keepalive сеанс вмирає під час годинного сну. Зафіксуйте мітку часу розриву; повторіть кілька разів — зазвичай побачите щільну зону (наприклад, ~15 хв).

Рішення: Якщо поріг стабільний, налаштуйте keepalive так, щоб вони спрацьовували значно раніше цього вікна.

Завдання 7: Повторіть з агресивним SSH keepalive, щоб перевірити гіпотезу

cr0x@server:~$ date; ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 3600; echo done'
Mon Dec 29 10:10:44 UTC 2025
connected
done

Що це означає: Якщо це живе, проблема майже напевно — втрата стану idle в мережевому шляху.

Рішення: Встановіть менш агресивне, але безпечне значення (30 секунд іноді підходить; 60 секунд часто достатньо; 5 секунд зазвичай — зайва театральність).

Завдання 8: Перевірте значення за замовчуванням TCP keepalive в ядрі (сервер)

cr0x@server:~$ sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

Що це означає: Перший запит через 2 години. Це не «keepalive» у людському розумінні; це археологія.

Рішення: Якщо вам потрібні ядерні keepalive для кількох сервісів, зменшіть ці значення — але зробіть це свідомо й задокументуйте можливі наслідки.

Завдання 9: Перевірте, чи TCP-сеанс показує повторні передачі або зависання (ss)

cr0x@server:~$ ss -tinp '( sport = :22 )' | head -n 12
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0      0      10.10.8.21:22    10.20.4.18:53144 users:(("sshd",pid=21903,fd=4))
	 cubic wscale:7,7 rto:204 rtt:0.356/0.112 ato:40 mss:1448 pmtu:1500 rcvmss:1392 advmss:1448 cwnd:10 bytes_sent:104925 bytes_acked:104901 bytes_received:20631 segs_out:161 segs_in:149 send 325Mbit/s lastsnd:2800 lastrcv:2800 lastack:2800

Що це означає: lastsnd/lastrcv у мілісекундах показує, скільки часу пройшло з останнього трафіку. Якщо ви бачите зростання retrans або роздування rto, у вас проблема якості шляху, а не лише старіння стану.

Рішення: Якщо перед розривом різко зростають повторні передачі, досліджуйте MTU/VPN/Wi‑Fi. Keepalive не врятує чорну діру пакета.

Завдання 10: Шукайте проблеми MTU/PMTU (ping з DF)

cr0x@server:~$ ping -M do -s 1472 -c 3 db-prod-1
PING db-prod-1 (10.10.8.21) 1472(1500) bytes of data.
1472 bytes from 10.10.8.21: icmp_seq=1 ttl=63 time=0.512 ms
1472 bytes from 10.10.8.21: icmp_seq=2 ttl=63 time=0.487 ms
1472 bytes from 10.10.8.21: icmp_seq=3 ttl=63 time=0.499 ms

--- db-prod-1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2040ms

Що це означає: Це свідчить, що шлях 1500 байт працює. Якщо це не вдається, але менші розміри проходять, можлива PMTU-чорна діра (поширено з певними VPN або неправильно налаштованими тунелями).

Рішення: Якщо підозрюєте PMTU, виправляйте мережу/MTU; не загороджуйте це keepalive.

Завдання 11: Підтвердіть, чи фаєрвол на сервері робить щось «корисне»

cr0x@server:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    10.20.0.0/16

Що це означає: UFW зазвичай не повинен довільно скидати встановлені з’єднання, але активне логування або додаткові правила nftables можуть впливати. Це перевірка на здоровий глузд, а не пряма вказівка.

Рішення: Якщо бачите ліміти за швидкістю, виснаження connection tracking або агресивні правила «recent», копайтеся в лічильниках nftables і використанні conntrack.

Завдання 12: Перевірте навантаження на conntrack (виснаження таблиці станів може виглядати як випадкові обриви)

cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 198432
net.netfilter.nf_conntrack_max = 262144

Що це означає: Якщо nf_conntrack_count близький до max, встановлені потоки можуть бути витіснені або нові з’єднання не проходитимуть. Симптоми SSH: переривчасті обриви або неможливість перепідключитися.

Рішення: Якщо ви близькі до ліміту, виправляйте навантаження (занадто багато короткоживучих потоків), збільшуйте розмір таблиці або переносіть станове фільтрування на пристрій, призначений для цього.

Завдання 13: Дізнайтеся, чи sshd перезапускається (і вбиває сеанси)

cr0x@server:~$ systemctl status ssh --no-pager
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Mon 2025-12-29 08:01:12 UTC; 2h 15min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1123 (sshd)
      Tasks: 1 (limit: 18920)
     Memory: 6.9M
        CPU: 1.421s
     CGroup: /system.slice/ssh.service
             └─1123 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"

Що це означає: Якщо час роботи короткий і збігається з обривами, ви женетеся не за тією проблемою: перезапуск вбиває сеанси. Може бути залучено конфігураційне управління, unattended-upgrades або watchdog.

Рішення: Якщо перезапуски корелюють з обривами, усуньте причину перезапусків спочатку. Keepalive не перехитрить демон, що перезапускається.

Завдання 14: Перевірте версію OpenSSH і політику криптографії (рідко, але важливо для краївого кейсу з перевстановленням)

cr0x@server:~$ ssh -V
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024

Що це означає: Ubuntu 24.04 постачається з сучасним OpenSSH. Це добре. Також це означає, що старі припущення щодо сумісності клієнта/сервера можуть провалюватися в краєвих середовищах.

Рішення: Якщо лише старі клієнти обриваються, протестуйте з оновленими клієнтами або відрегулюйте набори алгоритмів на конкретних хостах, але не послаблюйте глобальну безпеку заради одного архаїчного клієнта.

Завдання 15: Доведіть, що це «проста» проблема, генеруючи малонавантажувальний трафік під час сеансу

cr0x@server:~$ ssh ops@db-prod-1 'while true; do date +"%T"; sleep 120; done'
10:31:02
10:33:02
10:35:02

Що це означає: Якщо це тримається нескінченно, тоді ваш інтерактивний сеанс помирає, коли ви перестаєте друкувати — підтверджено, що це проблема таймауту простоя, а не загальна нестабільність.

Рішення: Виправте поведінку простоя за допомогою SSH keepalive або змін політики мережі; не витрачайте час на погоню за завантаженням CPU, якщо логи не вказують на це.

Реальні регулятори keepalive (клієнт, сервер, TCP)

На клієнті: ServerAliveInterval та ServerAliveCountMax

Це найефективніший і найменш політичний фікс, бо вимагає доступу лише до вашої машини. Він надсилає SSH-рівневе повідомлення через зашифрований канал періодично. Якщо сервер (або шлях) мертвий, клієнт помітить це після кількох пропущених відповідей і відключиться. Це корисно: воно швидко фіксує несправність замість того, щоб зависати півгодини.

Як це працює:

  • ServerAliveInterval задає, як часто (в секундах) клієнт надсилає keepalive-запит, коли немає вхідних даних.
  • ServerAliveCountMax задає, скільки невідповідей на keepalive допустити перед тим, як здатися.

Для чого це корисно:

  • Підтримувати стан NAT/фаєрволу «теплим» (бо це реальний трафік).
  • Швидко виявляти мертві сеанси і повертати вам підказку про повторне підключення.

Чого це не робить: Це не збереже ваш термінальний стан при зміні IP. Якщо ви роумите мережею, розгляньте інструмент, розроблений для роумінгу (див. FAQ). Keepalive — це пасок безпеки, не телепортація.

На сервері: ClientAliveInterval та ClientAliveCountMax

Якщо ви керуєте серверами, серверні keepalive — це про політику та гігієну. Ви змушуєте сервер опитувати простих клієнтів і видаляти мертві сесії. Це допомагає:

  • Очищати зомбі-сеанси, коли клієнти зникають за зламаними мережами.
  • Підтримувати стан через проміжні пристрої (той самий ефект, що й на клієнті, тільки ініційований з іншого боку).

Є компроміс: занадто агресивно — ви вбиваєте валідні сеанси на шляхах з високою затримкою або тимчасовою перевантаженістю. Надто лояльно — знову матимете загадкові підвисання.

Ядрові TCP keepalive: TCPKeepAlive і sysctl

OpenSSH має TCPKeepAlive (для клієнта і сервера). Коли увімкнено, ядро надсилає TCP-keepalive-запити згідно таймерів ядра.

Але є нюанс: за замовчуванням ядрові keepalive зазвичай надто повільні, і деякі NAT/фаєрволи трактують TCP-keepalive інакше, ніж реальний прикладний трафік. Крім того, TCP-keepalive може утримувати зламане відображення «напівживим» достатньо довго, щоб вас заплутати. SSH-рівневі keepalive зазвичай зрозуміліші і легше налаштовуються для окремих хостів.

Коли я справді рекомендую налаштовувати ядрові keepalive:

  • У вас кілька довготривалих TCP-сервісів (не лише SSH), які страждають від тих самих обривів простоя.
  • Ви не можете гарантувати застосування SSH-конфігів на клієнтах (флот клієнтів, неманеджовані ноутбуки).
  • Ви стандартизуєте поведінку в контрольованому середовищі (bastion, jump hosts).

Економіка таймаутів: підбирайте інтервали за найслабшим проміжним пристроєм

Якщо фаєрвол закриває прості TCP за 10 хв, keepalive кожні 5 хв працюватиме. Keepalive кожні 30 секунд теж працюватиме, але це зайвий шум. Ціль — «комфортно нижче найкоротшого таймауту, яким ви не керуєте».

Правило великого пальця, яке витримує реальність: 30–60 секунд зазвичай безпечно для ворожих мереж. 120 секунд підходить у добре керованих дата-центрах. Все, що менше 15 секунд, часто означає, що ви налагоджуєтесь через забобони.

Жарт #1: Якщо ваш інтервал keepalive для SSH — 1 секунда, ви не тримаєте сеанс живим — ви просто даєте фаєрволу роботу.

Одна цитата, щоб залишатися чесним

Парафразована ідея (Вернер Фогельс, контекст надійності/операцій): «Усе ламається; проектуйте, виходячи з того, що відмова — нормальна річ.»

Це правильна ментальна модель. Keepalive не запобігає відмовам; вони роблять відмови передбачуваними і виявлюваними.

Три корпоративні історії з практики

1) Інцидент через хибне припущення: «Фаєрвол stateful, отже він нас пам’ятатиме»

Команда керувала набором jump hosts для доступу інженерів до продакшен-баз. Сеанси «випадково» обривалися — переважно під час інцидентів, звісно. Люди звинувачували jump hosts. Хтось навіть патчив ядро «на всяк випадок», що вражаюче відволікає час.

Хибне припущення було тихим і руйнівним: «stateful фаєрвол пам’ятає нас, доки з’єднання не закриється». Насправді фаєрвол мав таймаут простоя для встановлених TCP-потоків. Якщо SSH-канал був тихим — читання логів в іншій вкладці, очікування — відображення зникало.

Вони пробували підвищувати серверні ліміти, піднімати числення дескрипторів, правити логування sshd. Нічого не змінилося. Ключовою підказкою був годинниковий паттерн: розриви збиралися навколо стабільного вікна простою. Як тільки вони провели контрольований тест sleep 3600 з і без ServerAliveInterval, все стало соромно очевидним.

Вирішення не було героїчним: на jump hosts встановили клієнтське ServerAliveInterval 30 (і порекомендували для ноутбуків), а на серверах — ClientAliveInterval 60 для прибирання зомбі. Постмортем інциденту був коротким і трохи принизливим — найкращий варіант.

2) Оптимізація, що дала зворотний ефект: мультиплексування скрізь

Інша організація стандартизувала SSH-мультиплексування, бо це прискорює повторні з’єднання. ControlMaster + ControlPersist прекрасні, коли ви виконуєте багато коротких команд (автоматизація, перевірки флоту). Приріст продуктивності реальний.

Потім команда VPN розгорнула «розумний» клієнт тунелю, що періодично перевстановлював маршрути. Базове TCP-з’єднання ставало в стані застою або сиротіло. Через мультиплексування інженери не бачили відразу «з’єднання впало». Вони бачили випадкові збої: зависання scp, нові ssh-сеанси, що відразу падають, термінали, які приймають введення, але не віддають вивід.

Оптимізація перетворила один мертвий сокет у спільну залежність. Один мертвий контрольний сокет міг зламати десять вкладок. Режим відчувався хаотичним, бо «нові сеанси» насправді не були новими.

Кінцеве рішення було тонким: зберегти мультиплексування для автоматизації і стабільних внутрішніх мереж, але вимкнути його (або тримати ControlPersist коротким) для хостів через хиткі VPN. Також посилили ServerAliveInterval, щоб контрольний сокет швидко помирав при збої VPN, дозволяючи новим з’єднанням створити свіжий сокет.

3) Нудна, але правильна практика, що врятувала день: явні стандарти keepalive

Фінансовий сервіс мав політику: всі bastion і адміністраторські робочі станції мали базову SSH-конфігурацію з keepalive, а сервери застосовували відповідну політику. Це не було гламурно. Це було задокументовано. І це було послідовно розгорнено.

Одного дня мережеві зміни ввели коротший таймаут простоя на наборі сегментаційних фаєрволів. Наслідки були майже нульовими. Інженери помітили кілька сеансів, що випадково обривалися швидше (бо виявлення через keepalive стало швидшим), але загальний рівень «випадкових» відключень не спостерігався.

Що врятувало їх — це нудна узгодженість: клієнт надсилав keepalive кожні 60 секунд; сервер видаляв мертвих клієнтів через кілька хвилин; sysctl ядра TCP keepalive були в розумних межах на bastion. Коли мережа посилилася, їхні сеанси й далі генерували достатньо періодичного трафіку, щоб залишатися «не простими».

Жарт #2: Найнадійніша система — та, яку ви можете пояснити аудитору без сліз.

Поширені помилки (симптом → причина → виправлення)

1) Симптом: «Broken pipe» через ~10–30 хв бездіяльності

Причина: Таймаут простоя в NAT/фаєрволі/балансувальнику. Відображення згорає; наступний пакет скидають або поміщають в чорну діру.

Виправлення: Встановіть ServerAliveInterval 30–60 і ServerAliveCountMax 3. Якщо ви також керуєте серверами, встановіть ClientAliveInterval аналогічно.

2) Симптом: Сеанс зависає (немає виводу), потім зрештою обривається

Причина: Чорна діра шляху або асиметричні втрати; TCP не одразу знає, що пір недоступний. Немає прикладного трафіку, щоб виявити відмову.

Виправлення: Увімкніть SSH keepalive, щоб SSH виявляв відсутність відповіді і коректно закривав сеанс. Якщо це корелює з рухом VPN/Wi‑Fi, вважайте це нестабільністю шляху і розгляньте роумінг-інструмент.

3) Симптом: Кілька терміналів вмирають одночасно; нові SSH-команди відразу падають

Причина: ControlMaster мультиплексування повторно використовує мертвий контрольний сокет, або одне TCP-з’єднання обслуговує багато сеансів.

Виправлення: Зменште ControlPersist, вимкніть мультиплексування для проблемного хосту і переконайтесь, що ввімкнено keepalive, щоб контрольний сокет швидко помирав.

4) Симптом: Серверні логи показують «Timeout, client not responding»

Причина: Серверний ClientAliveInterval ввімкнено і спрацьовує, або мережевий шлях блокує відповіді достатньо довго, щоб перевищити поріг.

Виправлення: Залишайте ClientAliveInterval, але встановіть реалістичний ClientAliveCountMax (часто 3). Якщо клієнти на лінках з великою затримкою, збільшіть count або інтервал.

5) Симптом: Обриви збігаються з перезапусками sshd або unattended-upgrades

Причина: Перезапуск служби вбиває сеанси. Це не питання keepalive.

Виправлення: Контролюйте частоту перезапусків, використовуйте вікна обслуговування або забезпечуйте стабільність bastion-хостів. Перевірте journalctl і systemctl status.

6) Симптом: Лише великі виводи (або scp) падають; інтерактивне друкування працює

Причина: PMTU/фрагментація або проблемний MTU тунелю. Малі пакети проходять; більші — в чорній дірі.

Виправлення: Перевірте DF-пінгами і виправте MTU. Keepalive не вирішить чорні діри пакетів.

7) Симптом: Повторні підключення іноді не вдаються; іноді SSH не може встановити з’єднання

Причина: Виснаження таблиці conntrack на фаєрволі або хості; або правила лімітування за швидкістю.

Виправлення: Перевірте nf_conntrack_count, проаналізуйте правила фаєрволу, налаштуйте розміри conntrack і зменшіть агресивний трафік в інших місцях.

8) Симптом: Працює з однієї мережі, падає з іншої

Причина: Інші проміжні політики: корпоративний фаєрвол, гостьовий Wi‑Fi з captive portal, ISP CGNAT.

Виправлення: Використовуйте блоки конфігурації SSH за хостами або мережами. За потреби розгляньте альтернативний транспорт (VPN/bastion) замість битви з кожною проблемною мережею.

Чек-листи / покроковий план

Покроково: стабілізувати власні SSH-сеанси (тільки клієнт)

  1. Виміряйте вікно відмови. Запустіть тест простою без keepalive і спостерігайте, коли воно обривається.

    cr0x@server:~$ ssh -o ServerAliveInterval=0 ops@db-prod-1 'echo connected; sleep 1800'
    connected
    Write failed: Broken pipe

    Рішення: Якщо воно обривається близько до послідовного часу, ви маєте справу з таймаутом простоя.

  2. Підтвердіть тимчасовим keepalive.

    cr0x@server:~$ ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 1800; echo survived'
    connected
    survived

    Рішення: Якщо це виживає, внесіть це в ~/.ssh/config.

  3. Застосуйте конфіг глобально, перевизначайте за потребою.

    cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'serveraliveinterval|serveralivecountmax'
    serveraliveinterval 60
    serveralivecountmax 3

    Рішення: Підтвердіть, що ефективні значення такі, як ви планували (Match блоки можуть вас здивувати).

  4. Якщо ви роумите мережами, припиніть очікувати, що TCP переживе зміну IP. Використовуйте інструмент, призначений для роумінгу, або приймайте перепідключення і користуйтесь tmux/screen на сервері (FAQ).

Покроково: стандартизувати поведінку на серверах (підхід для флоту)

  1. Перевірте ефективну конфігурацію sshd.

    cr0x@server:~$ sudo sshd -T | egrep -i 'clientalive|tcpkeepalive'
    clientaliveinterval 0
    clientalivecountmax 3
    tcpkeepalive yes

    Рішення: Якщо clientaliveinterval 0, ви не опитуєте простих клієнтів.

  2. Додайте drop-in політику keepalive.

    cr0x@server:~$ sudo tee /etc/ssh/sshd_config.d/50-keepalive.conf > /dev/null <<'EOF'
    ClientAliveInterval 60
    ClientAliveCountMax 3
    TCPKeepAlive yes
    EOF
  3. Перевірте і перезапустіть.

    cr0x@server:~$ sudo sshd -t
    cr0x@server:~$ sudo systemctl restart ssh

    Рішення: Якщо sshd -t падає, не перезапускайте; спочатку виправте синтаксис.

  4. Спостерігайте зміни в логах після розгортання.

    cr0x@server:~$ sudo journalctl -u ssh -S "now-1h" --no-pager | egrep -i 'timeout|disconnect|closed' | tail -n 20
    Dec 29 11:21:07 db-prod-1 sshd[24102]: Timeout, client not responding from 10.20.4.18 port 54119
    Dec 29 11:21:07 db-prod-1 sshd[24102]: Disconnecting: Timeout, client not responding

    Рішення: Якщо таймаути різко зростуть, ваш інтервал/count може бути занадто агресивним для вашого середовища.

Покроково: вирішіть, чи потрібно налаштовувати ядро TCP keepalive

  1. Перевірте поточні sysctl (tcp_keepalive_time особливо).
  2. Зробіть інвентар користувачів хоста. Зменшення keepalive впливає на всі TCP-сокети (БД, агенти, експортери).
  3. Змінюйте через /etc/sysctl.d/ і вимірюйте трафік/поведінку.
  4. Швидко відкотіть, якщо побачите неочікувані побічні ефекти (деякі додатки вже мають власні keepalive).

FAQ

1) Що вибрати: ServerAliveInterval чи TCPKeepAlive?

Спочатку використовуйте ServerAliveInterval. Це SSH-рівневе, налаштовується на хост і зазвичай надійно підтримує стан NAT. Тримайте TCPKeepAlive yes, але не покладайтеся на дефолти ядра.

2) Які значення слід встановити для keepalive?

Почніть з ServerAliveInterval 60 і ServerAliveCountMax 3 на клієнтах. Для суворіших мереж використовуйте 30 секунд. На серверах — ClientAliveInterval 60 і ClientAliveCountMax 3 як розумна база.

3) Чому мій сеанс вмирає навіть під час набору тексту?

Це зазвичай не таймаут простоя. Шукайте втрати пакетів, зміну маршрутів VPN, роумінг Wi‑Fi або проблеми MTU. Перевірте ss -tinp на наявність повторних передач і запустіть DF-ping для тесту MTU.

4) Чи викидає ClientAliveInterval користувачів?

Може, якщо встановлено занадто агресивно. Воно призначене для видалення мертвих сеансів, а не для покарання повільних лінків. Якщо бачите хибні відключення — збільшіть ClientAliveCountMax або інтервал.

5) Чи небезпечні keepalive для безпеки?

Не за своєю суттю. Вони надсилають мінімальний автентифікований трафік. Справжнє питання безпеки — чи ви тримаєте сеанси живими, які мають завершуватися за політикою. Якщо організація вимагає автоматичного логаута за простоєм, keepalive може суперечити цій політиці.

6) Я за корпоративним проксі/фаєрволом, що вбиває SSH. Чи допоможе keepalive?

Лише якщо SSH дозволений, але прості потоки видаляються за таймаутом. Якщо мережа активно блокує SSH або виконує глибоку інспекцію, яка руйнує довготривалі сеанси, вам може знадобитися санкціонований bastion/VPN-підхід.

7) Чи варто налаштовувати Linux net.ipv4.tcp_keepalive_* на Ubuntu 24.04?

Лише якщо у вас є системно-широка потреба. Для одного SSH краще використовувати OpenSSH keepalive. Якщо налаштовуєте ядро, задокументуйте це і розумійте, що це впливає на всі TCP-з’єднання.

8) Мій SSH-сеанс обривається, коли я закриваю кришку ноутбука. Чи пов’язано це з keepalive?

Не зовсім. Ймовірно, ноутбук переходить у сплячий режим і мережа вимикається; TCP-з’єднання стає застарілим. Keepalive може швидше виявити застарілий сеанс, але не примусить призупинений мережевий адаптер продовжувати працювати.

9) А як щодо tmux або screen?

Робіть це. Серверні мультиплексери терміналу не запобігають розривам, але запобігають втраті роботи. Поєднайте tmux з keepalive — отримаєте менше обривів і менше болю, коли обрив станеться.

10) Чи є кращий інструмент, ніж SSH, для роумінгу мереж?

Так: існують інструменти, орієнтовані на роумінг, спеціально розроблені, щоб переживати зміну IP і переривчасте підключення. Keepalive допомагає SSH, але не змінює фундамент TCP.

Наступні кроки, які можна зробити вже сьогодні

Ось продакшенний шлях виходу з «SSH випадково обривається»:

  1. Доведіть режим відмови. Виміряйте, чи це таймаут простоя або нестабільність шляху за допомогою тесту простою (sleep) і вербозних логів.
  2. Увімкніть клієнтські SSH keepalive. Встановіть ServerAliveInterval 60 (30 у ворожих мережах) і ServerAliveCountMax 3.
  3. Якщо ви керуєте серверами, додайте серверні keepalive. Використайте drop-in файл з ClientAliveInterval 60 і ClientAliveCountMax 3 для прибирання мертвих сеансів.
  4. Лише потім розгляньте налаштування ядра TCP keepalive. Це ширший вплив, іноді необхідний, часто зловживають ним.
  5. Зменште несподіванки. Якщо мультиплексування перетворює один мертвий сокет у багато зламаних сеансів, застосовуйте його вибірково.

Якщо ви зробите ці кроки в такому порядку, ви припините сприймати SSH-обриви як погоду і почнете трактувати їх правильно: як політику таймауту, що зустріла простий TCP-потік. Виправляється. Передбачувано. Інколи ще дратує — бо мережі — але принаймні більше не містично.

← Попередня
Debian/Ubuntu «Підключено, але немає інтернету»: виправлення маршрутизації, шлюзу та політики маршрутизації
Наступна →
Відновлення завантажувального пулу ZFS: повернення системи після невдалого оновлення

Залишити коментар