SIRINVIDEO Документация администратора и интегратора На сайт 2026.08

Доступ к админке и API

Пароль администратора и сессия, два статических токена API с разными правами, закрытый /metrics — и почему ни один из них не является доступом к видео.

Гейт управления закрывает панель управления, весь /api/v1/* и /metrics. Это совершенно отдельная подсистема от авторизации просмотра: у неё свой блок конфигурации, свои учётные данные и свои коды ответов.

Токен API — не доступ к видео

Ни пароль администратора, ни статические токены API не открывают ни одного медиа-пути. Обратное тоже верно: токеном зрителя нельзя обратиться к /api/v1. Это разные уровни доступа, и они не конвертируются друг в друга.

Блок auth { }#

auth {
    admin_password  "длинный-пароль-администратора";
    session_secret  "общий-секрет-32-и-более-символов";
    admin_ip_bind   off;
    api_read_token  "случайный-токен-только-на-чтение";
    api_write_token "случайный-токен-на-запись";
}
Параметр Тип и допустимые значения По умолчанию Применение
admin_password строка, непустая не задан горячее
session_secret строка, непустая случайный на загрузку горячее
admin_ip_bind флаг on / off; голое admin_ip_bind; = on off горячее
api_read_token строка, ≥ 16 символов не задан горячее
api_write_token строка, ≥ 16 символов, отличается от read не задан горячее

Гейт /api/v1 включается, если задана хотя бы одна из трёх учёток — пароль либо любой из токенов. Настроить один токен и получить открытую панель нельзя.

Если session_secret не задан, он генерируется случайно на каждую загрузку: токены сессий не переживут перезапуск и не примутся другим узлом.

Что печатается при старте#

Ровно одна строка о состоянии гейта и ровно одна о /metrics. Проверено на стенде:

hastreamer: WARNING admin UI + API are UNAUTHENTICATED — anyone who can reach the HTTP port has full read + control access. Set auth.admin_password (or auth.api_read_token / auth.api_write_token) to close the gate.
hastreamer: /metrics is CLOSED (it was public before 2026.08) — it accepts an admin session bearer only. Set auth.api_read_token to give a scraper its own non-admin credential.
hastreamer: /metrics is CLOSED (it was public before 2026.08) — it accepts `Authorization: Bearer` with auth.api_read_token, auth.api_write_token, an admin session token.

Есть и третий вариант первой строки — когда токены заданы, а пароля нет:

hastreamer: note auth.admin_password is unset — /api/v1 is gated by the static API token(s) alone and the admin UI cannot log in (POST /api/v1/auth/login 404s). Set auth.admin_password to restore the UI login.

Ошибки проверки конфигурации#

auth.api_read_token must not be empty (omit it entirely to leave that tier unconfigured)
auth.api_read_token must be at least 16 characters (use a generated random token)
auth.api_read_token and auth.api_write_token must differ (an equal pair silently grants write to the read token)

Порог длины существует потому, что перед этим гейтом нет задержки за неудачные попытки — в отличие от входа по паролю. Единственная защита от перебора — энтропия самого токена:

head -c 24 /dev/urandom | base64 | tr -d '=+/'
kQ8mVt3ZpR7bLxNfA2wYuJd0
Значение токена в сообщениях об ошибках не появляется

Ни в журнале, ни в ответе API, ни в тексте ошибки конфигурации. Сервер вообще не хранит токены — только их отпечатки, и сравнивает константным временем.

Сессия администратора#

POST /api/v1/auth/login#

Без аутентификации — единственный маршрут /api/v1, который никогда не закрыт гейтом.

Меняет пароль на bearer-токен сессии со сроком 12 часов.

TOKEN=$(curl -sS -X POST https://media.example.com/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"password":"длинный-пароль-администратора"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')

curl -sS https://media.example.com/api/v1/streams -H "Authorization: Bearer $TOKEN"
Код Когда
200 пароль верный, токен выдан
400 в теле нет ключа password либо это не JSON
401 неверный пароль; ответ отдаётся после безусловной задержки 600 мс
404 auth.admin_password не задан — входить некуда
413 тело больше 4 КиБ
429 открыто окно торможения для этого адреса

admin_ip_bind on дополнительно привязывает выданный токен к адресу, с которого выполнен вход: токен, украденный из журнала прокси, с другого адреса не сработает. За обратным прокси это требует корректно заполненного trusted_proxies — иначе привязка закрепится на адресе самого прокси.

Отказ приходит без WWW-Authenticate

Тело отказа — {"error":"unauthorized"}, заголовка WWW-Authenticate нет намеренно: иначе браузер показал бы поверх панели своё системное окно ввода пароля вместо формы входа.

Статические токены API#

Отдельная учётка для мониторинга и интеграций: не пароль администратора и не полный доступ. Два уровня, где write включает в себя read.

Уровень Методы /api/v1/* /metrics GET /api/v1/config POST /api/v1/auth/media-grants
api_read_token только GET, HEAD, OPTIONS да нет — 403 нет — 403
api_write_token все да да нет — 403
сессия администратора все да да да

Проверенная матрица:

R='случайный-токен-только-на-чтение'
W='случайный-токен-на-запись'
B=https://media.example.com

curl -s -o /dev/null -w '%{http_code}\n'      -H "Authorization: Bearer $R" $B/api/v1/streams
curl -s -o /dev/null -w '%{http_code}\n'      "$B/api/v1/streams?token=$R"
curl -s                                       -H "Authorization: Bearer $R" $B/api/v1/config
curl -s -X PATCH -d '{}'                      -H "Authorization: Bearer $R" $B/api/v1/config/globals
curl -s -X POST -d '{"stream":"cam","intent":"live","protocols":["hls"]}' \
                                              -H "Authorization: Bearer $R" $B/api/v1/auth/media-grants
curl -s -o /dev/null -w '%{http_code}\n'      -H "Authorization: Bearer $W" $B/api/v1/config
200
401
{"error":"forbidden","reason":"read_token_cannot_read_config"}
{"error":"forbidden","reason":"read_token_cannot_write"}
{"error":"forbidden","reason":"api_token_is_not_a_media_credential"}
200

Три правила, о которые спотыкаются#

1. Только заголовок Authorization: Bearer. ?token= для статических токенов не принимается — никогда и ни на одном маршруте. Запасной ход через строку запроса существует исключительно для короткоживущего токена сессии и только на GET /api/v1/events, потому что EventSource в браузере не умеет задавать заголовки. Статический токен живёт вечно и не ротируется при использовании: в URL он осядет в журналах прокси, в истории браузера и в заголовке Referer.

Проверено ровно на том маршруте, где послабление есть:

curl -s -o /dev/null -w '%{http_code}\n' "$B/api/v1/events?token=$R"
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $R" "$B/api/v1/events"
401
200

2. Уровень чтения не видит GET /api/v1/config. Этот ответ отдаёт текст конфигурации дословно — вместе с admin_password, api_write_token, секретом auth_backend и учётными данными камер в URL источников. Прочитать его значило бы за один запрос выйти из режима «только чтение». Уровню записи чтение оставлено: он и так может переписать этот файл.

Правило применяется к фактической цели запроса, а не к его пути: обращение через маршрут-перенаправитель GET /api/v1/fleet/self/config даёт тот же 403 с той же причиной (проверено).

3. Отказ по недостатку прав — 403, а не 401. Учётные данные подлинные, не хватает уровня; 401 заставил бы корректный клиент вечно повторять тот же самый токен. 401 остаётся за «ничего не предъявлено или не совпало».

Блок auth { } панель управления перезаписывает целиком

Сохранение вкладки Security и прохождение мастера первого запуска переиздают весь блок auth { }. Токен, выставленный руками в файле и не введённый в форму, будет удалён вместе с закрытием гейта. Правьте токены либо только в файле, либо только через панель.

/metrics закрыт теми же учётными данными#

curl -s -o /dev/null -w '%{http_code}\n' https://media.example.com/metrics
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $R" https://media.example.com/metrics
401
200

Экспозиция называет каждый поток, состояние его источника, число зрителей и загрузку хоста — то же операционное раскрытие, ради которого закрыт /api/v1. Арки «не настроено — значит открыто» здесь нет: узел без единой учётки отвечает 401 каждому запросу.

Настройте сборщику метрик собственный api_read_token и передавайте его заголовком: ?token= не принимается и здесь.

Два разных /metrics

GET /metrics в корне хоста — это экспозиция в формате Prometheus. GET /api/v1/metrics — это полный JSON-дамп внутреннего состояния, совсем другая поверхность с другим форматом и своим ограничением на размер. Не перепутайте их в настройках сборщика.

Выпуск медиа-гранта для встроенного плеера#

POST /api/v1/auth/media-grants#

Администратортолько сессия; оба статических токена получают 403.

Единственный встроенный в продукт способ выпустить медиа-credential. Им пользуется плеер панели управления, чтобы посмотреть закрытый поток, не превращая админ-сессию в доступ к видео.

Тело запроса — не более 4 КиБ, все поля обязательны, лишние отвергаются:

curl -sS -X POST https://media.example.com/api/v1/auth/media-grants \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"stream":"cam","intent":"live","protocols":["hls"]}'
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6Imhhc3RyZWFtZXItbWVkaWEtZ3JhbnQrand0IiwidiI6MX0…",
 "expires_at":1787143783,"server_time":1787143483,"ttl_secs":300,
 "session_expires_at":1787143783}
Поле Допустимые значения
stream путь потока; для intent: "vod" обязан начинаться с vod/
intent live · publish · dvr · vod · preview · export · timeshift
protocols 1…8 уникальных значений, без api; live не может включать whip; publish обязан быть ровно ["whip"]; dvr, vod, preview, export, timeshift — только hls

Срок жизни фиксирован — 300 секунд, изменить его нельзя. Грант привязан к точному ресурсу, к точному набору протоколов и к адресу клиента; подписан он отдельным ключом со своим типом токена, поэтому в клиентский бэкенд авторизации никогда не проваливается.

Код Причина
400 invalid_request — лишнее или отсутствующее поле, недопустимое сочетание
403 предъявлен статический токен API (api_token_is_not_a_media_credential)
409 admin_auth_disabled — не задан admin_password; media_auth_disabled — не настроен auth_backend
413 тело больше 4 КиБ
Это не способ выдать ссылку зрителю

Пять минут и жёсткая привязка к адресу делают такой грант непригодным для раздачи. Для зрителей выпускайте токены своим кодом — Генераторы токенов.

Аварийный отзыв всех сессий просмотра#

POST /api/v1/auth/rotate-session-keys#

Администратор

Заводит новый ключ подписи билетов. Предыдущий остаётся в ограниченном окне проверки (до трёх), чтобы уже идущие сессии не оборвались мгновенно; билеты под выпавшим ключом перестают работать. Тело запроса не требуется.

curl -sS -X POST https://media.example.com/api/v1/auth/rotate-session-keys \
  -H "Authorization: Bearer $TOKEN"
Код Причина
409 узел входит в кластер — согласованно меняйте session_keys { secret } вместо ротации; либо на узле нет яруса билетов (не настроен auth_backend)
Это не отзывает гранты

Ротация обесценивает билеты, то есть уже установленные сессии. Токен зрителя, у которого не истёк exp, немедленно установит сессию заново. Чтобы отозвать сами токены, меняйте секрет подписи auth_backend (для hs256) либо ключ поставщика (для jwks).

Что публично намеренно#

Поверхность Почему
оболочка /admin и её файлы, шрифты страница обязана загрузиться, чтобы показать форму входа; данных в ней нет
/{поток}/embed.html тестовый плеер; само видео закрывается авторизацией просмотра
вся медиа-выдача закрывается блоком auth_backend, а не гейтом управления

Проверка#

Гейт управления закрыт, а токен чтения не может писать
curl -s -o /dev/null -w '%{http_code}\n' https://media.example.com/api/v1/streams
curl -s -o /dev/null -w '%{http_code}\n' https://media.example.com/metrics
curl -s -H "Authorization: Bearer $R" https://media.example.com/api/v1/config
401
401
{"error":"forbidden","reason":"read_token_cannot_read_config"}

Если первый запрос вернул 200 — не задана ни одна из трёх учёток блока auth { }; при старте об этом было напечатано предупреждение.