Порты и сеть
Какой слушатель за что отвечает, что открывать в межсетевом экране, как работать за обратным прокси и какие UDP-порты нужны WebRTC и RTSP.
Сервер открывает ровно те порты, которые перечислены в конфигурации. Отдельного порта у API, панели управления, метрик и плеера нет — всё это едет по HTTP-слушателю вместе с медиа.
Слушатели#
| Директива | Протокол | По умолчанию | Что обслуживает |
|---|---|---|---|
http |
TCP | 8080 | HLS, LL-HLS, DASH, MSE-WS, HTTP-TS, превью, плеер, REST API, панель, /metrics, ответчик ACME |
https |
TCP | 0 (выключен) | то же самое поверх TLS |
rtsp |
TCP | 8554 | раздача по RTSP |
rtsps |
TCP | 0 (выключен) | раздача по RTSP поверх TLS |
rtmp |
TCP | 0 (выключен) | приём публикации и раздача по RTMP |
rtmps |
TCP | 0 (выключен) | то же поверх TLS |
webrtc |
UDP | 0 (выключен) | медиа WebRTC: просмотр (WHEP) и публикация (WHIP) |
http 8080; https 8443; rtsp 8554; rtsps 8555; rtmp 1935; rtmps 1936; webrtc 40000 public_ip=203.0.113.10;
Правила проверки, все они срабатывают при разборе файла:
httpиrtspобязательны и не могут быть нулевыми —http_port and rtsp_port must be non-zero;- они должны различаться —
http_port and rtsp_port must differ (both 8080); - все ненулевые порты попарно различны —
https and rtsps both use port 8443 — listener ports must be distinct; - значение больше 65535 не проходит разбор.
0 означает «слушатель выключен» для всех портов, кроме http и rtsp.
Директивы api, admin или metrics_port не существует. /api/v1, /admin/, /metrics и
<поток>/embed.html обслуживаются тем же слушателем http (и https), что и видео. Если вы
хотите закрыть управление от публики — разграничивайте это на межсетевом экране или обратном
прокси по пути URL, а не портом.
Все TCP-слушатели открываются на 0.0.0.0. Директивы для выбора интерфейса (bind, listen,
address) не существует. Ограничить доступ конкретным интерфейсом можно только средствами
операционной системы: правилами межсетевого экрана либо IPAddressAllow в юните systemd.
Что открывать в межсетевом экране#
Минимум — просмотр в браузере#
| Направление | Порт | Зачем |
|---|---|---|
| Входящий | TCP 8080 (или 443 через прокси) |
HLS, MSE-WS, DASH, плеер, API |
| Исходящий | TCP 554 до камер |
приём RTSP |
Этого достаточно: HLS, LL-HLS, DASH, MSE-WS, превью и панель живут на одном TCP-порту.
Добавления по мере включения возможностей#
| Возможность | Что открыть |
|---|---|
| TLS на самом сервере | входящий TCP 443 (или ваш https) |
| Выпуск сертификата по ACME | входящий TCP 80 — обязательно, см. ниже |
| Раздача по RTSP | входящий TCP 8554 |
| Раздача по RTSP через UDP | входящий TCP 8554 + эфемерный диапазон UDP, см. ниже |
| Приём публикации по RTMP | входящий TCP 1935 |
| WebRTC | входящий и исходящий UDP на одном порту webrtc |
| Приём по HLS, DASH, HTTP-TS | исходящий TCP 80/443 до источника |
| Приём по UDP или multicast | входящий UDP на порту из URL источника, плюс IGMP до маршрутизатора |
| Копирование архива в S3 | исходящий TCP 443 до адреса хранилища |
| Кластер | входящий и исходящий TCP и UDP на порту из peer <имя> <адрес> |
Приём публикации авторизуется, но только когда настроен auth_backend. Пока он не настроен, поток
пишет любой, кто дотянулся до порта, — как и всё остальное на таком узле.
У RTMP нет заголовков, поэтому удостоверение едет в строке запроса:
rtmp://media.example.com/live/поток?token=…. Отказ приходит публикатору как
NetStream.Publish.Denied. Проверка выполняется ДО захвата потока, поэтому неавторизованный
publisher не может занять его и запереть законного.
Порт rtmp всё равно разумно держать во внутреннем сегменте: сетевая граница дешевле разбора
неудачных попыток. См. Авторизация публикации и
Приём публикации.
Порт 80 и выпуск сертификатов#
Ответчик ACME HTTP-01 обслуживается слушателем http, но удостоверяющий центр всегда
обращается на порт 80. Это несовпадение — самая частая причина, по которой сертификат не
выпускается.
Работают оба варианта:
# Вариант А: сервер сам слушает 80. http 80; https 443;
# Вариант Б: сервер на 8080, а внешний 80 пробрасывается на него # правилом DNAT межсетевого экрана. http 8080; https 443;
# Проброс внешнего 80 на 8080. Первые две строки создают таблицу и цепочку, # поэтому набор выполняется на чистой системе целиком. sudo nft add table ip hastreamer sudo nft add chain ip hastreamer prerouting '{ type nat hook prerouting priority dstnat; }' sudo nft add rule ip hastreamer prerouting tcp dport 80 redirect to :8080 # Проверка: правило появилось в списке. sudo nft list chain ip hastreamer prerouting
table ip hastreamer {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
tcp dport 80 redirect to :8080
}
}
Команды nft add меняют только работающий набор правил. Чтобы правило вернулось после
перезагрузки, внесите его в постоянный набор правил вашей системы.
Предпосылки выпуска, все обязательны:
- настроен хотя бы один TLS-слушатель (
https,rtspsилиrtmpsненулевой) — иначе задача выпуска не запускается вовсе; - есть блок
certificate <имя> { hosts …; acme; }с конкретным именем узла — подстановочные имена по HTTP-01 невозможны; - удостоверяющий центр достаёт
http://<имя>/.well-known/acme-challenge/<токен>на порту 80; - разрешён исходящий HTTPS до адреса из
acme_directory.
Когда https настроен, сервер отвечает 307 на любой открытый HTTP-запрос, перенаправляя его на
HTTPS. Запросы ACME-испытания из этого правила исключены и всегда отдаются в открытом виде.
Если же перенаправление делает ваш обратный прокси — исключите путь
/.well-known/acme-challenge/ из его правила, иначе выпуск сертификата не состоится.
Подробности выпуска и обновления — TLS и сертификаты.
Работа за обратным прокси#
Типовая схема: прокси терминирует TLS на 443 и проксирует на http сервера. Тогда https в
конфигурации сервера остаётся нулевым, и перенаправление на HTTPS сервер не делает — этим
занимается прокси.
Проброс адреса клиента — обязателен#
Сервер верит заголовкам Forwarded, X-Forwarded-For и X-Real-IP только если непосредственный
TCP-собеседник перечислен в trusted_proxies. По умолчанию список пуст, то есть заголовки
пересылки не принимаются никогда — прямой клиент не может подделать свой адрес.
trusted_proxies 10.0.0.0/8 192.168.0.0/16 ::1;
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
trusted_proxies |
список IP-адресов и подсетей CIDR; голый адрес считается /32 или /128. Директиву можно повторять — значения накапливаются |
[] (пусто) |
горячее |
Порядок разбора при доверенном собеседнике: Forwarded: for= → X-Forwarded-For → X-Real-IP;
доверенные узлы срезаются справа по цепочке.
Если вы поставили обратный прокси и не заполнили trusted_proxies, сервер видит адрес прокси для
каждого зрителя. Ломается сразу восемь вещей:
- привязка токена администратора к IP (
admin_ip_bind); - задержка при переборе пароля — счётчик становится общим на всех, один злоумышленник блокирует вход всем;
- привязка билета сессии к IP (
session_keys { bind_ip }); - проверка полей
ipиnetв токенах просмотра; - ключ ограничения частоты отказов авторизации медиа;
- поле
client_ipв списке сессий; - поле адреса в журнале аудита;
- географическая и сетевая диагностика по журналам.
Значения при этом остаются формально валидными — ошибок в журнале не будет. Проверяйте не конфигурацию, а результат: см. блок проверки ниже.
Откройте плеер потока с рабочего места и посмотрите список сессий:
curl -sS "https://media.example.com/api/v1/sessions" \ -H "Authorization: Bearer $TOKEN"
В поле client_ip должен быть адрес вашего рабочего места, а не адрес прокси. Если там адрес
прокси — либо прокси не ставит заголовок, либо его адрес не попал в trusted_proxies.
Что должен уметь прокси#
| Требование | Почему |
|---|---|
Передавать X-Forwarded-For или Forwarded |
иначе см. предыдущий блок |
Пропускать апгрейд до WebSocket на /<поток>/mse.ws |
иначе не работает раздача MSE-WS |
| Не буферизовать ответы | HTTP-TS и поток событий SSE (/api/v1/events) отдаются без указания длины и закрываются по разрыву; буферизация превращает их в зависший запрос |
Не менять и не отбрасывать заголовок Range |
перемотка архива и прямая загрузка файлов VoD работают на диапазонах и отвечают 206 |
Пропускать Authorization |
это единственный способ передать учётные данные для /metrics |
Не кэшировать ответы с Cache-Control: no-store |
так помечены превью, плейлисты архива и потоки с включённой защитой от встраивания |
Не перенаправлять /.well-known/acme-challenge/ |
иначе не выпускается сертификат |
Инициализационные сегменты и медиасегменты живого потока помечены как неизменяемые и пригодны для
кэширования на прокси и в сети доставки. Плейлисты — нет. Исключение: если у потока задан
allowed_referers, его сегменты понижаются до no-store и кэширование для этого потока
отключается полностью. Защита от встраивания и кэширование в сети доставки несовместимы.
CORS#
Если панель или плеер открываются с другого домена, ограничьте список источников:
allow_origins https://console.example.com https://ops.example.com;
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
allow_origins |
список источников; директиву можно повторять — значения накапливаются | ["*"] — любой источник |
горячее |
Совпадение точное, без подстановок и суффиксов. Источник не из списка не получает заголовка
CORS вообще. Предварительный запрос OPTIONS сервер отвечает сам: 204,
Allow-Methods: GET, HEAD, OPTIONS, POST, DELETE,
Allow-Headers: Range, Content-Type, Authorization, Max-Age: 86400.
Ответ /metrics и ответчик ACME заголовков CORS не отдают намеренно.
UDP для WebRTC#
webrtc 40000 public_ip=203.0.113.10;
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
webrtc <порт> |
u16; 0 = выключено |
0 |
рестарт |
public_ip=<адрес> |
литерал IPv4; пусто = определить по маршруту по умолчанию | пусто | рестарт |
Нужен ровно один UDP-порт. Сервер работает в режиме ICE-lite и объявляет один-единственный
кандидат — свой адрес и этот порт. Диапазона портов нет, каждое рабочее ядро открывает тот же
самый порт через SO_REUSEPORT. Медиа идёт по нему в обе стороны.
Правило межсетевого экрана — одно:
# Разрешить входящий UDP на порт WebRTC. Цепочка `inet filter input` есть в # стандартном наборе правил; если её нет — создайте её так же, как выше. sudo nft add rule inet filter input udp dport 40000 accept
Без public_ip= сервер объявляет адрес, определённый по маршруту по умолчанию. На машине за NAT
или в облаке это внутренний адрес — браузер получит кандидата, до которого не дотянется, и
соединение не установится. Диагностический признак: страница плеера открывается, а видео не
появляется; в отладочной панели плеера видно, что проверки ICE не проходят. Укажите внешний адрес
явно и перезапустите службу.
Если камера отдаёт G.711, ни MSE-WS, ни HLS не воспроизведут её звук — это ограничение браузера. WebRTC его играет. Обратное тоже верно: WebRTC не передаёт AAC. См. Протоколы раздачи.
UDP для RTSP#
RTSP умеет два транспорта, и настраиваются они разными директивами — их часто путают.
| Директива | Область | Что задаёт |
|---|---|---|
source_transport |
глобальная | как сервер забирает видео с камеры |
rtsp_transports |
у потока | что разрешено запросить клиенту, который смотрит по RTSP |
Раздача по RTSP через UDP#
Когда клиент запрашивает RTP/AVP;unicast;client_port=…, сервер занимает пару соседних
UDP-портов из эфемерного диапазона ядра: RTP на P, RTCP на P+1. Настраиваемого диапазона
нет — директивы для него не существует.
Практические следствия:
- при строгих правилах для входящего UDP откройте эфемерный диапазон
(
sysctl net.ipv4.ip_local_port_range), либо запретите UDP-раздачу; - если свободной пары не нашлось (сервер делает 8 попыток), клиент получает честный ответ
461 Unsupported Transport, а счётчик отказов виден в/api/v1/system; - межсетевые экраны с отслеживанием состояния обычно пропускают эти пакеты сами, так как сессия открывается по управляющему TCP-соединению.
Самое простое решение для закрытых сетей — оставить только чередующийся TCP:
stream cam1 { input rtsp://admin:пароль@10.0.0.10:554/Streaming/Channels/101; rtsp_transports tcp; # клиенту доступен только TCP, UDP отклоняется }
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
rtsp_transports |
both | tcp | udp |
both |
горячее |
rtp_max_payload |
u16, полезная нагрузка RTP в байтах | 1200 |
рестарт |
1200 подобрано так, чтобы пакет проходил через VPN без фрагментации. Внутри одной сети с MTU 1500
значение можно поднять примерно до 1460.
rtsp_transports — параметр потока: его изменение пересоздаёт этот один поток, зрители
остальных не затрагиваются. rtp_max_payload — глобальный и требует перезапуска службы. Разбор
классов изменений — Применение изменений.
Приём с камеры через UDP#
source_transport udp-then-tcp;
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
source_transport |
tcp | udp | udp-then-tcp |
tcp |
рестарт |
При udp и udp-then-tcp сервер тоже занимает соседние пары портов из эфемерного диапазона и
сообщает их камере в client_port=. Соединение инициирует сервер, поэтому межсетевой экран с
отслеживанием состояния пропускает ответный поток без отдельного правила.
Чередующийся TCP использует одно соединение, проходит через NAT и межсетевые экраны без настройки
и не теряет пакеты. UDP имеет смысл только там, где камера или сеть заметно лучше себя ведут на
нём. Значение udp-then-tcp пробует UDP и при отказе 461 переходит на TCP для всей сессии,
не смешивая транспорты.
udp_ingest_rcvbuf, udp_ingest_shards, udp_ingest_pool_slots и udp_pull_rate настраивают
маршрутизатор RTSP-over-UDP. На приём udp:// и multicast:// они не влияют вообще — там
размер буфера и очередь заданы в коде. При source_transport tcp эти четыре параметра не делают
ничего.
UDP для приёма MPEG-TS и multicast#
Порт задаётся не директивой, а самим URL источника:
stream headend { input udp://@239.1.1.1:1234?iface=10.0.0.5; }
- адрес должен быть литералом IPv4 — ни DNS-имени, ни IPv6;
?iface=<IPv4>закрепляет интерфейс присоединения к группе;?source=<IPv4>выполняет присоединение к конкретному источнику (IGMPv3, только Linux); значение обязано быть одноадресным, иначе конфигурация отвергается;- на межсетевом экране нужен входящий UDP на этом порту, а на маршрутизаторе — разрешённый IGMP.
Подробности — RTSP, HLS, DASH, MPEG-TS.
Порты кластера#
cluster { key "общий-секрет-кластера"; peer alpha 10.0.0.10:9000 { public https://alpha.example.com; zone dc1; } peer beta 10.0.0.11:9000 { public https://beta.example.com; zone dc2; } }
Один порт на узел (9000 в примере) обслуживает и UDP — обмен сведениями о живости, и TCP —
канал между узлами. Открывайте оба протокола между узлами кластера.
Зрителей между узлами сервер перенаправляет, а не проксирует: клиент получает 302 (для WHIP
и WHEP — 307) на адрес из public. Поэтому адреса из public должны быть доступны зрителям, а
не только узлам кластера.
Итоговая карта портов типовой установки#
Зрители ── TCP 443 ─────────► обратный прокси ── TCP 8080 ──► сервер Зрители ── UDP 40000 ────────────────────────────────────────► сервер (WebRTC) Зрители ── TCP 8554 ─────────────────────────────────────────► сервер (RTSP) Сервер ── TCP 554 ──────────────────────────────────────────► камеры Сервер ── TCP 443 ──────────────────────────────────────────► S3, ACME Наблюдение ── TCP 8080 /metrics ──────────────────────────────► сервер
sudo ss -tulnp | grep hastreamer
tcp LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("hastreamer",pid=1234,fd=12))
tcp LISTEN 0 4096 0.0.0.0:8554 0.0.0.0:* users:(("hastreamer",pid=1234,fd=13))
udp UNCONN 0 0 0.0.0.0:40000 0.0.0.0:* users:(("hastreamer",pid=1234,fd=14))
Каждый настроенный порт должен присутствовать. Строк на один порт может быть несколько — по числу
рабочих ядер: слушатели открываются с SO_REUSEPORT.
Что дальше#
- hastreamer.conf — все директивы — полное описание каждой директивы.
- Применение изменений — смена порта требует
restart, а неreload. - TLS и сертификаты — выпуск и обновление сертификатов.
- Модель доступа — чем ограничение по сети отличается от авторизации.