Авторизация публикации
Как закрыть приём видео по RTMP(S) и WHIP: действие publish, учётные данные в строке запроса RTMP-URL и коды отказа, которые увидит кодировщик.
Завести видео в SirinVideo можно двумя принципиально разными способами, и авторизуются они по-разному.
| Способ | Кто инициирует соединение | Авторизация |
|---|---|---|
| Публикация: RTMP, RTMPS, WHIP | внешний кодировщик или браузер | да — действие publish через auth_backend |
Активный забор: input rtsp://…, input https://…m3u8, input udp://… |
сам сервер | неприменима — учётные данные источника едут в URL источника |
Эта страница — про первый случай.
Общее правило: действие publish#
Публикация авторизуется отдельным действием, а не «правом на поток». Токен на просмотр публиковать не даёт, и наоборот — проверено на стенде для обоих протоколов.
{"sub":"encoder-1","exp":1787200000, "grants":[{"action":"publish","path":"ingest/cam1","protocols":["rtmp"]}]}
| Поле | Значение для публикации |
|---|---|
action |
publish — не read и не playback |
path |
путь потока, для которого в конфигурации объявлен источник publish:// |
protocols |
rtmp для RTMP и RTMPS, whip для WHIP; пусто = любой |
Привилегии (live, dvr, export, preview) описывают просмотр и к публикации отношения не
имеют: маска привилегий никогда не может случайно стать правом публиковать. Поэтому компактный
грант по метке (<метка>.<маска>) публикацию не открывает — нужно обычное разрешение с
action: publish.
Решение по RTMP-публикатору принимается по реестру шлюзов приёма, в котором меток потоков нет:
в проверку приходит пустой список меток (это видно в теле запроса к внешней службе авторизации —
"labels":[]). Значит, разрешение вида {"action":"publish","labels":["site-north"]} RTMP-push
не авторизует — нужен path, точный или *-глоб. Для WHIP ограничение не действует: там метки
потока доступны, и правило по меткам работает как обычно.
auth_backend публикация открыта всем, кто знает путьКак и просмотр: если блока auth_backend нет, публиковать может любой, кто дотянулся до порта и
угадал путь потока. Порт приёма никогда не должен смотреть в интернет без настроенной авторизации.
RTMP и RTMPS#
Конфигурация#
rtmp 1935; rtmps 1936; auth_backend { type hs256; secret "секрет-подписи-токенов-публикаторов"; } stream ingest/cam1 { input publish://rtmp; }
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
rtmp |
номер порта, 0 = выключено |
0 |
рестарт |
rtmps |
номер порта, 0 = выключено |
0 |
рестарт |
Поток без объявленного publish://-источника публикацию не принимает вовсе. Если ни один порт
приёма не поднят, конфигурация не пройдёт проверку:
stream "ingest/cam1": a publish:// source needs an `rtmp`/`rtmps` listener (RTMP publish) or a `webrtc_udp_port` (WHIP)
Как адресуется публикатор#
Кодировщик идёт на rtmp://media.example.com:1935/<приложение>/<поток>. Сервер разрешает ключ так:
- сначала ищет поток с путём
<приложение>/<поток>— напримерingest/cam1; - если не нашёл — ищет поток с голым именем
<поток>.
Совпавший ключ и становится путём потока. Благодаря второму шагу поток с плоским именем
(stream cam1) принимает публикатора, пушащего в любое приложение (rtmp://host/live/cam1).
Учётные данные едут только в строке запроса URL#
У RTMP нет заголовков — предъявить Authorization: Bearer физически негде. Единственный
носитель — строка запроса:
rtmp://media.example.com:1935/ingest/cam1?token=$TOKEN
Хвост ?… срезается с обеих половин адреса — и с приложения (оно приезжает в составе tcUrl),
и с ключа потока — до любого поиска потока. Это важно практически: без такой обрезки имя с
хвостом просто не находилось бы, и включение авторизации выглядело бы как поломка совместимости.
Обе распространённые формы настройки кодировщика работают, обе проверены на стенде:
| Как настроен кодировщик | Что получается | Результат |
|---|---|---|
«Сервер» rtmp://media.example.com:1935/ingest, «Ключ потока» cam1?token=$TOKEN |
хвост на ключе | принято |
«Сервер» rtmp://media.example.com:1935/ingest?token=$TOKEN, «Ключ потока» cam1 |
хвост на приложении | принято |
Если хвост стоит на обеих половинах, побеждает тот, что на ключе потока.
Поле «Ключ потока» есть у любого кодировщика, и именно оно обычно хранится в его настройках как
секрет. Выдавайте кодировщику строку вида cam1?token=… целиком — менять «адрес сервера» при
ротации токена не придётся.
Проверка идёт до захвата шлюза потока#
У потока ровно один публикатор одновременно, и слот занимается после успешной авторизации. Порядок здесь — не деталь реализации, а свойство безопасности: если бы слот занимался первым, любой желающий без единого удостоверения занимал бы его и удерживал до истечения дедлайна чтения, запирая законного публикатора. Это отказ в обслуживании, не требующий вообще ничего.
Что увидит кодировщик при отказе#
| Ситуация | Статус RTMP | Текст причины |
|---|---|---|
| учётных данных нет | NetStream.Publish.Denied |
unauthorized |
| токен есть, но прав на публикацию в этот поток нет | NetStream.Publish.Denied |
forbidden |
для этого пути нет потока с publish:// |
NetStream.Publish.BadName |
no publish gate on this node — a publisher must target the stream's host node |
| слот уже занят другим публикатором | NetStream.Publish.BadName |
already publishing |
Проверенная матрица (сервер с auth_backend { type hs256; }, поток ingest/cam1 с источником
publish://rtmp):
| Что предъявлено | Результат |
|---|---|
| ничего | Server error: unauthorized |
токен {"action":"read","path":"ingest/cam1"} |
Server error: forbidden |
токен {"action":"publish",…,"protocols":["whip"]} |
Server error: forbidden |
токен {"action":"publish",…,"protocols":["rtmp"]} |
принято |
В журнале сервера отказ виден одной строкой; успешный приём — тоже:
[ingest/cam1] rtmp publish denied — unauthorized [ingest/cam1] rtmp publish denied — forbidden [ingest/cam1] rtmp publisher connected
rtmps — это тот же RTMP поверх TLS. Он защищает токен в пути (в открытом RTMP строка запроса
идёт по сети как есть), но сам по себе не является учётными данными. Для публикации из недоверенной
сети используйте rtmps и токен.
Выдача по RTMP авторизуется тоже#
Забрать поток по RTMP (rtmp://media.example.com:1935/ingest/cam1) — это отдельный гейт с
действием read и привилегией live, полный аналог DESCRIBE в RTSP. Учётные данные передаются
так же, строкой запроса; отказ приходит как NetStream.Play.Failed с той же парой причин
(unauthorized / forbidden). Проверено:
| Что предъявлено | Результат |
|---|---|
| ничего | Server error: unauthorized |
токен {"action":"read",…,"protocols":["rtmp"]} |
видео идёт |
токен {"action":"read",…,"protocols":["hls"]} |
Server error: forbidden |
токен {"action":"publish",…} |
Server error: forbidden |
Подробнее о просмотре — Авторизация просмотра.
Раньше RTMP обходил авторизацию: при полностью настроенном auth_backend HTTP отвечал 401, а
RTMP в тот же момент отдавал видео и принимал публикацию. Сейчас оба направления закрыты.
Практически это значит, что после обновления кодировщик или потребитель без токена перестанет
работать. Заранее раздайте токены публикаторам и всем, кто забирает поток по RTMP, — иначе приём
встанет молча, с одной строкой в журнале. Сервер без настроенного auth_backend не изменился:
там RTMP по-прежнему открыт, как и всё остальное.
WHIP (публикация WebRTC из браузера)#
Конфигурация#
webrtc 40000; auth_backend { type hs256; secret "секрет-подписи-токенов-публикаторов"; } stream ingest/studio { input publish://whip; }
| Параметр | Тип и допустимые значения | По умолчанию | Применение |
|---|---|---|---|
webrtc |
номер UDP-порта; необязательный суффикс public_ip=<адрес> |
0 = выключено |
рестарт |
Точка входа — POST /s/{поток}/whip. В отличие от RTMP, здесь есть заголовки, поэтому токен можно
передать и заголовком Authorization: Bearer, и как ?token=.
{"sub":"studio-1","exp":1787200000, "grants":[{"action":"publish","path":"ingest/studio","protocols":["whip"]}]}
Решение принимается до разбора SDP и до занятия слота публикатора — по той же причине, что и в RTMP.
| Что предъявлено | Ответ |
|---|---|
| ничего | 401 |
| токен на просмотр того же потока | 403 |
токен publish (заголовком или ?token=) |
авторизация пройдена, дальше — обычный обмен SDP |
токен publish, отправленный на …/whep |
403 |
Адрес сессии, который сервер возвращает в заголовке Location, — одноразовый идентификатор,
привязанный к пути и роли. DELETE по чужому пути, неизвестный и уже удалённый идентификатор
неотличимы: все три отвечают 404.
Активный забор источника#
Для input rtsp://…, input https://…m3u8, input udp://… соединение инициирует сам сервер —
авторизовывать входящего некого. Учётные данные источника передаются в самом URL и являются
исходящими:
stream cam1 { input rtsp://admin:пароль@10.0.0.10:554/Streaming/Channels/101; }
Подробнее — Приём видео: обзор.
Сводная таблица#
| Способ | Аутентификация | Носитель учётных данных | Если auth_backend не настроен |
|---|---|---|---|
| RTMP / RTMPS публикация | да, action: publish, protocols: ["rtmp"] |
только строка запроса URL | открыто всем, кто знает путь |
| RTMP выдача | да, action: read, protocols: ["rtmp"] |
только строка запроса URL | открыто |
| WHIP | да, action: publish, protocols: ["whip"] |
заголовок либо строка запроса | открыто всем, кто знает путь |
| Активный забор | неприменима | учётные данные источника в его URL | неприменима |
Проверка#
Токен выпускается тем же генератором, что и для просмотра, — достаточно поменять действие на
publish (см. Генераторы токенов).
# 1. без токена — должно быть отказано ffmpeg -re -f lavfi -i testsrc -c:v libx264 -t 3 -f flv \ rtmp://media.example.com:1935/ingest/cam1 # 2. с токеном публикации — должно быть принято ffmpeg -re -f lavfi -i testsrc -c:v libx264 -t 3 -f flv \ "rtmp://media.example.com:1935/ingest/cam1?token=$TOKEN"
[rtmp @ 0x…] Server error: unauthorized Error opening output rtmp://media.example.com:1935/ingest/cam1: Operation not permitted
Второй запуск завершается без сообщений об ошибке, а в журнале сервера появляется
[ingest/cam1] rtmp publisher connected.
Между разрывом соединения и готовностью принять следующего публикатора проходит несколько секунд:
поток фиксирует уход источника и переподключается. Повторная публикация в этом окне получает
already publishing либо no publish gate on this node — это не отказ авторизации. Подождите и
повторите.