Подключение к jitsi с отсутствием авторизации может стать небезопасным. Для того, чтобы избежать незваных гостей во время встречи, совещания или личной беседы стоит задуматься об авторизации. Под катом я подробно расскажу как нам улучшить наш сервис jitsi-meet с помощью JWT-токена.


Ниже я описал пошаговую инструкцию для установки и настройки JWT-токена на debian. Весь процесс можно провести как на уже работающем сервисе jitsi-jibri (с моего мана точно работает), так и в новой установке после завершения настройки jitsi.


apt install git cmake luarocks libssl-dev liblua5.2
wget http://packages.prosody.im/debian/pool/main/p/prosody-trunk/prosody-trunk_1nightly1273-1~buster_amd64.deb
dpkg -i prosody-trunk_1nightly1273-1~buster_amd64.deb

apt install prosody -y
echo deb http://packages.prosody.im/debian $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list
wget https://prosody.im/files/prosody-debian-packages.key -O- | sudo apt-key add -
apt update
apt upgrade
apt-get install jitsi-meet-tokens prosody-modules lua5.2 liblua5.2 luarocks libssl-dev

В процессе установки будет запрошен Application ID и Application secret. Указываем их и обязательно запоминаем, они нужны будут для генерации токена (если вдруг забыли их, то не страшно, их можно будет посмотреть/поменять в конфигурационном файле prosody)


luarocks install basexx

В конец файла /etc/prosody/prosody.cfg.lua добавим строчку:


vim /etc/prosody/prosody.cfg.lua

Include "conf.d/*.cfg.lua"

Так же находим строку начинающуюся "s2s_" и перед ней добавляем:


c2s_require_encryption = false

Продолжаем установку:


luarocks download lua-cjson
luarocks unpack lua-cjson-2.1.0.6-1.src.rock

В файле lua-cjson-2.1.0.6-1/lua-cjson/lua_cjson.c поменяем строку 743:


vim lua-cjson-2.1.0.6-1/lua-cjson/lua_cjson.c

len = lua_objlen(l, -1);
на
len = lua_rawlen(l, -1);

Сохраняем файл и продолжаем установку:


cd lua-cjson-2.1.0.6-1/lua-cjson
luarocks make --force
cd ..
git clone https://github.com/ASolomatin/luajwt.git
cd luajwt/

Внесём изменения в файл luajwtjitsi-1.3-7.rockspec:


package = "luajwtjitsi"
version = "1.3-7"

source = {
        url = "git://github.com/ASolomatin/luajwt/",
        tag = "replace_luacrypto"
}

description = {
        summary = "JSON Web Tokens for Lua",
        detailed = "Very fast and compatible with pyjwt, php-jwt, ruby-jwt, node-jwt-simple and others",
        homepage = "https://github.com/jitsi/luajwt/",
        license = "MIT <http://opensource.org/licenses/MIT>"
}

dependencies = {
        "lua >= 5.2",
        "luaossl >= 20190731-0",
        "lua-cjson >= 2.1.0",
        "lbase64 >= 20120807-3"
}

build = {
        type = "builtin",
        modules = {
                luajwtjitsi = "luajwtjitsi.lua"
        }
}

Продолжаем:


luarocks install luajwtjitsi 2.0-0
systemctl restart prosody

Проверяем на наличие ошибок, если есть, то чистим файл и перезапускаем просодию:


vim /var/log/prosody/prosody.err
systemctl restart prosody
vim /var/log/prosody/prosody.err

Для генерации токена можно перейти на сайт и там задав параметры сгенерировать его, а после вставить в URL в формате https:///name_room?jwt=token .

На сайте вводим следующие данные:


HEADER:ALGORITHM & TOKEN TYPE

{
  "typ": "JWT",
  "alg": "HS256"
}
PAYLOAD:DATA

{
 "context": {
    "user": {
      "name": "<user>",
      "id": "<user>@gmail.com",   #optional
      "email": "<user@gmail.com>",  #optional
      "avatar": "<link to user's avatar>"  #optional
    }
  },
  "aud": "jitsi",
  "iss": "<Application_ID>",
  "sub": "<FQDN>",
  "room": "*",
  "exp": 98753496345768,
  "moderator": true
}
VERIFY SIGNATURE

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),

