server_maul_hard: TURN и WireGuard

Эта страница описывает фактически развёрнутые сетевые сервисы server_maul_hard: TURN для WebRTC и центральный Linux WireGuard-хаб для связи бортового и операторского MikroTik.

Текущее состояние

На 30 октября 2026 года TURN работает в Docker. Интерфейс WireGuard поднят и слушает публичный UDP-порт. Добавлен первый клиент с адресом 10.77.0.10/32; peers бортового и операторского MikroTik ещё не добавлены: для этого нужны их публичные ключи и точные маршрутизируемые подсети.

Роль сервера

server_maul_hard — публичный узел в дата-центре Hetzner. Он решает две разные задачи:

Сервис Назначение
WireGuard Соединяет сети НРК и оператора, даже если обе стороны находятся за Starlink/LTE CGNAT
TURN Ретранслирует WebRTC-медиатрафик, когда браузер и видеоисточник не могут установить прямое соединение

TURN и WireGuard независимы. Отказ или отсутствие TURN не разрывает VPN, а наличие TURN не создаёт маршрута к локальным сетям НРК.

Основные параметры сервера:

Параметр Значение
Публичный IPv4 136.243.89.142
ОС Ubuntu Server 26.04 LTS, x86-64
TURN DNS turn.136-243-89-142.sslip.io
WireGuard endpoint 136.243.89.142:13231/udp
WireGuard VPN-сеть 10.77.0.0/24

Карта файловой системы

Ниже перечислены основные каталоги server_maul_hard, относящиеся к Maul, документации, WireGuard, TURN и публикации сервисов. Это не полный список файлов Ubuntu, а эксплуатационная карта проекта.

server_maul_hard
├── /srv/maul/                         исходники Maul и документации
│   ├── docs/                          исходные Markdown-страницы MkDocs
│   ├── mkdocs.yml                     навигация и настройки MkDocs
│   ├── requirements-docs.txt          зафиксированные зависимости документации
│   ├── .mkdocs-venv/                  Python-окружение MkDocs
│   ├── site/                          сгенерированный публичный сайт
│   ├── cfmoto-pixhawk-dashboard/      исходники и venv панели управления
│   ├── ardupilot/                     отдельный репозиторий ArduPilot
│   ├── cfmoto-gearbox-telemetry/      отдельный репозиторий телеметрии КПП
│   ├── cfmoto-steering-controller/    отдельный репозиторий рулевого контроллера
│   └── backups/                       резервные параметры и результаты проверок
│
├── /opt/borsuk-turn/                  управляемый проект TURN
│   ├── compose.yaml                   описание контейнера Coturn
│   ├── runtime/turnserver.conf        активная конфигурация внутри контейнера
│   ├── configure.py                   подготовка runtime-конфигурации
│   ├── probe.py                       проверка TURN
│   └── renew-cert.sh                  обновление файлов сертификата
│
├── /etc/borsuk-turn/                  защищённые runtime-файлы TURN
│   ├── auth-secret                    общий секрет аутентификации
│   ├── fullchain.pem                  цепочка TLS-сертификата
│   └── privkey.pem                    закрытый TLS-ключ
│
├── /etc/systemd/network/              постоянная конфигурация WireGuard
│   ├── 70-wg-maul.netdev              интерфейс, порт, ключ и peers
│   └── 70-wg-maul.network             VPN-адрес и IP forwarding интерфейса
├── /etc/wireguard/                    ключи WireGuard-сервера
│   ├── maul-server.key                закрытый ключ
│   └── maul-server.pub                публичный ключ
├── /etc/sysctl.d/
│   └── 90-maul-wireguard.conf         постоянный IPv4 forwarding
│
├── /etc/nginx/                        публикация MkDocs, dashboard и TURN TLS
│   ├── sites-available/               конфигурации виртуальных хостов
│   ├── sites-enabled/                 ссылки на активные сайты
│   └── stream-borsuk.conf             SNI-маршрутизация TCP/443
├── /etc/letsencrypt/                  сертификаты и правила Certbot
│   ├── live/                          стабильные ссылки на актуальные сертификаты
│   ├── archive/                       версии сертификатов
│   ├── renewal/                       параметры продления
│   └── renewal-hooks/deploy/
│       └── borsuk-turn                обновление сертификата Coturn после renew
├── /etc/systemd/system/
│   └── maul-dashboard.service         запуск dashboard на 127.0.0.1:8766
├── /etc/ufw/                          постоянные правила firewall
├── /var/lib/docker/                   внутренние данные Docker — вручную не править
└── systemd journal                    рабочие журналы сервисов

