Документация на Flussonic Media Server

  1. Быстрый старт
  2. How-to
  3. Потоковое вещание
    1. Варианты источников
    2. Переключение источников
    3. Публикация на Flussonic
    4. Прием мультикаста
    5. Серверные плейлисты
    6. Микширование
    7. Захват с SDI
    8. Отправка на другие серверы
    9. Распознавание DVB субтитров
    10. Наложение логотипа
  4. Транскодер
    1. Логотип
    2. Hardware
    3. Скриншоты
    4. Мозаика
  5. DRM
    1. Simple CAS
    2. Conax DRM
    3. BuyDRM (KeyOS)
    4. Widevine
    5. PallyCon
    6. EzDRM
  6. Авторизация
    1. Конструктор бэкендов
    2. Сервис сбора статистики
    3. Domain lock
    4. Middleware
    5. Secure links
    6. Ограничение сессий
    7. Мультиавторизация
    8. Бан IP адресов
    9. DVR
    10. Aliaser
  7. API
    1. HTTP API
    2. Events API
    3. MySQL API
    4. SQL API для кластеров
    5. SNMP
  8. Кластер
    1. Ретрансляция
    2. Кластерный захват
    3. Балансировщик нагрузки
    4. Пиринг
    5. Организация CDN
  9. VOD
    1. Кэш
    2. Облако
    3. Транскодирование файлов
    4. Мультибитрейтный VOD из файлов
  10. DVR
    1. Настройка
    2. Timeshift
    3. Catchup
    4. Проигрывание
    5. Экспорт в MP4
    6. Доступ по протоколам
    7. Timelapse
    8. API
    9. Кластеризация DVR
    10. Репликация
    11. Облако
  11. Воспроизведение
    1. HLS
    2. embed.html
    3. HTML5 с низкой задержкой
    4. Плеер HTML5 с низкой задержкой
    5. MPEG-TS
    6. RTMP
    7. DASH
    8. HDS
    9. RTSP
    10. Multicast
    11. Multicast с постоянным битрейтом
    12. WebRTC
    13. H.265
  12. Администрирование
    1. Установка
    2. Обновление
    3. Конфигурация
    4. Мониторинг
    5. Производительность
    6. Лицензия
    7. LUA скрипты
    8. Безопасность
    9. Let's Encrypt
    10. Миграция
  13. IPTV
    1. Захват спутникового видео
    2. Транскодирование
    3. Middleware в IPTV OTT
    4. Экспорт EPG со спутника
    5. Группы каналов
    6. Реклама

Пишем свой авторизационный бэкенд

На этой странице попробуем показать вам как можно написать свой авторизационный бэкенд. Пожалуйста, прочитайте сперва следующие страницы документации:

Там подробно описано, как работает авторизация и что она может. А на этой странице мы займемся практикой.

Если кратко: Flussonic принимает запрос и спрашивает разрешение у внешней программы. Это может быть локальный lua скрипт или внешний HTTP ресурс. Lua скрипт хоть и локальный, но все равно это внешняя программа для Flussonic.

Авторизационный бэкенд должен работать быстро, потому что пока он "думает", клиент не может начать просмотр. Поэтому логику лучше выносить в Lua, либо быть уверенным, что внешний HTTP-ресурс работает быстро, а интернет-соединение стабильно и имеет небольшую задержку.

Что получает бэкенд на входе Anchor Anchor x2

У вас же есть установленный Flussonic? Создаем поток, на котором будем учиться и сразу включаем debug-логирование:

loglevel debug;

stream testing-auth {
  url fake://fake;
  auth /etc/flussonic/backend.lua;
}

Применяем конфигурацию и пытаемся открыть поток:

root@flussonic2:~# curl -I http://127.0.0.1/testing-auth/index.m3u8
HTTP/1.1 403 Forbidden

403 - доступ запрещен. Смотрим в /var/log/flussonic/flussonic.log:

2017-03-29 19:46:19.297 <0.22665.266> [testing-auth] Failed to run auth backend /etc/flussonic/backend.lua ([{token,undefined},{name,<<"testing-auth">>},{ip,<<"127.0.0.1">>},{type,<<"hls">>}]):
enoent

В логе появилась логичная ошибка - не удалось выполнить авторизационный бэкенд по причине enoent, что в переводе на русский означает - файл не найден. Давайте создадим файл, путь к которому указан в заданной нами конфигурации и заполним его одной строкой "return false":

root@flussonic2:~# cat /etc/flussonic/backend.lua
return false, {}

Повторяем запрос к http://127.0.0.1/testing-auth/index.m3u8 и снова смотрим в лог:

2017-03-29 19:56:31.712 <0.26075.266> [testing-auth] flu_session:287 Backend auth request "/etc/flussonic/backend.lua" ([{token,undefined},{name,<<"testing-auth">>},{ip,<<"127.0.0.1">>},{type,<<"hls">>},{request_type,new_session},{duration,0},{stream_clients,0},{total_clients,16},{session_id,<<"903f6ff7c5a29dfcd19c8855e7af50232bf32991">>},{country,<<"NONE">>},{user_name,<<"testing-auth">>},{port,80},{qs,<<>>},{host,<<"127.0.0.1">>},{user_agent,<<"curl/7.47.0">>},{media_request,<<"hls_live-hls_mbr_playlist">>}]): Code: false, auth_time: undefined, user_id: undefined, max_sessions: undefined, location: undefined, time: 0ms

Мы снова получили отказ в просмотре, но теперь уже потому что наш бэкенд запрещает просмотр всем. Давайте разберемся, какую информацию Flussonic передал нашему бэкенду:

  • {token,undefined} - токен передается в запросе в соответствующем параметре. Вот так: /index.m3u8?token=12345. В данном случае - не задан.
  • name,<<"testing-auth">> - имя потока
  • {ip,<<"127.0.0.1">>} - IP-адрес пользователя
  • {type,<<"hls">>} - тут указывается тип запроса, возможные значения: hls,hls-dvr,mpegts,api,rtmp и т.д.
  • {request_type,new_session} - значение new_session если создается новая сессия или update_session, если перепроверяется старая
  • {duration,0} - срок жизни сессии, через это время Flussonic снова обратится к бэкенду за проверкой права просмотра
  • {stream_clients,0} - количество онлайн клиентов на этом потоке
  • {total_clients,16} - всего клиентов на сервере в момент обращения к бэкенду
  • {session_id,<<"903f6ff7c5a29dfcd19c8855e7af50232bf32991">>} - уникальный ID сессии. ID сессии вычисляется вот так: hash(name + ip + token)
  • {country,<<"NONE">>} - страна, указывается двухбуквенный код, например: RU, EN, CN и т.д. в данном случае, не указан, т.к. запрос с локалхоста
  • {user_name,<<"testing-auth">>} - снова имя потока, но может отличаться, если использовать алиасер
  • {port,80} - порт на который пришел запрос. Flussonic может слушать сразу несколько портов для HTTP-запросов, RTMP, RTSP.
  • {qs,<<>>} - query string, все параметры из запроса. Подробнее рассмотрим позже.
  • {host,<<"127.0.0.1">>} - интерфейс сервера, на который пришел запрос.
  • {user_agent,<<"curl/7.47.0">>} - User Agent клиента.
  • {media_request,<<"hls_live-hls_mbr_playlist">>} - почти тоже самое, что и type, но подробнее.

Вся эта информация доступна бэкенду, т.е. вам, чтобы написать собственную логику. Давайте перейдем к первому примеру.

Проверка IP-адреса Anchor Anchor x2

Отрываем наш бэкенд и добавляем всего 3 строки:
root@flussonic2:~# cat /etc/flussonic/backend.lua
if req.ip == "127.0.0.1" then
  return true, {}
end

return false, {}

Используя req.ip мы получили доступ к значению переменной с IP-адресом пользователя. Соотвественно, чтобы получить информацию из остальных параметров, используем req.token, req.name, req.type, req.user_agent и т.д.

Делаем новый запрос, уже посложнее:

root@flussonic2:~# curl -I 'http://127.0.0.1/testing-auth/index.m3u8?token=12345&admin=true'
HTTP/1.1 200 OK

Ура, никаких ошибок, просмотр разрешен. Смотрим что в логе:

2017-03-29 20:21:08.735 <0.29479.266> [testing-auth] flu_session:287 Backend auth request "/etc/flussonic/backend.lua" ([{token,<<"12345">>},{name,<<"testing-auth">>},{ip,<<"127.0.0.1">>},{type,<<"hls">>},{request_type,new_session},{duration,0},{stream_clients,0},{total_clients,16},{session_id,<<"3d3cd528904dbf5aeac3e648daa68bcdef3370b0">>},{country,<<"NONE">>},{user_name,<<"testing-auth">>},{port,80},{qs,<<"token=12345&admin=true">>},{host,<<"127.0.0.1">>},{user_agent,<<"curl/7.47.0">>},{media_request,<<"hls_live-hls_mbr_playlist">>}]): Code: true, auth_time: undefined, user_id: undefined, max_sessions: undefined, location: undefined, time: 0ms

В запросе мы указали параметры token и admin. В бэкенд токен попадает в соответствующей переменной req.token, а значение admin можно получить из req.qs. Таким образом, вы можете использовать любые параметры, кроме ?token, хоть и с некоторыми ограничениями.