<Application_secret>

) secret base64 encoded

image


Привести к такому виду с подстановкой своих значение вместо <...>

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


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


vim /etc/prosody/conf.avail/<FQDN>.cfg.lua

Находим наш виртуальный хост и приводим его к следующему значению:


VirtualHost "<FQDN>"        
    authentication = "token"
    app_id="<APP_ID>"
    app_secret="<APP_SECRET>"
    allow_empty_token = false
    allow_unencrypted_plain_auth = true
  …
    ssl = {
        key = "/etc/prosody/certs/<FQDN>.key";
        certificate = "/etc/prosody/certs/<FQDN>.crt";
    }
    av_moderation_component = "avmoderation.<FQDN>"
    speakerstats_component = "speakerstats.<FQDN>"
    conference_duration_component = "conferenceduration.<FQDN>"
    -- we need bosh
    modules_enabled = {
        "bosh";
        "pubsub";
        "ping"; -- Enable mod_ping
        "speakerstats";
        "external_services";
        "conference_duration";
        "muc_lobby_rooms";
        "muc_breakout_rooms";
        "presence_identity";
        "av_moderation";
    }
    c2s_require_encryption = false
    lobby_muc = "lobby.<FQDN>"
    breakout_rooms_muc = "breakout.<FQDN>"
    main_muc = "conference.<FQDN>"
    muc_lobby_whitelist = { "recorder.<FQDN>" } -- Here we can whitelist jibri to enter lobby enabled rooms

VirtualHost "guest.<FQDN>"
    authentication = "anonymous"
    modules_enabled = {
            "bosh";
            "pubsub";
            "ping"; -- Enable mod_ping
            "speakerstats";
            "conference_duration";
        }
    c2s_require_encryption = false

Component "conference.<FQDN>" "muc"
    restrict_room_creation = true
    storage = "memory"
    modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        "polls";
        "token_verification";
        "token_moderation";
        "muc_rate_limit";
    }
    admins = { "focus@auth.<FQDN>" }
    muc_room_locking = false
    muc_room_default_public_jids = true

Component "breakout.<FQDN>" "muc"
    restrict_room_creation = true
    storage = "memory"
    modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        "token_verification";
        "muc_rate_limit";
        "polls";
    }
    admins = { "focus@auth.<FQDN>" }
    muc_room_locking = false
    muc_room_default_public_jids = true

-- internal muc component
Component "internal.auth.<FQDN>" "muc"
    storage = "memory"
    modules_enabled = {
        "ping";
    }
    admins = { "focus@auth.<FQDN>", "jvb@auth.<FQDN>" }
    muc_room_locking = false
    muc_room_default_public_jids = true
    muc_room_cache_size = 1000

VirtualHost "auth.<FQDN>"
    ssl = {
        key = "/etc/prosody/certs/auth.<FQDN>.key";
        certificate = "/etc/prosody/certs/auth.<FQDN>.crt";
    }
    modules_enabled = {
        "limits_exception";
    }
    authentication = "internal_hashed"

-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.<FQDN>" "client_proxy"
    target_address = "focus@auth.<FQDN>"

Component "speakerstats.<FQDN>" "speakerstats_component"
    muc_component = "conference.<FQDN>"

Component "conferenceduration.<FQDN>" "conference_duration_component"
    muc_component = "conference.<FQDN>"

Component "avmoderation.<FQDN>" "av_moderation_component"
    muc_component = "conference.<FQDN>"

Component "lobby.<FQDN>" "muc"
    storage = "memory"
    restrict_room_creation = true
    muc_room_locking = false
    muc_room_default_public_jids = true
    modules_enabled = {
        "muc_rate_limit";
        "polls";
    }

В конец файла добавляем следующее (это понадобится для jibri)