/srv/maul: рабочее дерево проекта

/srv/maul — канонический каталог Maul. Основные правила работы:

  • страницы редактируются в /srv/maul/docs, а навигация — в /srv/maul/mkdocs.yml;
  • /srv/maul/site является результатом сборки, поэтому вручную его не редактируют;
  • строгая проверка выполняется из /srv/maul командой .mkdocs-venv/bin/mkdocs build --strict;
  • nginx публикует именно /srv/maul/site;
  • каталоги ardupilot, cfmoto-gearbox-telemetry и cfmoto-steering-controller — самостоятельные репозитории: изменения в одном из них не следует смешивать с документацией;
  • временные каталоги и файлы с backup в имени являются снимками перед изменениями, но не заменяют внешнюю резервную копию сервера.

Dashboard запускается unit-файлом /etc/systemd/system/maul-dashboard.service от непривилегированного пользователя maul-dashboard. Его рабочий каталог — /srv/maul/cfmoto-pixhawk-dashboard, а listener доступен только локально на 127.0.0.1:8766; наружу запросы пропускает nginx.

/opt/borsuk-turn: управляемый TURN-проект

Контейнер borsuk-turn-coturn-1 создаётся из /opt/borsuk-turn/compose.yaml. Каталог /opt/borsuk-turn/runtime монтируется в контейнер как /etc/coturn только для чтения. Защищённые файлы из /etc/borsuk-turn также передаются в контейнер read-only.

Файлы /etc/turnserver.conf и /etc/default/coturn относятся к старому host-варианту Coturn. Активный TURN работает в Docker, поэтому эти файлы не следует принимать за текущую конфигурацию или запускать параллельно с контейнером.

Секретные файлы

Нельзя выводить, копировать в документацию или коммитить содержимое /etc/borsuk-turn/auth-secret, /etc/borsuk-turn/privkey.pem, /etc/wireguard/maul-server.key, /opt/borsuk-turn/.env и закрытые ключи из других конфигураций. Для диагностики обычно достаточно имён, владельцев, прав доступа и публичных ключей.

/etc: конфигурация хоста

Конфигурация WireGuard разделена по назначению:

Путь Что хранится Чем применяется
/etc/systemd/network/70-wg-maul.netdev параметры wg-maul и peers systemd-networkd
/etc/systemd/network/70-wg-maul.network адрес 10.77.0.1/24 и forwarding systemd-networkd
/etc/wireguard/maul-server.key приватный ключ сервера WireGuard через .netdev
/etc/wireguard/maul-server.pub публичный ключ сервера выдаётся клиентам
/etc/sysctl.d/90-maul-wireguard.conf глобальный IPv4 forwarding systemd-sysctl
/etc/ufw/user.rules скомпилированные IPv4-правила UFW UFW

Nginx использует схему sites-available → sites-enabled. Включены сайты документации, dashboard и ACME challenge. Отдельный /etc/nginx/stream-borsuk.conf распределяет TLS-соединения на порту 443 по SNI между HTTPS и TURN.

Где смотреть журналы

Основные сервисы пишут в systemd journal или Docker logging driver:

# WireGuard и создание интерфейса
journalctl -u systemd-networkd --since today

# Dashboard
journalctl -u maul-dashboard --since today

# Nginx
journalctl -u nginx --since today

# TURN
cd /opt/borsuk-turn
docker compose logs --tail=100 coturn

Для текущего состояния журнала не требуется искать отдельный wireguard.log: handshake и счётчики проверяются командой wg show wg-maul. TURN-логи могут содержать клиентские IP и идентификаторы, поэтому перед передачей их нужно маскировать.

Что резервировать

Минимальный набор для восстановления инфраструктуры:

  1. /srv/maul/docs, /srv/maul/mkdocs.yml и /srv/maul/requirements-docs.txt;
  2. /opt/borsuk-turn, кроме временных runtime-артефактов, с отдельной защищённой копией секретов;
  3. /etc/borsuk-turn, /etc/systemd/network/70-wg-maul.*, /etc/wireguard и /etc/sysctl.d/90-maul-wireguard.conf;
  4. активные nginx-, UFW- и systemd-конфигурации;
  5. репозитории и необходимые параметры оборудования из /srv/maul/backups.

