Flash Media Server: токенизация видео ссылок

This post is also available in: Английский

Данной статьей мы продолжаем серию подробных обзоров по разработке Flash Media Server C++ плагинов, которые позволяют решать важные задачи по построению специализированной логики обработки пользовательских запросов на предоставление контента. Рассмотренная в статье система RTMP URL токенизации контента на основе authorization плагина позволяет предоставлять контент по динамически генерируемым ссылкам вместо статических. Это часто необходимо делать по требованию правообладателей предоставляемого видео контента.

Содержание статьи

  • 1.Введение
  • 2.Общие рекомендации
  • 3.RTMP URL токенизация
  • 4.Кластер из origin серверов
  • 5.Запуск edge/origin серверов
  • 6.Заключение

Введение
Authorization плагины авторизуют доступ клиентских приложений к внутренней событийной модели FMIS. Этот плагин выполняется внутри коре (core) процесса после того, как соединение с клиентским приложением установлено (established), но перед тем, как оно будет принято коре процессом (accepted).

На сервере может быть установлено более одного authorization плагина. Например, один плагин будет авторизовывать проигрывание контента, а другой проверять доступ при публикации контента. Так как коре процесс загружает плагины в алфавитном порядке, то надо учесть, что если плагин А подписан на событие E_PLAY, то плагин В в цепочке плагинов не получит уже это событие, если плагин А его заблокирует!

Детальную информацию о типах событий, доступных полях для каждого события authorization плагина, можно получить на сайте Адоби в разделе Developing an Authorization plug-in.

Общие рекомендации
События authorization плагина делятся на два типа:

  • notify events — это события, которые не блокируют поток внутри коре процесса, т.е. поток коре процесса просто информирует плагин(ы) о неком событии и продолжает свое исполнение
  • authorize events — это события, которые блокируют поток, т.е. поток ждет непосредственной реакции плагина на событие и в зависимости от реакции плагина продолжит исполнение

При обработке обоих типов событий необходимо обязательно вызывать специальные сервисные методы, которые проинформируют коре процесс, что событие получено плагином и событие больше не нужно. Для notify событий — это onNotify(); если не вызывать этот метод для каждого полученного события, то события так и останутся висеть во внутренних структурах коре процесса и, фактически, будет происходить утечка памяти. Для authorize событий необходимо вызывать метод onAuthorize(); если этого не делать, то все будет хуже, чем с notify событиями — так как это блокирующие события, то рано или поздно FMS перестанет принимать входящие соединения, а текущие, уже подключенные, клиенты «подвиснут» ввиду того, что все рабочие потоки коре процессов будут ждать авторизации событий.

Также стоит отметить, что authorization плагин действует глобально на все SSAS приложения запущенные FMS. Если нужно, чтобы плагин получал события только для определенного приложения, то это можно сделать так: внутри плагина должно быть известно имя SSAS приложения (например, через конфигурационный файл), затем в обработчике события нужно получить имя SSAS приложения, для которого было сгенерировано событие и сравнить с целевым приложением. Пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void MyFmsAuthorizeEvent::authorize() {
    // целевое SSAS приложение, для которого будет работать плагин
    std::string our_app_name = "vod";
    bool allowed = true;
    switch(m_pAev->getType()) {
        case IFmsAuthEvent::E_PLAY: {
            // получаем имя SSAS приложения, для которого сгенерированно событие
            // оно может содержать также именя инстансов и т.д.
            std::string app_name = getStringField(m_pEvent, IFmsAuthEvent::F_APP_NAME);
            // достаем "чистое" имя SSAS приложения
            size_t dash_pos = app_name.find('/');
            if(dash_pos!=std::string::npos)
                app_name = app_name.substr(0, dash_pos);
            // для нашего ли приложения это событие?
            if(app_name!=our_app_name)
                break;
            // иначе обрабатываем событие
            .........
            allowed = ...;
        }
    }
    pServerCtx->onAuthorize(m_pEvent, allowed, NULL);
 }

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

RTMP URL Токенизация
Одной из техник повышения безопасности доступа к контенту является токенизация или обфускация пути до контента на сервере. Это может потребоваться по разным причинам обладателям прав на контент. Например, чтобы было невозможно вычислить злоумышленнику полный путь размещения контента на сервере или нужна организация ограниченного доступа к контенту с помощью токенизации.

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

