Egress IP Family Policy (IPv4/IPv6) (Русский)
Содержание
Заголовок раздела «Содержание»- Что это такое
- Зачем это нужно
- Три значения
- Как это настроить
- Как разрешается
auto - Как обеспечивается использование
ipv4/ipv6 - Совместимость с SOCKS5
- Поведение с запретом переключения
- Модель данных
- Связанная документация
Что это такое
Заголовок раздела «Что это такое»У каждого прокси в реестре есть поле family с тремя возможными значениями, проверяемыми с помощью перечисления Zod:
family: z.enum(["auto", "ipv4", "ipv6"]).optional().default("auto"),По умолчанию поле имеет значение "auto", сохраняющее прежнее поведение двойного стека. Значение ipv4 или ipv6 закрепляет семейство подключений для этого прокси.
Директива повсеместно нормализуется с помощью единой вспомогательной функции, поэтому любое неизвестное значение преобразуется в auto:
export type ProxyFamily = "auto" | "ipv4" | "ipv6";
export function parseProxyFamily(value: unknown): ProxyFamily { return value === "ipv4" || value === "ipv6" ? value : "auto";}Зачем это нужно
Заголовок раздела «Зачем это нужно»Функциональность представлена в PR #3777. Причины её появления:
| Проблема | Что исправляет директива |
|---|---|
| Утечка исходящего трафика, предназначенного только для IPv6, в IPv4 | Если у хоста прокси есть записи как A, так и AAAA (или операционная система предпочитает IPv4), Happy Eyeballs может установить исходящее соединение через IPv4, даже если предполагается путь только через IPv6. Закрепление ipv6 предотвращает такую утечку. |
| Отзыв токенов из-за аномалий общего исходящего IP-адреса | Провайдеры с ротацией (codex/openai) отзывают токены, когда множество учётных записей при высокой нагрузке используют для исходящего трафика один и тот же IP-адрес. Управление семейством исходящих адресов помогает распределять учётные записи по разным предсказуемым маршрутам исходящего трафика (см. диагностику исходящих IP-адресов в src/lib/proxyEgress.ts, которая используется совместно с этой функцией). |
| Детерминированный исходящий трафик для соответствия требованиям и тестирования | Когда необходимо гарантировать, что трафик выходит через конкретное семейство адресов, значения auto недостаточно. |
Директива намеренно задаётся для каждого прокси отдельно, а не глобально: разные прокси в вашем пуле могут иметь разные политики.
Три значения
Заголовок раздела «Три значения»| Значение | Метка в интерфейсе | Поведение |
|---|---|---|
auto |
Авто (dual-stack) |
ОС выбирает семейство. Для хоста прокси, заданного IP-литералом, семейство определяется самим литералом; для имени хоста доступны оба семейства. Это значение по умолчанию. |
ipv4 |
Только IPv4 |
Ограничивает подключение семейством IPv4. Завершается ошибкой, если у хоста прокси отсутствует запись IPv4 (A). |
ipv6 |
Только IPv6 |
Ограничивает подключение семейством IPv6. Завершается ошибкой, если у хоста прокси отсутствует запись IPv6 (AAAA). |
Строки интерфейса находятся в src/i18n/messages/en.json (labelFamily, familyAuto, familyIpv4, familyIpv6, familyHint).
Настройка
Заголовок раздела «Настройка»Панель управления
Заголовок раздела «Панель управления»Селектор находится в форме прокси на вкладке Пул прокси:
- Откройте Панель управления → Настройки → Прокси → Пул прокси
- Добавьте или отредактируйте прокси
- В раскрывающемся списке Семейство IP выберите
Авто (dual-stack),Только IPv4илиТолько IPv6 - Сохраните изменения
Элемент управления отображается компонентом ProxyRegistryManager.tsx (подключённым в proxy/ProxyPoolTab.tsx).
Поле family входит в данные запросов создания и обновления реестра прокси, проверяется схемами createProxyRegistrySchema / updateProxyRegistrySchema (src/shared/validation/schemas.ts) и обрабатывается запросами POST / PATCH /api/v1/management/proxies:
# Создание прокси только для IPv6curl -X POST http://localhost:20128/api/v1/management/proxies \ -H "Content-Type: application/json" \ -d '{ "name": "IPv6 egress", "type": "socks5", "host": "proxy.example.com", "port": 1080, "family": "ipv6" }'
# Переключение существующего прокси в режим только IPv4curl -X PATCH http://localhost:20128/api/v1/management/proxies \ -H "Content-Type: application/json" \ -d '{ "id": "proxy-uuid-here", "family": "ipv4" }'Это же поле принимается во встроенном объекте конфигурации прокси, используемом для записей вышестоящего прокси (upstream_proxy_config.family, см. Модель данных).
Остальную информацию об API для операций CRUD и назначения прокси см. в PROXY_GUIDE.md.
Как разрешается auto
Заголовок раздела «Как разрешается auto»Когда для family задано значение auto, OmniRoute не добавляет никаких директив — URL-адрес прокси используется без изменений, а семейство подключения определяется автоматически.
При формировании URL (proxyConfigToUrl / normalizeProxyUrl в open-sse/utils/proxyDispatcher.ts) прокси со значением auto получает обычный URL без маркера:
const fam = parseProxyFamily(config.family);const normalized = normalizeProxyUrl(proxyUrlStr, "context proxy", { allowSocks5,});return fam === "auto" ? normalized : `${normalized}?family=${fam}`;Во время диспетчеризации (resolveDispatcherFamily) значение auto разрешается в семейство, соответствующее IP-литералу хоста, или в null (позволяя ОС выбрать семейство) для имени хоста:
function resolveDispatcherFamily(parsed: URL): 4 | 6 | null { const directive = parseProxyFamily(parsed.searchParams.get("family") ?? undefined); const literal = detectIpLiteralFamily(parsed.hostname); if (directive === "auto") return literal; // null для имени хоста → выбор делает ОС // ...}Итого:
auto+ хост, заданный IP-литералом (192.0.2.1/[2001:db8::1]) → семейство этого литерала.auto+ имя хоста →null→ стандартное dual-stack-разрешение средствами ОС.
Как применяются ipv4 / ipv6
Заголовок раздела «Как применяются ipv4 / ipv6»Директива, отличная от auto, передаётся как единственный синтетический маркер запроса — ?family=ipv4 или ?family=ipv6 — однократно добавляемый к нормализованному URL прокси. normalizeProxyUrl аккуратно удаляет и повторно добавляет этот маркер ровно один раз, чтобы он никогда не нарушал разбор порта.
При создании диспетчера маркер считывается и преобразуется в конкретное семейство подключений. Если хост является IP-литералом противоположного семейства, OmniRoute выбрасывает исключение (при противоречии используется принцип fail-closed):
const want = directive === "ipv6" ? 6 : 4;if (literal !== null && literal !== want) { throw new Error( `[ProxyDispatcher] Proxy family directive ${directive} contradicts ${literal === 6 ? "IPv6" : "IPv4"} literal host` );}Затем конкретное семейство закрепляется за коннектором:
- HTTP/HTTPS-прокси (
ProxyAgent):proxyTls: { family, autoSelectFamily: false }— отключает Happy Eyeballs, поэтому подключение устанавливается только через выбранное семейство. - SOCKS5-прокси: пользовательский коннектор передаёт
socket_options: { family, autoSelectFamily: false }в SOCKS-клиент (см. Совместимость с SOCKS5).
Совместимость с SOCKS5
Заголовок раздела «Совместимость с SOCKS5»Закрепление семейства работает с SOCKS5-прокси, однако стандартный fetch-socks не предоставляет доступ к параметрам сокета, необходимым для закрепления семейства прокси-перехода. Для этого OmniRoute поставляется с собственным коннектором:
export function buildSocksFamilySocketOptions(family: 4 | 6 | null): Record<string, unknown> { if (family === 6) return { family: 6, autoSelectFamily: false }; if (family === 4) return { family: 4, autoSelectFamily: false }; return {};}Все SOCKS5-подключения проходят через createSocksDispatcherWithFamily независимо от значения family (включая null / auto при использовании имени хоста): buildSocksFamilySocketOptions(null) возвращает {}, а тот же путь SocksClient.createConnection + TLS buildConnector используется с закреплением через socket_options, чтобы Happy Eyeballs не мог выбрать IPv4 для политики исходящего трафика, допускающей только IPv6.
Сама поддержка SOCKS5 включена по умолчанию (её можно отключить с помощью ENABLE_SOCKS5_PROXY=false); см. PROXY_GUIDE.md → Переменные окружения.
Поведение Fail-Closed
Заголовок раздела «Поведение Fail-Closed»Основная цель директивы — отказать, а не незаметно переключиться на неподходящее семейство. Это обеспечивается двумя проверками:
-
Противоречие IP-литерала — директива, противоречащая хосту в виде IP-литерала, вызывает исключение при создании диспетчера (
resolveDispatcherFamily, показанная выше). -
Предварительная DNS-проверка имени хоста — для прокси, заданного именем хоста, с закреплённым семейством
proxyFetch.tsдо отправки исходящего трафика проверяет черезassertHostnameSupportsFamily, что у имени хоста действительно есть запись требуемого семейства:open-sse/utils/proxyFamilyResolve.ts const hasFamily = records.some((r) => r.family === family);if (!hasFamily) {throw new Error(`[ProxyFamily] Proxy host ${host} has no ${family === 6 ? "IPv6 (AAAA)" : "IPv4 (A)"} record; ` +`refusing ${family === 6 ? "IPv6" : "IPv4"}-only egress (fail-closed)`);}При ошибке
proxyFetch.tsпомечает её значениямиcode = "PROXY_FAMILY_UNAVAILABLE"иstatusCode = 503. Ошибка разрешения DNS также обрабатывается по принципу fail-closed (исходящий трафик блокируется).
Для хостов в виде IP-литералов предварительная DNS-проверка ничего не делает: их семейство определяется самим адресом и не требует поиска.
Модель данных
Заголовок раздела «Модель данных»Столбец family был добавлен миграцией 099_proxy_family.sql в две таблицы:
-- src/lib/db/migrations/099_proxy_family.sqlALTER TABLE proxy_registry ADD COLUMN family TEXT NOT NULL DEFAULT 'auto';ALTER TABLE upstream_proxy_config ADD COLUMN family TEXT NOT NULL DEFAULT 'auto';proxy_registry.family— директива для отдельного прокси в записях реестра (src/lib/db/proxies.ts). Запросы разрешения выбираютfamilyвместе с другими столбцами прокси, а отсутствующее или нестроковое значение преобразуется в"auto".upstream_proxy_config.family— директива для записей вышестоящего прокси (src/lib/db/upstreamProxy.ts) с тем же значением по умолчанию"auto".
Когда разрешённый объект прокси содержит значение family, отличное от auto, функция proxyConfigToUrl добавляет маркер ?family=, чтобы закреплённое значение сохранялось на всём пути до диспетчера.
Связанная документация
Заголовок раздела «Связанная документация»📖 Связанная документация:
- Руководство по прокси — полное описание системы прокси: CRUD реестра, четырёхуровневое разрешение, ротация, проверка работоспособности, справочник API
docs/security/STEALTH_GUIDE.md(git; не включается в/docs) — уровни отпечатков TLS и CLI, работающие поверх прокси- Уровни защиты маршрутов — ограничение доступа через loopback-интерфейс для маршрутов, доступных только локально
HagiCode
HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.
Превращайте идеи в полезное ПО с более умным, быстрым и увлекательным агентным рабочим процессом.

- SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
- EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
- FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.