PHP-хук OpenVPN: trusted_ip против ifconfig_pool_remote_ip
Два адреса с двумя разными смыслами, которые читает процесс, запущенный не вами. Что такое переменные хука и почему пустой $_ENV ничего не доказывает.
Пустой массив $_ENV в скрипте хука кажется доказательством того, что OpenVPN ничего не передал. Обычно он доказывает совсем другое: PHP был настроен не заполнять эту суперглобальную переменную, а значения всё это время лежали в окружении процесса. Второй ложный вывод тоньше, потому что две переменные адреса в хуке соединения означают разные вещи, а используются как взаимозаменяемые. Обе ошибки стоят половины дня, если начать с неправильной.
Два адреса, два разных утверждения
Внешняя конечная точка — это то, что сервер наблюдал при подключении клиента, доступна как trusted_ip для IPv4 и trusted_ip6 для IPv6. Виртуальный адрес — это то, что пул выделил для этого сеанса, доступен как ifconfig_pool_remote_ip.
Они отвечают на разные вопросы, и записи обычно нужен один из них, а не оба. Первый говорит, откуда пришло соединение, и это может быть общий адрес NAT, которым пользуются сотни людей. Второй говорит, где этот сеанс живёт внутри туннеля, и позже его можно переназначить другому сеансу. Смешение двух даёт записи, которые выглядят правдоподобно и при этом неверны.
Минимальное чтение выглядит так.
$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner = getenv('ifconfig_pool_remote_ip');
$outer = $outer4 !== false ? $outer4 : $outer6;Ни одно из значений не идентифицирует человека. Одно — сетевое наблюдение, другое — аренда, и запись, которая считает любое из них стабильным идентификатором устройства, окажется неверной в первый же раз, когда клиент переподключится из другой сети или получит другой адрес из пула. Выбирайте поле по вопросу, на который должна отвечать запись, а затем запишите этот вопрос рядом со столбцом.
Проверяйте, как запущен PHP, а не свою оболочку
Хук работает как дочерний процесс демона VPN с окружением, которое создаёт демон. У вашей интерактивной оболочки оно другое, и запуск скрипта вручную проверяет окружение оболочки, а не хука.
Начните с подтверждения, что хук действительно сработал на том этапе, которого вы ждали. Скрипт, привязанный к неверному этапу, или такой, который так и не запустился, потому что директива указывает на другой путь, даёт ровно тот же симптом, что и отсутствующая переменная. Добавьте намеренную строку-маркер в начало скрипта и убедитесь, что она появляется, прежде чем проверять что-либо ещё. Одна строка с отметкой времени в журнале отличает «так и не запускался» от «запускался без значения».
Затем убедитесь, какой интерпретатор и какой файл конфигурации использует этот вызов. Правка конфигурации PHP для веб-сервера ничего не меняет в процессе CLI, запущенном OpenVPN, и оба могут загружать совершенно разные файлы php.ini.
Почему пустой $_ENV ничего не доказывает
PHP справка по getenv описывает прямое чтение окружения процесса, которое возвращает false, когда имени нет. Суперглобальная переменная $_ENV заполняется отдельно, а директива variables_order решает, какие суперглобальные заполняются.
Если эта директива опускает E, $_ENV остаётся пустым независимо от того, что содержит окружение. Код, который к нему обращается, тогда сообщает об отсутствующей переменной, которая на самом деле есть, и расследование уходит в сторону OpenVPN, тогда как ответ лежит в php.ini. Проверяйте директиву именно для конфигурации CLI, а не для веб-сервера.
Чтение через getenv избавляет от всей этой путаницы, а скрипт-обёртка, очищающий окружение перед вызовом интерпретатора, возвращает её в другой форме.
Храните связь, а не обновление строки
Решите, какое значение нужно записи, прежде чем её писать. Если цель — отметить, откуда пришёл сеанс, сохраните внешнюю конечную точку и пометьте её как наблюдение. Если цель — связать сеанс с адресом туннеля на время соединения, сохраните адрес из пула и привяжите его к сеансу.
Обновляйте по аутентифицированной личности и текущему сеансу, а не по каждой строке с одним и тем же адресом туннеля, потому что адреса из пула используются повторно, и позднее событие отключения иначе может перезаписать данные более нового соединения. Обрабатывайте IPv6 явно, а не пишите молча только значение IPv4. Строка с NULL во внешнем адресе лучше строки с неверным, потому что первая видимо неполна.
Привязывайте запись к идентификатору сеанса, который предоставляет OpenVPN, чтобы переподключение создавало новую запись, а не правил старую. Используйте параметризованные запросы и учётную запись только с теми правами, которые нужны операции. Хук работает с полномочиями демона, поэтому небрежный запрос — проблема шире, чем неверная строка журнала.
Диагностируйте, не вываливая окружение
Записывайте, какие имена присутствовали, какой этап выполнялся и какой интерпретатор использовался. Не печатайте всё окружение, потому что процесс хука может унаследовать посторонние значения, которым не место в файле журнала.
Проверьте одно соединение от начала до конца: прочитайте внешнюю конечную точку, прочитайте выданный адрес и сверьте оба с собственным представлением сервера об этом сеансе. Как только пара подтверждена для одного клиента, связи можно доверять и в других местах.