Когда запускаешь WebRTC проект в локальной сети или тестируешь звонки между двумя домашними роутерами, прямое соединение через STUN работает идеально. Проблемы начинаются в продакшене. Около 15 или 20 процентов пользователей находятся за NAT, операторским CGNAT у мобильных провайдеров или жесткими корпоративными файрволами. Без надежного сервера посредника (TURN) такие клиенты видят бесконечный экран подключения и уходят.
Годами стандартом по умолчанию оставался Coturn. Это комбайн, который поддерживает десятки старых спецификаций и баз данных. Но когда тебе нужно быстро поднимать и масштабировать узлы ретрансляции медиатрафика под реальной нагрузкой, классический подход с Coturn превращается в постоянную борьбу с конфигурационными файлами и настройками операционной системы.
С чем приходится сталкиваться при поддержке Coturn в продакшене
При развертывании стандартного TURN сервера из пакетного менеджера Linux разработчик сразу упирается в пять инфраструктурных задач, которые приходится решать вручную на каждом новом сервере.
1. Раздутый конфиг и ручной проброс внешнего IP
Файл turnserver.conf насчитывает сотни строк с флагами, половина из которых устарела. На облачных виртуальных машинах сервер часто не знает свой реальный публичный IPv4 адрес. Приходится писать обертки в bash скриптах, чтобы при старте контейнера или виртуалки узнавать внешний адрес, прописывать его в external-ip и отдельно открывать в ufw или firewalld порт 3478 вместе с диапазоном портов ретрансляции от 49152 до 65535.
2. Необходимость писать отдельный бэкенд для выдачи временных ключей
Передавать статический логин и пароль от TURN сервера в браузер нельзя любой пользователь откроет вкладку сети в инструментах разработчика, заберет доступы и начнет гонять чужой трафик за ваш счет. Механизм временных токенов в Coturn требует, чтобы вы сами подняли отдельный HTTP сервис на своем бэкенде, синхронизировали общий секретный ключ, генерировали HMAC подпись с временем жизни и отдавали JSON клиенту перед созданием RTCPeerConnection.
3. Потеря UDP пакетов из за дефолтных буферов ядра Linux
По умолчанию в большинстве дистрибутивов Linux размер буферов сокетов net.core.rmem_max и net.core.wmem_max составляет около 208 КБ. Когда через TURN сервер одновременно идет несколько десятков видеопотоков или случается резкий всплеск голосового трафика, ядро начинает молча отбрасывать UDP пакеты еще до того, как приложение успевает их прочитать из сокета. Без принудительного увеличения буферов через sysctl до 8 МБ и распределения нагрузки по ядрам процессора через SO_REUSEPORT голос начинает рассыпаться.
4. Уязвимость к SSRF атакам через ретрансляцию
TURN сервер по своей природе принимает пакеты от внешнего клиента и пересылает их от своего имени. Если при ручной настройке забыть жестко заблокировать локальные подсети через списки контроля доступа, злоумышленник может запросить пересылку пакетов на внутренний адрес облачного провайдера 169.254.169.254, на localhost или во внутреннюю приватную сеть вашей инфраструктуры.
5. Отсутствие встроенной проверки и нагрузочного тестирования
После запуска службы systemd вы видите статус active, но это не гарантирует, что ретрансляция реально работает снаружи. Приходится открывать сторонние веб утилиты вроде Trickle ICE, вручную вбивать туда адреса и гадать, почему не проходит Allocate запрос или какой реальный джиттер выдает сервер под нагрузкой.
Как я решил эти задачи в Slyber TURN Server
Чтобы убрать ручную рутину на своих WebRTC проектах, я написал Slyber TURN Server на Go поверх библиотеки pion/turn/v4. Главная архитектурная идея простая сервер поставляется как один статически скомпилированный бинарный файл для linux/amd64 и linux/arm64 без единой внешней системной зависимости.
Вместо редактирования длинных конфигураций вся сетевая подготовка вынесена внутрь самого бинарника и установочного скрипта:
- Автоматическое определение сети и тюнинг ядра. При старте сервер сам определяет свой внешний IPv4 через быстрый STUN запрос, поднимает несколько UDP листенеров по числу ядер процессора с флагом
SO_REUSEPORTи расширяет системные буферыrmem_maxиwmem_maxдо 8 МБ. - Встроенный HTTP API для выдачи доступов. Внутри сервера уже работает защищенный эндпоинт
/api/turn-credentialsс проверкойOrigin, ограничением частоты запросов и белыми списками IP. Генерация локальногоHMACтокена занимает 0 мс, поэтому отдельный микросервис для авторизации больше не нужен. - Защита от SSRF и работа с мобильными сетями из коробки. Фильтр по умолчанию блокирует обращения к метаданным облаков
169.254.0.0/16иfd00:ec2::254/128, петлевым интерфейсам и локальным подсетям, сохраняя при этом поддержку TURN Hairpinning. А когда мобильный клиент переключается между WiFi и LTE, сервер группирует сессии поuser_idи автоматически очищает зависшие сокеты через 5 секунд.
| Параметр | Slyber TURN Server | Классический Coturn |
| Установка и зависимости | Один статический бинарник | Пакеты ОС и внешние библиотеки |
| Выдача токенов для браузера | Встроенный HTTP эндпоинт | Требуется свой скрипт на бэкенде |
| Настройка буферов Linux | Автоматически до 8 МБ | Ручная правка sysctl.conf |
| Защита облачных метаданных | Включена по умолчанию | Ручное добавление правил ACL |
| Сброс мертвых сокетов LTE | 5 секунд по user_id | Ожидание общего таймаута |
| Диагностика и бенчмарк | Встроены в CLI бинарника | Отсутствуют |
Развертывание на Linux в одну команду
Весь процесс установки сводится к запуску одного скрипта. Он проверяет архитектуру процессора, сверяет контрольную сумму SHA256SUMS, настраивает сетевые параметры ядра, открывает нужные порты в ufw или firewalld, создает файл окружения /opt/slyber_turn/.env, регистрирует службу systemd и сразу прогоняет самодиагностику:
Bash
curl -fsSL https://raw.githubusercontent.com/slyber-app/slyber-turn-server/main/install.sh | sudo bash
Если нужно сразу задать свой домен, порт для HTTP API или жестко ограничить пропускную способность на одну сессию через встроенный атомарный лимитер, параметры передаются прямо в команду установки:
Bash
curl -fsSL https://raw.githubusercontent.com/slyber-app/slyber-turn-server/main/install.sh | \
sudo REALM="turn.example.com" \
PUBLIC_IP="auto" \
HTTP_PORT=":8080" \
MAX_KBPS_PER_SESSION="2000" \
bash
Сразу после установки встроенная утилита slyber_turn doctor проверяет реальное прохождение пакетов и делает живую тестовую аллокацию релея:
[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)
В любой момент можно нагрузить сервер синтетическим трафиком прямо из консоли командой sudo slyber_turn bench -clients 20 -duration 5s, чтобы увидеть реальную задержку RTT, потери пакетов и джиттер по стандарту RFC 3550 до запуска живых пользователей.
Подключение к браузерному WebRTC клиенту
Поскольку генерация временных учетных данных уже встроена в сервер, код на стороне клиента сокращается до одного запроса перед инициализацией пира:
JavaScript
const res = await fetch("https://turn.example.com/api/turn-credentials?user_id=user_42");
const turn = await res.json();
const pc = new RTCPeerConnection({
iceServers: [{
urls: turn.uris,
username: turn.username,
credential: turn.password
}]
});
Для мониторинга под капотом доступны эндпоинт /metrics в формате Prometheus, суточная статистика /api/node-stats с отслеживанием пула портов и журнал событий events.jsonl.
Посмотреть полную техническую спецификацию и забрать команды управления можно на странице проекта Slyber TURN Server.