iProxy.online logo
Прокси для
Ресурсы
Компания
Search icon
/
RU
English
Português
Русский
Español
Türkçe
Українська
Tiếng Việt
ไทย
中文
हिंदी
Show menu icon

Как SOCKS5 UDP ASSOCIATE убил наш DNS

База Знаний
Средний рейтинг: 0.00 голосов

Почти год назад мы реализовали фичу строго по RFC — и положили продакшен. Откатили, разобрались, нашли причину. Спойлер: дело было не в DNS. Мы просто забыли, что порты — ресурс конечный.

Нужны мобильные прокси?
Создайте прокси прямо сейчас!
Начните 48-часовой пробный период

TL;DR

Мы добавили полноценную поддержку SOCKS5 UDP ASSOCIATE (RFC 1928) на прокси-серверах iProxy.online. Через пару дней DNS перестал резолвить, туннели начали падать, а случайные сервисы уходили в таймаут. Откатили и начали копать. Корневая причина — исчерпание эфемерных портов на Linux: тысячи UDP-ассоциаций держали по выделенному порту, полностью осушив системный пул. Починка свелась к двум параметрам конфигурации. Найти проблему заняло куда больше времени.

Что такое SOCKS5 UDP ASSOCIATE?

SOCKS5, описанный в RFC 1928, поддерживает три команды: CONNECT (TCP-туннелирование), BIND (приём входящих TCP-соединений) и UDP ASSOCIATE (ретрансляция UDP-датаграмм). Большинство провайдеров прокси реализуют только CONNECT. UDP ASSOCIATE — самая сложная команда и единственная, которая нужна для real-time приложений: VoIP, игры, DNS-over-UDP, QUIC.

Как это работает:

  1. Клиент открывает TCP control connection к SOCKS5-серверу.
  2. По этому TCP-соединению отправляет запрос UDP ASSOCIATE.
  3. Сервер выделяет отдельный UDP-сокет на эфемерном порту и возвращает клиенту адрес и номер порта.
  4. Клиент шлёт UDP-датаграммы на этот relay-порт. Сервер пересылает их на целевой адрес и ретранслирует ответы обратно.
  5. Ассоциация живёт, пока не закроется TCP control connection или не истечёт таймаут сессии.

Каждая UDP-ассоциация занимает отдельный эфемерный порт на сервере. Это ключевая деталь.

И вот ловушка. UDP — протокол без соединения. Нет FIN, нет RST. Как узнать, что клиент закончил работу, и освободить порт?

  1. Ждать закрытия TCP control connection — сигнал по RFC. Но клиент может держать его открытым бесконечно.
  2. Idle timeout. Если за X секунд через relay-порт не прошло ни одной датаграммы — убиваем ассоциацию. Но если таймаут слишком щедрый, сокеты копятся.

Мы выставили щедрые таймауты и не ограничили количество ассоциаций на клиента. Итог: сокеты копились.

Наша инфраструктура

В iProxy.online мы управляем инфраструктурой мобильных прокси в 100+ странах и через 600+ мобильных операторов. Реальные Android-телефоны превращаются в прокси-серверы. Если ты только разбираешься в теме, в нашем гайде по созданию 4G прокси-сети подробно описана архитектура. Наши бэкенд SOCKS5-серверы обрабатывают десятки тысяч одновременных соединений.

Около года назад мы решили реализовать полноценный UDP ASSOCIATE — стать по-настоящему RFC-совместимыми и открыть юзкейсы, которые конкуренты не поддерживают: проксирование VoIP, игрового трафика, QUIC-протоколов. Реализация была корректной. Тесты прошли. Выкатили в прод.


Прокси-инфраструктура такого масштаба — это когда каждое решение по протоколу имеет реальные последствия. В iProxy.online каждое Android-устройство работает как независимый мобильный прокси-сервер — SOCKS5, HTTP и теперь полный UDP — с управлением через единый дашборд или Telegram-бот. Хочешь посмотреть, как всё устроено, прежде чем читать, как мы это сломали? Создай мобильный прокси бесплатно на 48 часов — без привязки карты.


