Туннель падает, kill switch срабатывает, и загрузка, завершённая на три четверти, либо замирает, либо сразу завершается ошибкой. Эти два исхода связаны с разными решениями о применении правил, а не с разными уровнями защиты. Отброшенный пакет оставляет соединение в ожидании, а отклонённый закрывает его и сообщает приложению. Решить, какое поведение нужно, — вопрос проектирования, а проверка — отдельное упражнение.

Отбрасывание, отклонение и сброс — разные вещи

Правило межсетевого экрана, которое отбрасывает трафик, уничтожает пакет и ничего не возвращает, поэтому отправитель продолжает повторять отправку по своим таймерам. Правило, которое отклоняет с TCP reset, немедленно закрывает соединение, и приложение видит ошибку. Большинство kill switch отбрасывают, потому что отклонение раскрыло бы политику и потому что отбрасывание похоже на поведение мёртвой сети.

Блокировка — это не то же самое, что закрытие сокета. Приложение по-прежнему держит дескриптор в установленном состоянии, и отсутствуют только пакеты. Поэтому страница может минутами висеть на индикаторе загрузки, пока операционная система настаивает, что соединение существует. Читайте документацию о правилах применения, а не выводите её из сообщения приложения, поскольку оба поведения выглядят как упавшая сеть.

Что делает приложение, пока длится разрыв

TCP повторяет отправку с растущими задержками, и стандартный бюджет повторов Linux исчерпывается примерно за четверть часа, а не за секунды. До этого сокет не является ни полезно открытым, ни закрытым. Приложения с собственными keepalive или тайм-аутами запросов часто сдаются раньше, поэтому две программы на одной машине ведут себя по-разному во время одного сбоя.

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

Решите, нужно ли восстановление

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

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

Проверяйте блокировку и восстановление по отдельности

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

ss -tnp state established '( dport = :443 )'
sudo conntrack -L -p tcp --dport 443
sudo tcpdump -ni any 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'

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

Запишите ожидаемое поведение рядом с правилом применения и поставьте дату. Следующему человеку, который изменит это правило, нужно знать, какой из двух контрактов обещало развёртывание.

Похожие статьи