Написать базовый STUN и TURN сервер на Go с использованием библиотеки pion/turn/v4 можно за один вечер в сотню строк кода. Но как только такой прототип попадает на боевой сервер с сотнями одновременных медиапотоков, вскрываются системные проблемы очередь одного UDP сокета упирается в блокировки ядра Linux, мобильные клиенты при смене сети забивают пул портов мертвыми аллокациями, а открытый релей становится мишенью для сканирования внутренней облачной сети.
В прошлой статье про альтернативу Coturn на Linux я разбирал операционные проблемы ручной настройки конфигурационных файлов и причину перехода на единый исполняемый файл. Ниже подробно разберем внутреннее устройство Slyber TURN Server и технические решения, которые позволили выжать максимум из сетевого стека Linux внутри одного статического бинарника.
Обход узкого горлышка одного UDP сокета через SO_REUSEPORT и тюнинг буферов ядра
В стандартной реализации сетевого сервера приложение открывает один UDP сокет на порту 3478 и читает из него пакеты в цикле. Под высокой нагрузкой в WebRTC трафике, где идут тысячи мелких аудио и видео пакетов в секунду на каждую сессию, единый сокет становится главной точкой деградации. Ядро Linux тратит процессорное время на блокировки очереди приема одного файлового дескриптора, пока соседние ядра процессора простаивают.
В slyber_turn сетевой уровень изначально спроектирован под многоядерную обработку:
- Масштабирование листенеров по числу ядер. При старте сервер считывает количество доступных ядер процессора
TURN_THREADS=NumCPUи поднимает независимые UDP сокеты на одном и том же порту с системным флагомSO_REUSEPORT. Распределение входящих датаграмм по потокам берет на себя сетевой стек ядра Linux без конкуренции за блокировки в пространстве пользователя. - Автоматическое расширение сокетных буферов до 8 МБ. Дефолтный лимит
net.core.rmem_maxиnet.core.wmem_maxв 208 КБ переполняется при первом же всплеске битрейта от видеокамер. Установочный скрипт и сам сервер поднимают системные буферы чтения и записи до 8 МБ, полностью исключая тихие потери пакетов на уровне сетевой карты и ядра. - Генерация сертификатов в памяти. Для шифрованного транспорта не требуется возиться с файлами ключей на диске: сервер на лету генерирует в оперативной памяти сертификат на базе кривой
ECDSA P-256.
Атомарный шейпинг трафика и очистка сокетов при переходе с WiFi на LTE
Вторая проблема боевой эксплуатации связана с контролем ресурсов. Пул релейных портов от 49152 до 65535 вмещает 16384 одновременных сессии. В реальных условиях этот пул быстро деградирует по двум причинам: бесконтрольное потребление полосы пропускания отдельными пирами и зависшие мобильные соединения.
Когда пользователь смартфона выходит из зоны домашнего роутера и переключается с WiFi на мобильную сеть 4G или LTE, его IP адрес мгновенно меняется. Клиентское приложение запрашивает новую аллокацию на TURN сервере, но старый релейный сокет продолжает висеть в памяти до истечения стандартного многоминутного времени жизни. Если таких переключений тысячи, сервер исчерпывает лимит портов.
В slyber_turn эта задача решена через привязку аллокаций к идентификатору пользователя:
- При запросе ключей через встроенный эндпоинт
/api/turn-credentials?user_id=user_42идентификатор зашивается в структуру сессии. - Как только тот же
user_idподнимает новую активную сессию с другого сетевого интерфейса, старые неактивные сокеты этого пользователя принудительно очищаются и возвращаются в пул через 5 секунд. - Для защиты канала от перегрузки на каждую сессию навешивается неблокирующий ограничитель скорости
MAX_KBPS_PER_SESSION, построенный по алгоритму Token Bucket на атомарных операцияхsync/atomicбез использования мьютексов.
| Сетевой механизм | Реализация в Slyber TURN Server | Эффект в продакшене |
| Прием UDP трафика | Пул сокетов с флагом SO_REUSEPORT | Равномерная загрузка всех ядер CPU |
| Буферы сокетов ядра | Автоматический тюнинг до 8 МБ | Отсутствие потерь при пиках видео |
| Шейпинг полосы | Атомарный Token Bucket без мьютексов | Стабильная задержка без блокировок потоков |
| Смена сети WiFi и LTE | Группировка по user_id и сброс за 5 с | Защита пула из 16384 портов от утечек |
Фильтрация SSRF атак в облаках с сохранением поддержки TURN Hairpinning
Поскольку TURN сервер пересылает трафик от имени своего сетевого интерфейса, любая ошибка в фильтрации адресов назначения превращает его в открытый прокси внутрь вашей серверной инфраструктуры. Через команды CreatePermission и ChannelBind злоумышленник может попытаться прочитать метаданные облачного провайдера или просканировать соседние сервисы в локальной сети.
В slyber_turn фильтр безопасности работает на уровне ядра релея до отправки первого пакета и включен по умолчанию без настройки внешних списков доступа:
- Жёстко блокируются обращения к облачным метаданным IPv4
169.254.0.0/16и IPv6fd00:ec2::254/128, петлевым интерфейсамloopbackи всем приватным диапазонам локальных подсетей. - Автоматически отсекается сетевой шум, когда браузеры по ошибке пытаются пробросить через TURN свои внутренние локальные кандидаты.
- При всей строгости фильтра полностью сохранена поддержка TURN Hairpinning. Если два клиента за строгими симметричными NAT подключены к одному и тому же узлу
slyber_turn, сервер корректно распознает свой собственный внешний IP на релейных портах и замыкает маршрут между пирами внутри памяти без выхода в локальную подсеть.
Методика нагрузочного тестирования через встроенный slyber_turn bench
Обычные системные утилиты вроде iperf3 не подходят для проверки TURN инфраструктуры, так как они тестируют чистый транспортный канал, минуя фазу авторизации HMAC, выделение релейного порта, заголовки каналов TURN и логику пересылки между пирами.
Чтобы проверять сервер в боевых условиях без установки сторонних пакетов на клиенте, я встроил два инструмента прямо в основной бинарный файл: slyber_turn doctor для быстрой диагностики после старта и slyber_turn bench для нагрузочного тестирования.
При запуске sudo slyber_turn doctor утилита проверяет конфигурацию, валидирует защиту HTTP API, сверяет внешний IP через STUN и проводит живую аллокацию релейного порта:
Plaintext
[OK] Configuration valid (realm: turn.example.com, relay port pool: 49152-65535, capacity: 16384 sessions)
[OK] HTTP API protection active (origins: 1, require_secret: false)
[OK] STUN check passed in 12ms: PUBLIC_IP (203.0.113.50) matches external IP
[OK] HTTP API (:8080/healthz) responded normally (200 OK)
[OK] TURN Allocate succeeded in 390µs (allocated relay address: 203.0.113.50:50124)
Полный цикл выделения релейного адреса занимает 390µs. Когда нужно проверить поведение сервера под конкурентной нагрузкой, запускается встроенный генератор трафика:
Bash
sudo slyber_turn bench -clients 20 -duration 5s
Команда поднимает 20 одновременных клиентских сессий, проходит реальную процедуру получения токенов и аллокации портов, гонит поток медиапакетов через релей и на выходе считает круговую задержку RTT, процент потерь и сетевой джиттер строго по формуле стандарта RFC 3550. Это дает точный ответ, сколько реальных звонков выдержит конкретная виртуальная машина еще до пуска живого трафика.
Наблюдаемость в продакшене и переход к групповой маршрутизации
Для постоянного контроля узла в slyber_turn встроены три механизма телеметрии, которые не требуют подключения внешних баз данных:
- Эндпоинт
/metricsотдает метрики в формате Prometheus для построения графиков в Grafana. - Запрос к
/api/node-statsвозвращает агрегированную статистику за последние 24 часа, текущую утилизацию пула портов и списокTop Offendersс клиентами, которые создают аномальную нагрузку или чаще других получают отказы фильтра безопасности. - Все критические системные события и срабатывания защиты записываются на диск в структурированный лог
/opt/slyber_turn/events.jsonl, который сохраняется даже при переустановке бинарника.
Установить сервер одной командой и посмотреть полную документацию по API можно на странице проекта Slyber TURN Server.