Рассмотрим реализацию данной функциональности для двух популярных схем диплоймента FMS: для конфигурации только из Origin серверов, и для конфигурации состоящей из Edge и Origin серверов.

Фактически работа плагина для данной функциональности сводится к трансформации логического пути до контента, на физический, который вычисляется обычно на основе имени контента (в данном случае токене), который приходит из клиентского приложения.

Чтобы изменить физический путь до контента, нужно изменить значение поля F_STREAM_PATH, которое доступно для чтения для нескольких событий, но только для одного события E_FILENAME_TRANSFORM это поле может быть изменено. Это событие генерируется сразу после события E_PLAY, когда коре (core) процесс пытается маппировать логический путь до контента в физический. Причем оно будет генерироваться до тех пор, пока либо не будет найден контент по логическому пути, либо не будут перебраны все возможные источники для поиска контента.

В дальнейшем мы будем говорить только о типе событий, которые блокируют работу потока в коре процессе, т. е. authorize events, для того, чтобы полностью контролировать работу FMS.

Origin only конфигурация
В данной конфигурации работа плагина с токенизацией достаточно проста: плагин должен подписаться на событие E_FILENAME_TRANSFORM и изменить логический путь на физический.


Рис 1. Origin only конфигурация

Рассмотрим по шагам с примерами кода работу такого плагина.

1. Достаточно подписаться только на событие E_FILENAME_TRANSFORM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void FmsAuthAdaptor::getEvents(I32 aevBitAuth[], I32 aevBitNotf[], unsigned int count) {
    ....
    // отписываемся от все событий кроме нужного нам
    IFmsAuthEvent::EventType authExcludeEvent[] = {
        IFmsAuthEvent::E_APPSTART,
        IFmsAuthEvent::E_APPSTOP,
        IFmsAuthEvent::E_CONNECT,
        IFmsAuthEvent::E_DISCONNECT,
        IFmsAuthEvent::E_PLAY,  
        //IFmsAuthEvent::E_FILENAME_TRANSFORM,  
        IFmsAuthEvent::E_STOP,
        IFmsAuthEvent::E_SEEK,
        IFmsAuthEvent::E_PAUSE,
        IFmsAuthEvent::E_PUBLISH,
        IFmsAuthEvent::E_UNPUBLISH,
        IFmsAuthEvent::E_LOADSEGMENT,
        IFmsAuthEvent::E_ACTION,
        IFmsAuthEvent::E_CODEC_CHANGE,
        IFmsAuthEvent::E_RECORD,
        IFmsAuthEvent::E_RECORD_STOP
    };
    m_pFmsAuthServerContext->excludeEvents(aevBitAuth, count, authExcludeEvent, sizeof(authExcludeEvent)/sizeof(authExcludeEvent[0]));
    ....
}

2. В обработчике события получаем токен-имя контента, вычисляем физический путь и изменяем логический путь на физический:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void MyFmsAuthorizeEvent::authorize() {
    bool allowed = false;
    switch(m_pAev->getType()) {
        case IFmsAuthEvent::E_FILENAME_TRANSFORM: {
            // получаем имя контента
            std::string stream_name = getStringField(pEvent, IFmsAuthEvent::F_STREAM_NAME);
            // вычисляем физический путь на основе полученного токена
            const char* real_path = ...............
            // смогли ли найти токен и вычислить физический путь?
            if(!real_path) {
                // что-то пишем в лог ...
                break;
            }
            // меняем физический путь
            if(!setStringField(pEvent, IFmsAuthEvent::F_STREAM_PATH, real_path)) {
                // что-то пишем в лог
                break;
            }
            allowed = true;    
        }
    }
    pServerCtx->onAuthorize(pEvent, allowed, NULL);
}

Существует одна небольшая неприятность с событием E_FILENAME_TRANSFORM: если заблокировать первое событие в цепочке событий, которые генерируются для каждого возможного источника с контентом, то остальные события все равно будут сгенерированы, хотя мы уже вычислили, что по пришедшему токену не существует контента физически.

В принципе это не страшно, если вычисление физического пути недорогая операция и не требует отправки запроса во внешнюю систему. Но если это не так, то эту проблему можно обойти подписавшись дополнительно на событие E_PLAY, в котором произвести первичную валидацию токена и авторизовать или заблокировать событие. Или, например, после первого неуспешного вычисления физического пути по токену, принудительно изменять физический путь на контент со специальным (алтернативным) содержанием.

