fix(provisioning): remove redundant ufw reload and avoid ephemeral ports - #19
Open
Inhibit0r wants to merge 1 commit into
Open
Conversation
Inhibit0r
force-pushed
the
fix/provisioning-ufw-reload-and-ephemeral-ports
branch
from
September 3, 2026 10:38
8a097bd to
20a57c9
Compare
…ephemeral range Создание HAPP-подписки (`create_profile_all_routes`, 7 маршрутов) занимало 22.9с и не укладывалось в 20-секундный таймаут вызывающего бот-стека. 1. open_firewall_port/close_firewall_port звали `ufw reload` после `ufw allow`/`ufw delete allow`. Он избыточен: правило уже применено к живому iptables (проверено через `iptables -S ufw-user-input` до и после). Каждый вызов стоил 2353мс и сбрасывал conntrack, разрывая активные сессии. На 7 маршрутах это 16.5с из 22.9с. После правки — 6.7с. 2. find_available_random_port выбирал из 30000-60000, что почти целиком лежит внутри эфемерного диапазона ядра (ip_local_port_range, по умолчанию 32768-60999). Проверка `ss -tuln` видит только слушающие сокеты, поэтому порт, занятый исходящим соединением в ESTABLISHED/TIME_WAIT, проверку проходит — а bind() падает уже после рестарта, и safe_restart_xray трактует это как отказ конфига и откатывает профиль. Окно гонки — сам рестарт: xray освобождает свои inbound-порты на ~300мс, и TIME_WAIT только что закрытых им же исходящих соединений могут их занять. Наблюдалось 3 отказа старта из 6. Правка опускает ТОЛЬКО потолок, под ip_local_port_range. Нижняя граница 30000 остаётся нетронутой: замер с мобильного оператора МегаФон (RU) показал, что исходящие TCP на порты <=23295 у него не проходят вообще (0/20), а >=30330 проходят полностью (16/16) — инбаунд на низком порту у таких абонентов был бы недостижим. Если ядро отдаёт эфемерный диапазон выше 60000 или ниже 31000, окно не сужается вовсе. Проверено на Debian 13 / Xray 26.3.27, конфиг из 214 inbound: create-subscription 22912мс -> 6717 и 6404мс, удаление 7-маршрутного профиля ~21с -> 6.3с, 6/6 рестартов xray без коллизий (до правки 3 отказа из 6). validation: 15/16 на Linux/bash5 (единственный сбой — отсутствие `rg` в окружении, с правкой не связан); на macOS/bash3.2 три теста падают на `declare -A`/`mapfile` одинаково до и после правки. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01QPztpCoQCACXjBdeTFJaPj
Inhibit0r
force-pushed
the
fix/provisioning-ufw-reload-and-ephemeral-ports
branch
from
September 3, 2026 10:38
20a57c9 to
3f7c768
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Проблема
Создание HAPP-подписки (
create_profile_all_routes, 7 маршрутов) занимает 22.9 с. Вызывающий с таймаутом 20 с (у меня — бот-стек сVPNBOT_TIMEOUT_SECONDS=20) получает timeout и откатывает профиль.Замер по фазам, Debian 13 / Xray 26.3.27, конфиг из 214 inbound:
open_firewall_port× 7add_inboundбез firewallsafe_restart_xrayПараллельно наблюдались перемежающиеся отказы старта:
Причина 1 — избыточный
ufw reloadopen_firewall_portзовётufw reloadпослеufw allow. Он не нужен —ufw allowуже применяет правило к живому iptables:Один
ufw reloadна 453 правилах стоит 2 353 мс и вдобавок сбрасывает conntrack — то есть создание одной подписки семь раз рвёт живые пользовательские сессии. То же самое вclose_firewall_portна пути удаления;ufw delete allowтоже применяется сразу (проверено тем же способом: правило исчезает изufw-user-inputбез reload).Причина 2 — порты внутри эфемерного диапазона ядра
find_available_random_portвыбирает из30000-60000, что почти целиком лежит внутриip_local_port_range(по умолчанию32768-60999). Проверкаss -tulnпоказывает только слушающие сокеты, поэтому порт, занятый исходящим соединением в ESTABLISHED/TIME_WAIT, проверку проходит — аbind()падает уже после рестарта, иsafe_restart_xrayтрактует это как отказ конфига и откатывает профиль.Окно гонки — сам рестарт: xray освобождает свои inbound-порты на ~300 мс, и TIME_WAIT только что закрытых им же исходящих соединений могут их занять. Наблюдалось 3 отказа старта из 6.
Правка опускает только потолок, под
ip_local_port_range. Нижняя граница30000остаётся нетронутой — и это принципиально: замер с мобильного оператора МегаФон (RU) показал, что исходящие TCP на низкие порты у него не проходят вообще.Со стороны сервера все эти порты отвечают одинаково (
ufwALLOW v4+v6, xray listen, self-connect на публичный IP — OK), то есть режется именно путь. Инбаунд ниже ~30000 у таких абонентов был бы просто недостижим, поэтому нижнюю границу трогать нельзя.Если ядро отдаёт эфемерный диапазон выше
60000или ниже31000, окно не сужается вовсе — правка в этом случае no-op.Результат
create_profile_all_routesopen_firewall_portПроверено
bash -nнаxrayebator,install.sh,update.sh,uninstall.sh— чисто.validation/— 15/16 на Debian 13 / bash 5.2. Единственный сбой,test-legacy-udp443-migration.sh, требуетrg(нет в тестовом окружении) и с правкой не связан.declare -A/mapfile— контрольный прогон на чистомmainдаёт ровно те же 3, регрессии нет./sub→ пинг всех 7 маршрутов → удаление) — 5 прогонов из 5 успешно.Совместимость
Существующие профили и их порты не затрагиваются: новый диапазон действует только на вновь выбираемые порты, ссылки у клиентов остаются валидными. Для инстансов, где inbound'ы уже стоят в эфемерном диапазоне, остаточная гонка снимается на стороне хоста через
net.ipv4.ip_local_reserved_ports— правки кода это не требует.