Сделайте пересылку IPv4 постоянной после перезапуска VPN-шлюза
Задайте ip_forward в пространстве имён, которое несёт пакеты клиентов, назначьте настройке одного владельца и докажите, что она переживает перезапуск, а не просто редактируете файл.
Пинг шлюза работает, а доступ к чему-либо за ним — нет, и это форма проблемы пересылки, а не проблемы туннеля. Локальная доставка до шлюза доказывает, что туннель поднялся и назначение адреса сработало; она ничего не говорит о том, пропустит ли ядро пакет из одного интерфейса в другой. Прежде чем трогать какой-либо sysctl, убедитесь, что машина предназначена для маршрутизации этого трафика и найдите сетевой контекст, в котором находится интерфейс, обращённый к клиенту. Затем читайте значение там, а не там, куда случайно заглянуло последнее прочитанное руководство.
Доступ к шлюзу — не то же самое, что пересылка
Внутри ядра принимаются два отдельных решения. Доставка на локальный адрес завершает путь пакета; пересылка снова отправляет его наружу через другой интерфейс, и этим вторым решением управляют для каждого сетевого пространства имён. Шлюз может быть полностью доступен при выключенной пересылке, и из командной строки ping обе неисправности выглядят одинаково.
Запишите, на какой интерфейс приходит пакет клиента и через какой интерфейс уходит дальнейший путь. Оба принадлежат одному пространству имён, и именно это пространство имён хранит важную настройку.
Читайте значение там, где пакеты действительно находятся
На шлюзе, который маршрутизирует в пространстве имён хоста, на вопрос отвечает одна команда: sysctl net.ipv4.ip_forward. Когда входящий и дальнейший путь живут в именованном пространстве имён, спросите это пространство: ip netns exec vpn-gw sysctl net.ipv4.ip_forward. Значение хоста не заменяет значение пространства имён, и единица на хосте ничего не доказывает.
Не у каждого контекста маршрутизации есть метка, которую можно передать команде ip netns. Среды выполнения контейнеров и службы создают пространства имён, которые никогда не регистрируются под понятным именем, и для них используются инструменты проверки среды. Подставьте вместо vpn-gw реальное имя или механизм среды, достигающий того же контекста, и перестаньте гадать на уровне хоста.
Назначьте настройке одного владельца
На обычном хостовом шлюзе с systemd постоянной формой является drop-in-файл с выбранным администратором именем, например /etc/sysctl.d/60-vpn-forwarding.conf, содержащий net.ipv4.ip_forward = 1. Запишите файл, затем загрузите именно этот файл, чтобы изменилось и текущее состояние: sudo sysctl -p /etc/sysctl.d/60-vpn-forwarding.conf, после чего прочитайте значение обратно. Имя файла — пример, а не зарезервированный путь VPN, и ведущее число важно, потому что файлы, отсортированные позже, побеждают.
Приоритет — это место, где постоянство тихо ломается. Проверьте и загрузчик дистрибутива, и свой drop-in, потому что procps sysctl --system в конце также читает /etc/sysctl.conf, тогда как порядок sysctl.d в systemd — совсем другая последовательность. Более позднее назначение может сразу вернуть тот же ключ, оставив ваш файл правильным, а действующее значение неправильным. Не перезаписывайте существующий администраторский файл, чтобы выиграть этот спор; найдите владельца и измените там.
Пространство имён, созданное после загрузки, вообще не управляется хостовым drop-in. Его настройка принадлежит тому, что его создаёт: среде выполнения контейнеров или модулю подготовки пространства имён. Давать контейнеру широкие права хоста, чтобы обойти вопрос, — худшая сделка, чем потратить десять минут на поиск правильного владельца.
Знайте, чего стоит переключатель, прежде чем его переключать
ip_forward не безобидный boolean. Документация ядра отмечает, что его изменение сбрасывает несколько других параметров IPv4 к значениям по умолчанию хоста или маршрутизатора, поэтому диагностическое переключение может выбросить настройку, сделанную в другом месте, и даже не упомянуть об этом. Относитесь к нему как к изменению конфигурации с побочными эффектами, а не как к проверке, которую можно выполнить небрежно.
Это также исключает выключение и включение при каждом случае, когда клиент не может достичь хоста. Если значение уже единица, неисправность в другом месте пути, и третья перезапись того же ключа его не сдвинет.
Докажите постоянство, затем проверьте остальную часть пути
Прочитайте значение обратно в четыре момента: сразу после изменения, после появления интерфейса VPN, после переподключения и после планового перезапуска. Значение, которое откатывается в любой из этих точек, имеет второго владельца, и ещё одна разовая запись только скрывает это до следующей перезагрузки.
Включённая пересылка всё ещё оставляет три вещи для проверки. Клиенту нужен маршрут до назначения, шлюзу нужен дальнейший маршрут плюс разрешение им пользоваться, а назначению нужен обратный путь к адресу клиентского туннеля. Частной маршрутизируемой LAN NAT автоматически не нужен, поэтому не добавляйте masquerading, пока не узнаете, какого из трёх не хватает, и проверяйте с предполагаемого клиента, а не с самого шлюза, потому что запрос, начатый на шлюзе, никогда не затрагивает путь пересылки, который вы только что включили. У IPv6 своя политика пересылки и маршрутизации, поэтому работающая проверка IPv4 ничего о ней не говорит.
Держите вместе в runbook путь drop-in, владельца пространства имён и команду обратного чтения. Тот, кто возьмётся за это следующим, должен уметь прочитать значение, увидеть, кому оно принадлежит, и в течение минуты решить, проблема ли это постоянства или остальной части пути.