Что пошло не так

Через пару дней на продакшен-серверах начали проявляться странные, казалось бы, не связанные друг с другом симптомы:

  • DNS перестал резолвить. Внутренние сервисы не могли найти друг друга по хостнейму.
  • Туннели падали. Управляющая связь с серверами терялась.
  • NTP-синхронизация отвалилась. Часы серверов начали расходиться.
  • Случайные UDP-сервисы уходили в таймаут без видимой закономерности.

Отказ был не плавным, а обрывным. Всё работало нормально часами, а потом несколько сервисов падали одновременно на одном хосте. Мы откатили UDP ASSOCIATE и начали разбирать.

Поиск корневой причины

Первый инстинкт оказался неверным. Проверили DDoS, утечки памяти, нагрузку на диск, CPU — всё в норме. Application-level health checks были зелёными ровно до момента, когда всё умирало.

Прорыв дали метрики уровня ядра, которые большинство команд даже не мониторит:

node_sockstat_UDP_inuse рос в десятки тысяч. На здоровом сервере этот показатель — пара сотен. У нас перевалило за 20K.

Счётчики ICMP Type 3 Code 3 (port unreachable) резко выросли. Это ядро говорит: «Не могу выделить исходный порт для исходящего UDP-пакета».

Ручная проверка подтвердила:

ss -u state all | wc -l
# 28 431

cat /proc/net/sockstat
# UDP: inuse 28419

Диапазон эфемерных портов Linux по умолчанию — 32768–60999, примерно 28 000 портов. Мы использовали почти все.

Каскадный отказ

Арифметика простая. У Linux конечный пул эфемерных портов — около 28K по умолчанию. Каждый UDP ASSOCIATE съедает один. Сотни одновременных ассоциаций плюс медленная очистка — пул исчерпан.

Когда эфемерные порты кончаются, всё на сервере, что пытается открыть новый UDP-сокет, падает. Включая DNS — каждый исходящий DNS-запрос требует эфемерного порта, чтобы отправить пакет на порт 53. Резолвинг имён умирает, и вдруг всё на сервере сломано по причинам, которые к DNS не имеют никакого отношения.

Каскад:

Выделение портов → каждый UDP ASSOCIATE вызывает bind() с портом 0, запрашивая у ядра следующий доступный эфемерный порт.

Накопление портов → порт остаётся занятым до закрытия TCP-соединения или срабатывания idle timeout. Щедрые таймауты — порты копятся быстрее, чем освобождаются.

Исчерпание пула → тысячи ассоциаций держат по порту, весь пул вычерпан. bind() начинает возвращать EADDRINUSE на каждый новый сокет.

Системный отказ → DNS-запросы падают (systemd-resolved тоже нужны эфемерные порты). WireGuard-хендшейки не проходят. NTP не работает. Syslog-over-UDP умирает тихо. Затем отказ DNS вызывает вторичный каскад — всё, что резолвит хостнеймы, перестаёт работать: health checks, подключения к базам, агенты мониторинга. Сервер выглядит «лежащим», хотя CPU, память и диск в норме.

Почему стандартный мониторинг не поймал

HTTP health checks проходили — эндпоинт слушал. CPU, память, диск, пропускная способность — всё штатно. Метрики на уровне процессов — ничего необычного. SOCKS5-процесс был здоров. Исчерпался пул портов ядра, а ни один стандартный дашборд Grafana это не отслеживает.

Единственные метрики, которые сработали, мы добавили почти «на всякий случай»: счётчики сокетов на уровне ядра и частота ICMP-ошибок.

Решение

Починили двумя настройками. Дебаг занял куда больше времени, чем сам фикс.

1. Резко сократили idle timeout. Снизили таймаут бездействия UDP-ассоциаций с минут до секунд. Если через relay-порт за короткий интервал не прошло ни одной датаграммы — ассоциация рвётся, порт освобождается. Большинство легитимных UDP-сессий (DNS-запросы, NTP) завершаются за доли секунды. Долгоживущие сессии (VoIP, игры) поддерживают ассоциацию регулярным трафиком и не страдают.

