Чи дістанеться скрипт-хук до іншого боку тунелю, залежить від трьох умов: до якої події він прив'язаний, чи OpenVPN ще всередині своєї послідовності запуску, коли скрипт виконується, і чи відокремлена робота від цієї послідовності. Більшість скриптів спотикаються на третьому. Додавання паузи трактує проблему життєвого циклу як проблему часу, і це працює рівно настільки часто, щоб вводити в оману.

Спершу вирішіть подію, потім механізм відкладеної роботи, потім місце, де виконується відкладена робота. Ці три рішення незалежні, і їх змішування — це те, через що хук у два рядки перетворюється на цілий день.

Чому хук чекає на тунель, який чекає на нього

OpenVPN викликає свої хуки синхронно, всередині того самого процесу, який переміщує пакети. up спрацьовує, коли пристрій tun або tap уже існує, але до додавання будь-якого маршруту, а route-up — після маршрутів; жоден із цих моментів не обіцяє, що протилежна сторона відповість. Поки скрипт будь-якого хука ще виконується, демон не пересилає, тому команда, яка очікує, що трафік пройде крізь тунель, чекає на тунель, який чекає на цю команду. Саме це циклічне очікування пояснює, чому ручний запуск проходить, а хук отримує тайм-аут.

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

Вибирайте хук за подією, а не за зручністю

Прив'яжіть up до роботи, якій потрібен лише пристрій, route-up — до роботи, якій потрібні маршрути, а client-connect і client-disconnect залиште для завдань на кожного клієнта на боці сервера. Очищення при від'єднанні має витримувати подвійний запуск, бо повторні підключення через постійний інтерфейс не дотримуються тієї самої послідовності, що повний перезапуск. На сервері client-connect спрацьовує, коли клієнт автентифікується, а не коли стартує демон; це варто підтвердити перед перенесенням логіки туди. Якщо робота належить до життєвого циклу служби, а не до одного з'єднання, юніт systemd — краще місце, ніж будь-який хук.

Команди хука демон виконує безпосередньо, без оболонки між ними, тому ifconfig_remote у рядку up надходить як буквальний текст. Загорніть команду в явну оболонку або тримайте логіку в скрипті, який читає середовище. ipchange — це одна директива, яка додає власні аргументи: вона передає команді адресу й порт, тому скрипт, написаний ні під що, бачить два неочікувані.

Три способи відокремити роботу

Перший варіант — відправити помічник у фон зсередини хука: запустіть його через setsid або systemd-run --scope, закрийте успадковані дескриптори й одразу вийдіть з нулем. Другий — дозволити хуку записати невеликий файл стану, а таймеру systemd або path-юніту діяти за ним; так хук залишається тривіальним, а політика повторів потрапляє туди, де є журнали. Третій — повністю відмовитися від хука й перенести роботу в юніт, упорядкований після служби VPN; це правильна відповідь, коли робота зовсім не прив'язана до з'єднання. Усі три поділяють одне правило: хук має повернутися раніше, ніж йому знадобиться тунель.

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

Відокремлюйте відмови пісочниці від відмов життєвого циклу

Коли скрипт виконується з root-запрошення і падає як служба, причина зазвичай у пісочниці, а не в мережі. Обмежувальний ProtectHome, доступний лише для читання ProtectSystem або робочий каталог, якого не існує можуть зробити скрипт або його дані недоступними, а на хості SELinux файлу потрібен ще й контекст, який дозволяє виконання. Прочитайте юніт через systemctl cat openvpn-server@server і журнал через journalctl -u openvpn-server@server -n 80, потім шукайте помилку прав або контексту, а не тайм-аут з'єднання. Вимкнення пісочниці має бути останнім кроком, а не першим.

Відмова життєвого циклу виглядає інакше: хук стартує, помічник працює, а сокета просто ще немає. Запишіть рядок з позначкою часу із самого хука, від'єднайтеся й підключіться знову, прочитайте події по порядку. Коли помічнику справді потрібен тунель, нехай він опитує готовність, а не спить фіксований час.

Три поля роблять наступний звіт корисним: який хук працював, яким обліковим записом користувався і де зупинився. Без них кожне подальше розслідування починається заново.

Схожі статті