Таким образом, в текущих версиях FMS блокировать событие E_FILENAME_TRANSFORM не имеет большого смысла.

Edge/Origin конфигурация
В данной конфигурации, как известно, edge сервер обычно выполняет роль кеширующего прокси. Т. е. сначала Edge сервер пытается найти контент в файловом кеше и если его там не обнаружит(может отсутствовать только нужный фрагмент), то будет запрашивать с Origin серверов. Далее предполагаем, что файловый кеш на Edge сервере включен.

В принципе для данной конфигурации достаточно функционала описанного на предыдущем шаге, и плагин можно разместить только на Origin сервере. Почему только? В данной конфигурации не будут генерироваться события E_FILENAME_TRANSFORM для коре процессов на Edge сервере, что логично, так как SSAS приложения (а соответственно и конфигурационные файлы, где настроены виртуальные директории) выполняются на Origin сервере.

Когда поступит запрос на проигрывание контента, Edge сервер не сможет найти нужный фрагмент контента в файловом кеше, потому что контент — это просто токен! Edge сервер пошлет запрос на Origin сервер. Там будет трансформирован физический путь до контента на основе токена(имя контента). Ответ вместе с частью контента будет отослан обратно Edge серверу, который узнает об изменениях в физическом пути контента(это информация присылается с Origin сервера, так как она используется для организации файл-кеша на Edge сервере) и далее либо Edge сервер будет и дальше запрашивать части контента с Origin сервера, либо уже будет использовать контент из файл-кеша, если он был получен ранее.


Рис 2. Обмен сообщениями между Edge и Origin серверами

Чтобы задача не казалось тривиальной, добавим требование, помимо работы токенизации имен контента, нужно еще производить геофильтрацию входящих запросов на проигрывание контента на основе IP адресов, что также требуется практически всеми правообладателями для соблюдения условий лицензирования при доступе к их видео контенту.

Правильнее было бы производить такую геофильтрацию в access плагине, но имея уже работающий authorization плагин реализуем это в нем. Также, это дополнительно позволит гарантировать, что никакие запросы на проигрывание контента не минуют этапа геофильтрации!

Таким образом мы получим следующую схему:


Рис 3. Плагины для геофильтрации на Edge и токенизации на Origin

Плагин на Edge сервере будет выполнять:

  • первичную валидацию токенов
  • геофильтрацию запросов на основе IP адреса для каждой единицы контента

Плагин на Origin сервере будет выполнять:

  • трансформацию физического пути до контента на основе полученного токена (имя контента полученное от клиентского приложения)

Плагин для Edge сервер должен:
1. Подписаться на событие E_PLAY. Код, как это делать был уже приведен выше.
2. В обработчике события произвести валидацию токена и геофильтрацию запроса к контенту на основе IP адреса

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void MyFmsAuthorizeEvent::authorize() {
    bool allowed = false;
    switch(m_pAev->getType()) {
        case IFmsAuthEvent::E_PLAY: {
            // проверить валидность токена
            // получаем имя контента
            std::string stream_name = getStringField(pEvent, IFmsAuthEvent::F_STREAM_NAME);
            if(!validate_token(stream_name)) {
                // что-то пишем в лог...
                break;
            }
            // получаем ip
            std::string cip = getStringField(pEvent, IFmsAuthEvent::F_CLEINT_IP);
            // проверяем ip адрес на доступ к контенту
            if(!content_available_for_ip(stream_name, cip)) {
                // что-то пишим в лог...
                break;
            }
            // авторизуем событие
            allowed = true;
        }
    }
    pServerCtx->onAuthorize(pEvent, allowed, NULL);
}

Понятно, что валидацию токена и доступ к контенту можно объединить в одну операцию — это зависит от архитектуры вашей системы.

Плагин для Origin сервера полностью повторяет код приведенный в разделе «Конфигурация из origin серверов».

Заключение
Особое внимание при разработке authorization плагинов нужно уделить Вашему коду, который отвечает за скорость обработки событий, не забывать оповещать коре процесс о том, что событие больше не нужно или авторизовать события. Также, «тяжелые» операции со внешними системами стоит делегировать специальным потокам с организацией пула, таймаутов и т. д.

Успешной доставки видео контента в большом количестве!