2. Лимиты на одновременные ассоциации на клиента. Ограничили, сколько UDP-ассоциаций один клиент может держать одновременно. Это не даёт одному пользователю — или кривому клиенту — монополизировать пул портов. Лимит достаточно щедрый для реального использования, но останавливает неконтролируемое накопление.

Вместе эти меры вернули UDP_inuse с 28K до нескольких сотен. Перевыкатили UDP ASSOCIATE с новыми лимитами — с тех пор стабильно.


Сам фикс был простым — сложнее было построить поддержку SOCKS5 UDP ASSOCIATE, которая выдерживает десятки тысяч одновременных сессий и не жрёт системные ресурсы. Нужен мобильный прокси с полноценным UDP relay и правильным управлением жизненным циклом? iProxy.online работает на реальных Android-устройствах через 600+ операторов, с ротацией IP, поштучным управлением устройствами и тарифами от $6/мес. Начать бесплатный 48-часовой триал →


Что нужно мониторить

Если ты эксплуатируешь что-то, что массово открывает UDP-сокеты — SOCKS5 прокси, игровые серверы, VoIP-инфраструктуру, QUIC-балансировщики — добавь в стек мониторинга:

  1. node_sockstat_UDP_inuse (node_exporter). Открытые UDP-сокеты в реальном времени. Норма — пара сотен. Если используешь Prometheus, метрика уже есть — нужна только панель и алерт. Рекомендуем порог — 5 000.
  2. node_netstat_Icmp_OutDestUnreachs (ICMP Type 3 Code 3, port unreachable). Всплески означают, что ядро отвечает на UDP-пакеты, прилетающие на порты, которые никто не слушает. Единицы в минуту — шум. Тысячи в секунду — пожар.
  3. ss -u state all | wc -l — быстрая проверка руками во время инцидента.
  4. cat /proc/net/sockstat — классический однострочник без зависимостей.

Эти метрики не входят в стандартные дашборды. А должны. Подробнее о том, как поддерживать прокси-инфраструктуру в здоровом состоянии — в нашем гайде по оптимизации скорости и стабильности прокси.

Выводы

Порты — конечный ресурс, планируй их расход. Мы бюджетируем CPU, RAM, диск, полосу. Эфемерные порты не бюджетирует никто. На сервере с десятками тысяч соединений ~28K портов — это немного. Расширить диапазон можно через sysctl -w net.ipv4.ip_local_port_range="1024 65535", но даже 64K конечны, если каждая ассоциация держит порт неопределённо долго.

RFC говорит ЧТО, а не КАК. RFC 1928 написан в 1996 году, когда «нагруженный» сервер обрабатывал сотни соединений. Механика протокола описана идеально. Про управление жизненным циклом портов, лимиты ресурсов или graceful degradation — ни слова. Если реализуешь любой протокол на масштабе — читай RFC для корректности, а управление ресурсами проектируй сам.

Метрики ядра ловят то, что пропускают метрики приложений. Health checks, Prometheus-скрейперы, HTTP-пинги — все говорили, что серверы здоровы. Ядро знало лучше. Если в мониторинге нет статистики сокетов и ICMP-счётчиков — у тебя слепая зона для целого класса отказов из-за исчерпания ресурсов. Мы столкнулись с похожей проблемой наблюдаемости при обнаружении скрытых сбоев TLS 1.3 на нашем флоте Android-устройств — другая причина, тот же урок.

UDP требует явного управления жизненным циклом. У TCP есть встроенный lifecycle — соединения открываются, передают данные и закрываются через определённый хендшейк. Порты переиспользуются после TIME_WAIT. У UDP ничего этого нет. Сокет висит открытым, пока что-то явно его не закроет. В relay-архитектурах нужно строить свой lifecycle, иначе потребление ресурсов растёт неограниченно.

Кому ещё стоит об этом задуматься