Секреты в резервной копии должны быть зашифрованы. Каталоги /srv/maul/site, .mkdocs-venv и /var/lib/docker можно восстановить из исходников и декларативных конфигураций, поэтому они не являются главным источником истины.

Общая схема

                    СЕТЕВОЙ КОНТУР

Бортовой RB5009UPr ── исходящий WireGuard ──┐
                                            │
                                      server_maul_hard
                                      10.77.0.1
                                            │
Операторский RB5009UG ─ исходящий WireGuard ┘


                    МЕДИАКОНТУР WEBRTC

видеоисточник / MediaMTX ───── прямое соединение ───── браузер
             │                                           │
             └──── TURN fallback через server_maul_hard ──────┘

WireGuard

Зачем нужен центральный хаб

Обычные тарифы Starlink и многие мобильные операторы используют CGNAT. Входящее соединение к операторскому или бортовому MikroTik в этом случае невозможно. Центральный сервер имеет публичный адрес, а оба MikroTik сами подключаются к нему исходящими UDP-соединениями.

борт → интернет → server_maul_hard ← интернет ← оператор

Развёрнутые параметры

Параметр Значение
Linux-интерфейс wg-maul
Endpoint 136.243.89.142:13231
Протокол WireGuard, UDP
Адрес сервера в VPN 10.77.0.1/24
Public key сервера 08WVMsOYsbIclgxlHcb71ZJV49+G8oLiz7MAbYG9kkQ=
Peers один клиент: 10.77.0.10/32; MikroTik ещё не добавлены

Планируемая адресация пилота:

Участник VPN-адрес
server_maul_hard 10.77.0.1
MikroTik оператора 10.77.0.2
MikroTik НРК Maul 94 10.77.0.94

Адреса бортовых VLAN, например 192.168.94.0/24, добавляются в AllowedIPs соответствующего peer только после подтверждения фактической адресации. Одинаковые подсети разных НРК использовать нельзя.

Как проходит пакет

Пример обращения оператора к бортовой камере:

ПК оператора
    │
RB5009UG
    │ WireGuard
    ▼
server_maul_hard
    │ маршрутизация по AllowedIPs
    ▼
RB5009UPr
    │ Camera VLAN
    ▼
камера

На server_maul_hard не выполняется NAT между WireGuard peers. Сервер маршрутизирует пакеты, а обратные маршруты должны быть известны обоим MikroTik.

Конфигурация на сервере

WireGuard управляется systemd-networkd:

Файл Назначение
/etc/systemd/network/70-wg-maul.netdev создание интерфейса и UDP listener
/etc/systemd/network/70-wg-maul.network адрес 10.77.0.1/24 и forwarding
/etc/sysctl.d/90-maul-wireguard.conf системное разрешение IPv4 forwarding
/etc/wireguard/maul-server.key закрытый ключ сервера, защищённый файл
/etc/wireguard/maul-server.pub публичный ключ сервера

Закрытые ключи

Приватный ключ сервера и приватные ключи MikroTik нельзя добавлять в MkDocs, Git, экспорт для пересылки или сообщения. Для регистрации peer используется только его публичный ключ.

Добавление peer

Для каждого MikroTik необходимо:

  1. создать новый WireGuard-интерфейс и уникальную пару ключей;
  2. получить только публичный ключ MikroTik;
  3. добавить публичный ключ и уникальные AllowedIPs на server_maul_hard;
  4. добавить public key сервера и endpoint 136.243.89.142:13231 на MikroTik;
  5. установить persistent-keepalive=25s для работы через CGNAT;
  6. проверить handshake, VPN-адреса и маршруты до нужных VLAN;
  7. только после этого разрешать прикладной трафик firewall-правилами.

Firewall

На публичном интерфейсе открыт только WireGuard-порт 13231/udp. UFW разрешает пересылку трафика wg-maul → wg-maul; доступ определяется криптографическими peer-ключами и AllowedIPs.

Нельзя добавлять общий peer с AllowedIPs=0.0.0.0/0 на сервере: он перехватит маршруты других НРК. Каждому peer назначаются только его VPN-адрес и принадлежащие ему LAN/VLAN-подсети.

TURN

Что делает TURN

TURN используется как fallback для WebRTC. Если прямой UDP-путь между видеоисточником и браузером не создаётся из-за NAT или firewall, оба клиента соединяются с TURN, а сервер ретранслирует медиапакеты.

видеоисточник → TURN → браузер оператора