Бэкенд несложно модифицировать, чтобы проверять несколько адресов:

root@flussonic2:~# cat /etc/flussonic/backend.lua
if req.ip == "127.0.0.1" or req.ip == "192.168.0.1" or req.ip == "192.168.0.2" then
  return true, {}
end

return false, {}

Большое количество адресов, а тем более подсети, указывать такие образом неудобно, но для небольшой группы подойдет. Дальше рассмотрим как указывать большие списки адресов.

А если поменять местами false и true и скрипт будет разрешать всем просмотр, но запрещать указанным адресам. Это тоже может быть полезно, если вы обнаружили, что кто-то 24/7 захватывает ваш поток, а значит рестримит.

Результат

Итак, что мы получили: наш авторизационный бэкенд запрещает просмотр всем, кроме тех, у кого IP-адрес - "127.0.0.1".

Что нам это дает? Мы можем добавить камеру, видео с которой никто не должен увидеть, кроме владельца или тех, кому он доверяет. Или запретить определенным лицам доступ ко всем потокам (например, кто-то пытается захватить сразу все потоки, что создает большую нагрузку).

Проверка IP-адресов (сложнее) Anchor Anchor x2

Добавим функцию, которая проверяет IP-адрес по массиву:

allowed = {
  "192.168.1.3", -- Other Flussonic
  "5.6.7.8", -- Me
  "1.3.4.5", -- Office
  "172.20.1.1", -- Max
  "8.8.8.8" -- Google
}

function member(items, el)
   for _, item in pairs(items) do
      if el == item then
         return true
      end
   end
   return false
end

if member(allowed, req.ip) then
  return true, {code = 200}
end

return false, {code = 403}

На первый взгляд конструкция может показаться сложной, но она позволяет записать все IP-адреса в столбик и сделать примечания. Его просто читать и редактировать.

Используем другую функцию, чтобы проверить подсеть:

networks = {
  "192.168.1.",
  "192.168.2."
}

function member_match(items, el)
  for _, item in pairs(items) do
    if string.match(el, item) then
      return true
    end
  end
  return false
end

if member_match(networks, req.ip) then
  return true, {}
end

Эта функция сравнивает IP-адрес со строкой из массива networks, поэтому можно указать только крупные подсети: /24 /16 /8, но и этого более чем достаточно, когда речь идет об управляемой операторской сети.

HTTP-бэкенд Anchor Anchor x2

Хоть Flussonic и позволяет обращаться напрямую к HTTP-бэкенду, может быть полезно использовать локальный lua-бэкенд для проверки админских IP-адресов и токенов.

Пример:

if req.ip == "192.168.1.1" or req.token == "TrustMeImAdmin" then
  return true, {}
end

url = "http://iptv.myservice.com/auth.php?"..http.qs_encode(req)
reply = http.get(url)

if reply.code == 200 then
  t = {}
  if reply.headers["x-authduration"] then
    t["auth_time"] = tonumber(reply.headers["x-authduration"])
  end
  if reply.headers["x-max-sessions"] then
    t["max_sessions"] = tonumber(reply.headers["x-max-sessions"])
  end
  if reply.headers["x-userid"] then
    t["user_id"] = reply.headers["x-userid"]
  end
    return true, t
  else
    return false,{["code"] = reply.code}
  end
end

Скрипт проверит IP-адрес и токен локально, не делая HTTP-запрос на другой сервер, позволяя администраторам получить доступ к потокам, даже если авторизационных бэкенд ничего не знает про них. Это полезно, если HTTP-бэкенд - это IPTV Middleware, которая позволяет просмотр только с приставок, а проверять работу потоков надо другими плеерами или специальным софтом.

На самом деле, этот очень похожий пример есть на странице про мультиавторизацию, так что давайте усложним его:

if req.token == "TrustMeImAdmin" and req.host == "192.168.1.2" then
  return true, {}
end

url = "http://iptv.myservice.com/auth.php?"..http.qs_encode(req)
reply = http.get(url)

if reply.code == 200 then
  t = {}
  if reply.headers["x-authduration"] then
    t["auth_time"] = tonumber(reply.headers["x-authduration"])
  end
  if reply.headers["x-max-sessions"] then
    t["max_sessions"] = tonumber(reply.headers["x-max-sessions"])
  end
  if reply.headers["x-userid"] then
    t["user_id"] = reply.headers["x-userid"]
  end
    return true, t
  else
    return false,{["code"] = reply.code}
  end
end

Данный скрипт разрешит доступ с токеном "TrustMeImAdmin" только если запрос придет на интерфейс с адресом "192.168.1.2", что исключает возможность использования токена сотрудниками из дома в личных целях. Только из офиса. Для этого на сервере должно быть два интерфейса: публичный и локальный, доступный только из офиса.