-- internal muc component, meant to enable pools of jibri and jigasi clients
-- Component "internal.auth.<FQDN>" "muc" -- Данный блок уже может быть выше в конфиге
--    modules_enabled = {
--        "ping";
--    }
--    storage = "memory"
--    muc_room_cache_size = 1000

VirtualHost "recorder.<FQDN>"
    modules_enabled = {
        "ping";
    }
    authentication = "internal_plain"

Клонируем репозиторий с модулями и устанавливаем:


cd ~
git clone https://github.com/nvonahsen/jitsi-token-moderation-plugin.git
mv jitsi-token-moderation-plugin/mod_token_moderation.lua /usr/share/jitsi-meet/prosody-plugins/bak.mod_token_moderation.lua
git clone https://github.com/dumasti/jitsi_mods.git
mv /usr/share/jitsi-meet/prosody-plugins/mod_muc_lobby_rooms.lua /usr/share/jitsi-meet/prosody-plugins/bak.mod_muc_lobby_rooms.lua
mv jitsi_mods/*.lua /usr/share/jitsi-meet/prosody-plugins/
mv /usr/share/jitsi-meet/prosody-plugins/mod_token_moderation_grand.lua /usr/share/jitsi-meet/prosody-plugins/mod_token_moderation.lua

Меняем настройки в файле /etc/jitsi/meet/-config.js для включения гостевого хоста и другие полезности (опционально).


cp /etc/jitsi/meet/<FQDN>-config.js /etc/jitsi/meet/bak.<FQDN>-config.js
vim /etc/jitsi/meet/<FQDN>-config.js

/* eslint-disable no-unused-vars, no-var */

var config = {
    hosts: {
        domain: '<FQDN>',

        anonymousdomain: 'guest.<FQDN>',
        muc: 'conference.<FQDN>'
    },

    bosh: '//<FQDN>/http-bind',

    testing: {
    },
    flags: {
    },

    enableNoAudioDetection: true,

    enableNoisyMicDetection: true,

    startAudioOnly: true,

    startWithAudioMuted: false,

    resolution: 1080,

    constraints: {
        video: {
            height: {
                ideal: 720,
                max: 1080,
                min: 360
            }
        }
    },

    disableSimulcast: true,

    fileRecordingsEnabled: true,
    liveStreamingEnabled: true,
    hiddenDomain: 'recorder.<FQDN>',

    channelLastN: -1,

    enableWelcomePage: true,

    defaultLanguage: 'en',

    enableUserRolesBasedOnToken: true,

    p2p: {
        enabled: true,
        stunServers: [

            { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
        ]
    },

    analytics: {
    },

    deploymentInfo: {
    },

    mouseMoveCallbackInterval: 1000,
    makeJsonParserHappy: 'even if last key had a trailing comma'
};

/* eslint-enable no-unused-vars, no-var */

vim /etc/jitsi/jicofo/sip-communicator.properties

org.jitsi.jicofo.jibri.BREWERY=JibriBrewery@internal.auth.<FQDN>
org.jitsi.jicofo.jibri.PENDING_TIMEOUT=90
org.jitsi.jicofo.auth.URL=EXT_JWT:<FQDN>

systemctl restart jitsi-videobridge2 jicofo prosody

Все готово. Идем проверять.


Если jibri еще не установлен, но необходимо иметь возможность записывать или транслировать конференции, то идем сюда и продолжаем установку jibri.


Благодарю за внимание!


Благодарности за помощь

Благодарю за редакторские правки Дарью Гулькович. Так же выражаю благодарность за техническую поддержку (пендаль в нужном направлении) Никиту Суклич.


Свой сервер видеоконференций Jitsi. Часть 1
Свой мессенджер Matrix-synapse в связке с Jitsi-meet. Часть 3


Комментарии (2)


  1. Xtray
    31.05.2022 15:39

    ... токену-токену :)


  1. w3cproject
    01.06.2022 16:16
    +1

    Спасибо за статью, токены заработали.

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

    Понятно, что это не баг, а фича. Просто так работает лобби в jitsi.

    Наверное, решается изменением логики работы лобби и токенов в prosody-modules/{}.lua, но в стартовом состоянии работает именно так.