TURN:

  • не преобразует RTSP в WebRTC;
  • не захватывает видео с камеры;
  • не заменяет MediaMTX/Janus или другой медиашлюз;
  • не заменяет WireGuard;
  • расходует серверный трафик на каждый ретранслируемый поток.

Реализация

Фактический TURN работает в Docker Compose:

Параметр Значение
Каталог /opt/borsuk-turn
Compose-файл /opt/borsuk-turn/compose.yaml
Контейнер borsuk-turn-coturn-1
Образ coturn/coturn:4.18.0-r0-debian
Сеть контейнера host network
Restart policy unless-stopped
TURN realm turn.136-243-89-142.sslip.io
Relay ports UDP 49160–49260

Контейнер работает с read-only filesystem, без дополнительных Linux capabilities кроме NET_BIND_SERVICE, с no-new-privileges. Конфигурация и TLS-файлы монтируются внутрь контейнера только для чтения.

Стандартный host-unit coturn.service выключен. Это ожидаемо: нельзя одновременно запускать host Coturn и Docker Coturn на одних портах.

Аутентификация

Coturn использует long-term credentials на основе общего auth secret. Клиент получает короткоживущие TURN credentials от доверенного аутентифицированного API. Общий secret хранится в защищённой runtime- конфигурации и не публикуется.

Нельзя прописывать общий TURN secret в браузерный JavaScript, мобильное приложение или публичный репозиторий.

Порты TURN

Порт Протокол Назначение
3478 UDP/TCP STUN/TURN listener
443 TCP/TLS рекомендуемый TURN TLS через nginx stream/SNI
5349 TCP/TLS прямой TURN TLS listener
49160–49260 UDP relay-порты медиатрафика
80 TCP только ACME HTTP challenge для сертификата

В конфигурации включён no-tcp-relay: клиент может подключиться к TURN по TCP/TLS, но медиатрафик от TURN к peer ретранслируется по UDP.

TLS на порту 443

Порт 443/tcp принимает nginx stream с ssl_preread. По SNI nginx разделяет соединения:

  • docs.136-243-89-142.sslip.io и dashboard.136-243-89-142.sslip.io направляются на внутренний HTTPS;
  • TURN TLS направляется на Coturn 127.0.0.1:5349;
  • TLS завершается в Coturn, nginx передаёт TCP без расшифровки.

Сертификат Let's Encrypt выпущен для turn.136-243-89-142.sslip.io. certbot.timer проверяет обновление, а deploy hook копирует обновлённые файлы в /etc/borsuk-turn и перезапускает только Coturn-контейнер.

TURN и WireGuard: когда что используется

Ситуация WireGuard TURN
Управление MikroTik да нет
Доступ к камере по её VPN/LAN-адресу да нет
Телеметрия и команды НРК да нет
Прямой WebRTC работает возможно нет
WebRTC не проходит NAT/firewall возможно да
RTSP необходимо преобразовать в WebRTC нет нет — нужен медиашлюз

Предпочтительный порядок для видео:

  1. прямой WebRTC-путь;
  2. прямой медиапуть через WireGuard;
  3. TURN только как fallback.

Это уменьшает задержку и не пропускает каждый видеопоток через публичный сервер без необходимости.

Диагностика

WireGuard

wg show wg-maul
networkctl status wg-maul
ss -lunp | grep 13231

Рабочий peer должен показывать свежий latest handshake и растущие счётчики transfer. Наличие listener само по себе не подтверждает связь с MikroTik.

TURN

cd /opt/borsuk-turn
docker compose ps
docker compose logs --tail=50 coturn

Проверка открытого порта или TLS-сертификата недостаточна. Полная проверка TURN должна создать аутентифицированную allocation и передать тестовые пакеты через relay-порт.

Журналы

TURN-логи могут содержать IP-адреса клиентов и идентификаторы временных credentials. Их нельзя публиковать целиком без маскировки.

Текущее ограничение и развитие

Сейчас server_maul_hard является единственной публичной точкой WireGuard и TURN. Для пилота это допустимо, но сервер остаётся единой точкой отказа.

Перед production необходимо:

  1. развернуть второй WireGuard-хаб в другом дата-центре;
  2. настроить два endpoint на каждом MikroTik;
  3. проверить переключение Starlink/LTE и основного/резервного VPN-хаба;
  4. добавить мониторинг handshake age, packet loss и трафика;
  5. контролировать суммарную полосу TURN при нескольких видеопотоках;
  6. выполнять тест восстановления из защищённой резервной копии.