FreeBind против After=: привязка сокета systemd к адресу VPN
FreeBind позволяет systemd владеть слушателем ещё до появления адреса VPN; After= лишь упорядочивает задания. Сравните их и проверьте порядок и готовность.
При загрузке сокет-юнит, привязывающий фиксированный адрес VPN, может отказать ещё до того, как туннель назначит адрес, и служба за ним так и не запустится. Два переключателя выглядят как исправление и решают разные половины этой проблемы. FreeBind позволяет systemd создать слушатель, пока адрес ещё отсутствует. After= лишь упорядочивает задания, поэтому меняет, когда что запускается, но не приближает появление адреса.
Две разные проблемы, два разных переключателя
Первая проблема — сбой привязки: ядро отказывает в прослушивании адреса, которым хост не владеет, поэтому сокет-юнит сразу отказывает, а служба никогда не активируется. Вторая проблема — сбой готовности: адрес в конце концов существует, но что-то пытается использовать его до того, как туннель начнёт передавать трафик. FreeBind решает первую и ничего не делает для второй. Директивы порядка не решают ни одну напрямую, потому что они лишь определяют, что и в какой последовательности запускается. Назвать сбой, который у вас действительно есть, — это и есть всё решение.
Убедитесь, какой из двух у вас, по журналу, а не по описанию службы. Сбой привязки называет адрес и сообщает, что его нельзя назначить. Сбой готовности обычно проявляется позже — как тайм-аут или отклонённое соединение, видимое с другой стороны, при совершенно здоровом сокет-юните в журналах.
FreeBind: слушатель существует до адреса
FreeBind сообщает ядру, что привязка к нелокальному адресу разрешена, поэтому systemd может создать слушающий сокет при загрузке и передать его приложению, когда бы приложение ни запустилось. В этом смысл активации через сокет и причина, по которой сокет может пережить службу за ним.
[Socket]
ListenStream=
ListenStream=10.8.0.1:8443
FreeBind=yes
Пустое назначение очищает записи прослушивания, унаследованные от юнита поставщика, прежде чем добавить новую, что не даёт оставшемуся слушателю wildcard отвечать на всех интерфейсах. Замените адрес и порт своими и проверьте действующий юнит, а не только файл drop-in. Используйте drop-in под именем юнита вместо правки файла пакета на месте, потому что следующее обновление пакета перезапишет правку и не оставит следа о намерении. Затем примените его через процедуру перезагрузки и перезапуска, которую ожидает сокет-юнит.
Справочник по сокетам systemd описывает поведение IPv4 и IPv6 за этой опцией, и важная деталь в том, что она относится к привязке, а не к маршрутизации. Ничто в нём не назначает адрес и не запускает VPN. FreeBind — это разрешение, выданное слушателю, а не замена интерфейса.
After= и Wants=: порядок — это не готовность
Порядок и требование — разные оси, и их смешение даёт юниты, которые запускаются в правильном порядке и всё равно отказывают. After=vpn-client.service означает лишь, что если оба задания запущены, это ждёт запуска другого. Он не притягивает VPN и не ждёт, пока VPN завершит подключение.
Директивы требования, такие как Wants= и Requires=, отвечают за активацию и различаются тем, что происходит при отказе зависимости: одна продолжает, другая останавливается. Ни одна из них не знает, что значит подключённый туннель. Процесс VPN может быть активен без назначенного адреса или с назначенным адресом и без маршрута к партнёру, и оба состояния удовлетворяют зависимость. Если приложению нужно существование адреса, моделируйте это условие явно, вместо того чтобы считать запуск процесса доказательством готовности.
Проверьте, что юнит действительно объявляет, с помощью systemctl cat и systemctl show -p After -p Wants -p Requires. Drop-in из трёх разных каталогов могут добавить директивы, которых вы никогда не писали, а systemd читает только действующий юнит.
Чего network-online.target не делает
Юнит, упорядоченный после network-online.target, ждёт, пока менеджер сети объявит сеть пригодной, а это точка синхронизации при загрузке, а не постоянное условие. Он ничего не говорит о том, поднят ли конкретный туннель, и не сработает снова, когда VPN переподключится после обеда. Считать его монитором переподключения — самое частое ошибочное прочтение этой цели.
Проект systemd прямо документирует это различие, и практическое следствие в том, что всё, чему нужен адрес туннеля, должно само владеть зависимостью. Юнит, который ждёт network-online, а затем привязывает адрес туннеля, верен только когда туннель случайно поднимается первым, а это совпадение, а не проект.
Проверьте жизненный цикл, который вы действительно выбрали
Проверьте чистую загрузку, запрос после появления адреса и поведение после падения и возврата VPN. Эти три случая отделяют работающий слушатель от работающей службы.
systemd-analyze verify myapp.socket
systemctl cat myapp.socket
ss -ltn "sport = :8443"
Первая команда ловит ошибки файла юнита до перезагрузки, вторая показывает объединённый юнит, а третья доказывает, существует ли слушатель и на каком адресе. Слушатель на адресе wildcard там, где вы ждали адрес туннеля, — это ровно тот остаток, который пустой ListenStream должен был убрать. Повторите третью команду после переподключения VPN, чтобы увидеть, сместился ли адрес.
Выбирайте жизненный цикл осознанно: либо свободно привязанный слушатель, который ждёт возвращения стабильного адреса, либо служба, которая останавливается вместе с туннелем и снова запускается при восстановлении. Это разные проекты, и они отказывают по-разному. Затем держите политику доступа привязанной к тем источникам и путям, которые вы задумали, потому что привязка адреса назначения не требует, чтобы принятые пакеты пришли через доверенный интерфейс.