Это не только проблема прокси. Любая инфраструктура, выделяющая UDP-сокеты по запросам пользователей, может упереться в тот же обрыв:

  • TURN/STUN-серверы для WebRTC — каждый медиа-relay выделяет пару портов.
  • Игровые серверы — сессия каждого игрока может занимать UDP-порт.
  • QUIC-балансировщики — миграция соединений ведёт к накоплению портов.
  • Рекурсивные DNS-резолверы — каждый исходящий запрос использует эфемерный порт.
  • VPN-концентраторы — WireGuard и IPsec IKE работают поверх UDP.

Если ты эксплуатируешь что-то из этого на масштабе — или управляешь фермой мобильных прокси — проверь sockstat сегодня. Возможно, ты ближе к обрыву, чем думаешь.

Итог

Мы реализовали SOCKS5 UDP ASSOCIATE корректно — по RFC, с тестами, задеплоили. И получили системный отказ, невидимый для стандартного мониторинга.

Вывод, который мы теперь считаем железным правилом: любая фича, выделяющая ресурсы на уровне ядра — порты, файловые дескрипторы, записи conntrack — требует явного управления жизненным циклом и бюджетирования ресурсов с первого дня. Не как фикс после первого аутейджа.

Порты как кислород. Не замечаешь, пока не кончатся.


Это реальный production-инцидент iProxy.online. Мы строим инфраструктуру мобильных прокси, превращая Android-телефоны в SOCKS5 и HTTP прокси-серверы в 100+ странах через 600+ операторов. Полная поддержка UDP ASSOCIATE — теперь с правильным управлением ресурсами. Попробовать iProxy →

Часто задаваемые вопросы

Что такое SOCKS5 UDP ASSOCIATE?

SOCKS5 UDP ASSOCIATE — одна из трёх команд, описанных в RFC 1928. Позволяет клиенту ретранслировать UDP-датаграммы через SOCKS5 прокси, выделяя отдельный UDP-сокет на каждую ассоциацию. В отличие от CONNECT (TCP-туннелирование), UDP ASSOCIATE обрабатывает connectionless-трафик — DNS, VoIP, игры, QUIC.

Сколько эфемерных портов в Linux по умолчанию?

По умолчанию — диапазон 32768–60999, примерно 28 000 портов. Проверить текущий диапазон: cat /proc/sys/net/ipv4/ip_local_port_range. Расширить: sysctl -w net.ipv4.ip_local_port_range="1024 65535" — до ~64 000 портов. Но даже расширенного диапазона может не хватить при интенсивной UDP-ретрансляции.

Почему исчерпание эфемерных портов убивает DNS?

Каждый исходящий DNS-запрос требует эфемерного порта для отправки UDP-пакета на порт 53. Когда все эфемерные порты заняты другими UDP-сокетами, ядро не может выделить новый — и DNS-резолвинг отказывает по всей системе, хотя сам DNS-сервер полностью исправен.

Как мониторить расход эфемерных портов на Linux?

Отслеживай node_sockstat_UDP_inuse в Prometheus/node_exporter — количество открытых UDP-сокетов в реальном времени. Для ручной проверки: ss -u state all | wc -l или cat /proc/net/sockstat. Настрой алерты на всплески ICMP Type 3 Code 3 (port unreachable) через node_netstat_Icmp_OutDestUnreachs.

Поддерживает ли iProxy SOCKS5 UDP ASSOCIATE?

Да. iProxy.online поддерживает полноценный SOCKS5 UDP ASSOCIATE наряду с CONNECT и HTTP прокси. После инцидента, описанного в этой статье, мы добавили лимиты на клиента и агрессивные idle timeout для предотвращения исчерпания портов — при полностью рабочем UDP relay для VoIP, игр и QUIC-трафика.


Неважно, эксплуатируешь ли ты TURN-серверы, игровую инфраструктуру или сеть мобильных прокси — управление эфемерными портами критично. iProxy.online — это боевой SOCKS5 с UDP ASSOCIATE, управление через дашборд и API, настройка за 5 минут на любом Android-телефоне. Создай мобильный прокси бесплатно на 48 часов →

Оцените эту статью, если она вам понравилась: