Почему nc -uz не может доказать, что сервер OpenVPN готов
UDP-проверка netcat сообщает об успехе, даже когда никто не слушает, поэтому она не может доказать, что сервер OpenVPN готов. Проверьте процесс, журнал, слушателя и реального клиента.
Запуск nc -uz против OpenVPN сервера почти ничего не измеряет, и руководство OpenBSD говорит об этом в своих оговорках: UDP-сканирование в этом режиме сообщает об успехе независимо от того, слушает что-нибудь или нет. Код выхода отражает локальную отправку, а не удалённое состояние, поэтому супервизор, который ему доверяет, держит мёртвый сервер помеченным как здоровый, пока таблица процессов выглядит правдоподобно. Замена начинается с решения, на какой вопрос должна ответить проверка, потому что процесс, слушатель, аутентификация и доступность приложения — это четыре разных наблюдения. Проверка, называющая одно из них, полезна; проверка, подразумевающая все четыре, — обуза.
Что UDP-проверка может и чего не может наблюдать
nc -uz отправляет датаграмму и завершается, а возвращаемый статус описывает локальную отправку. Закрытый порт может ответить ICMP port unreachable, но документированное поведение этого режима сканирования не превращает это в ошибку, а отфильтрованный порт вообще не даёт ответа. Поэтому тишина и успех означают для вызывающего одно и то же.
Это делает проверку тестом локального стека, а не сервера. Она не устанавливает, что хост доступен, что порт привязан, что процесс OpenVPN работает или что клиент мог бы пройти аутентификацию. Тест TCP-соединения сильнее, потому что рукопожатие даёт определённый ответ, но только когда развёртывание действительно слушает TCP. Переключение проверки на TCP на том же номере тестирует слушателя, которого может не быть, а против сервера только с UDP оно провалит здоровый сервис.
Согласование проверки с настроенным транспортом идёт до доверия к любому из двух результатов. Развёртыванию с UDP нужен другой сигнал, а не другой протокол.
Три вызывающих, три разных вопроса
Готовность зависит от того, кто спрашивает. Менеджер служб, перезапускающий упавший процесс, должен знать, что экземпляр запустился и привязал свой слушатель. Балансировщик нагрузки должен знать, что клиент, приходящий сейчас, был бы принят. Система мониторинга должна знать, что путь из реальной сети всё ещё несёт сеанс к разрешённому ресурсу.
Это не одна и та же проверка, и одна проверка не может честно покрыть все три. Сначала выберите вызывающего, потом доказательство.
Постройте проверку из текущего экземпляра
OpenVPN пишет строку запуска, когда экземпляр завершает инициализацию, и эта строка — полезное доказательство локального запуска. Читайте её из текущего запуска, а не из конца дописываемого файла, потому что успех прошлой недели читается точно так же, как успех минуту назад. Сервер может завершить инициализацию и до того, как хоть один клиент когда-либо подключился, поэтому строка ничего не говорит об аутентификации.
Для проверки запуска соедините состояние процесса с актуальным доказательством из журнала и слушателем, соответствующим настроенному транспорту. Проверка должна проваливаться, когда процесс работает, но не завершил инициализацию, а это значит, что запрос к журналу нужно ограничить этой загрузкой и этим юнитом.
systemctl is-active openvpn@server
sudo journalctl -u openvpn@server -b --no-pager | grep "Initialization Sequence Completed"
ss -lunp | grep 1194
Три выхода, три утверждения: юнит активен, эта загрузка создала строку инициализации, и что-то держит UDP-порт. Ни одно из них не говорит, что клиент может подключиться. Держите их раздельно на странице состояния, чтобы инцидент не схлопывался в одну зелёную точку.
Сломайте это намеренно и понаблюдайте за проверкой
Проверка заслуживает доверия только после того, как вы увидели её отказ. Остановите сервер, запустите проверку и подтвердите ожидаемый результат. Затем запустите его снова и подтвердите, что проверка восстанавливается без каких-либо других изменений.
sudo systemctl stop openvpn@server
nc -uz 203.0.113.10 1194; echo "exit=$?"
ss -lunp | grep 1194
sudo systemctl start openvpn@server
ss -lunp | grep 1194
Читайте блок строка за строкой. Команда netcat печатает exit=0 , хотя ничего не привязано, и это вся проблема в одной строке. Первый ss ничего не печатает, перезапуск удаётся, а второй ss снова показывает слушателя. Если ваша проверка проходит против остановленного сервера, она измеряет локальный стек и ничего больше.
Обратный отказ тоже важен. Проверка, которая проваливается против здорового сервера, вызывает перезапуски, которые никогда не были нужны, а цикл перезапусков сам по себе является сбоем. Проверьте оба направления, прежде чем подключать проверку к чему-либо, что может на неё реагировать.
Что должна говорить строка состояния
Запишите, какую границу покрывает каждая проверка: процесс, слушатель, аутентификацию или приложение. Одна проверка редко покрывает больше одной, а страница состояния, которая сжимает их в “up”, скрывает разницу, важную во время инцидента. Для сетевого пути используйте реального клиента с выделенной учётной записью, дайте ему завершить рукопожатие и одну безобидную разрешённую операцию, затем отключите. Это упражняет канал управления, политику аутентификации и слушателя за один шаг.
Держите эту учётную запись отдельно от реальных пользователей и дайте ей минимальные права, которые нужны проверке. Проверка готовности с широким доступом — это новая поверхность атаки, добавленная ради удобства. Меняйте её учётные данные, как у любого другого аккаунта, потому что проверка, которая перестала аутентифицироваться, выглядит точно как сервер, который перестал принимать клиентов.
Называйте границу в тексте оповещения, чтобы первый реагирующий знал, смотреть ли на юнит, сокет, сертификат или маршрут. Проверке, которая не может сказать, что доказала, будут доверять по неправильной причине, и именно так проверка netcat вообще выжила.