Persistent Passwords on Self Hosted Rooms

Hey all,

We are working on a self hosted Jitsi Meet server, and we have a use case that is not persistently covered by the Jitsi Meet documentation.

We would like to be able to set persistent chatrooms on our server that have persistently set passwords. Our use case being that we have a specific client assigned to a particular URL, and we send that client a URL and a password for that room. They should be able to continue to use both the password and URL over the course of time.

Passwords are currently not persistent, and they are reset when all peers leave the room. This is particularly problematic when the admin and the client lose connection to the server in the middle of a meeting, and have to recreate and reset the conference rooms password when we reestablish connection.

Hopefully theres a known way to do this! Thanks.

We want the conference room passwords to be persistent, even if no users are in the conference room. Is that a possibility?

7 Likes

There are two options here:

  • Install and use prosody 0.11 and set muc_room_default_persistent=true https://prosody.im/doc/modules/mod_muc
    Not sure how it will work, but you can experiment and report.
  • Keep current behaviour with non persistent rooms, but on every create check for a password and set it, by querying an external service and by implementing your custom module.
module:hook("muc-room-pre-create", function(event)
            local pass = http.query.for.password for this event.room;
            event.room:set_password(pass);
end);

The second approach for sure works as we use it.

4 Likes

Damencho,

I will be experimenting with the first option over the next few days and will let you know how badly I blow things up!

Thank you so much for helping out.

1 Like

I have two open threads about two separate instances, FYI so you dont get confused.

We tried the first one on our test instance, and prosody 0.11 basically broke Jitsi-meet, wherein users who join a conference room cant see each other or chat at all. Starting a room also doesn’t prompt the user to set a password.

|Nov 18 21:42:37 general|info|Hello and welcome to Prosody version 0.10.0|

|Nov 18 21:42:37 general|info|Prosody is using the select backend for connection handling|

|Nov 18 21:42:37 portmanager|info|Activated service ‘s2s’ on [::]:5269, :5269|

|Nov 18 21:42:37 portmanager|info|Activated service ‘c2s’ on [::]:5222, :5222|

|Nov 18 21:42:37 portmanager|info|Activated service ‘legacy_ssl’ on no ports|

|Nov 18 21:42:37 mod_posix|info|Prosody is about to detach from the console, disabling further console output|

|Nov 18 21:42:37 mod_posix|info|Successfully daemonized to PID 10316|

|Nov 18 21:42:37 portmanager|info|Activated service ‘component’ on [127.0.0.1]:5347, [::1]:5347|

|Nov 18 21:42:37 portmanager|info|Activated service ‘http’ on [::]:5280, :5280|

|Nov 18 21:42:37 portmanager|error|Error binding encrypted port for https: No key present in SSL/TLS configuration for https port 5281|

|Nov 18 21:42:37 portmanager|error|Error binding encrypted port for https: No key present in SSL/TLS configuration for https port 5281|

|Nov 18 21:42:37 portmanager|info|Activated service ‘https’ on no ports|

|Nov 18 21:42:38 jcp559f52507370|info|Incoming Jabber component connection|

|Nov 18 21:42:38 jitsi-videobridge.domain. com:component|info|External component successfully authenticated|

|Nov 18 21:42:38 c2s559f52465dd0|info|Client connected|

|Nov 18 21:42:39 jcp559f52581950|info|Incoming Jabber component connection|

|Nov 18 21:42:39 focus.domain. com:component|info|External component successfully authenticated|

|Nov 18 21:42:39 c2s559f52465dd0|info|Stream encrypted (TLSv1.2 with ECDHE-RSA-AES256-GCM-SHA384)|

|Nov 18 21:42:39 c2s559f52465dd0|info|Authenticated as focus@auth.domain. com|

|Nov 18 21:43:08 mod_posix|warn|Received SIGTERM|

|Nov 18 21:43:08 general|info|Shutting down: Received SIGTERM|

|Nov 18 21:43:08 c2s559f52465dd0|info|Client disconnected: connection closed|

|Nov 18 21:43:08 jcp559f52507370|info|component disconnected: jitsi-videobridge.domain. com (false)|

|Nov 18 21:43:08 jcp559f52581950|info|component disconnected: focus.domain. com (false)|

|Nov 18 21:43:08 general|info|Shutting down…|

|Nov 18 21:43:08 general|info|Shutdown status: Cleaning up|

|Nov 18 21:43:08 general|info|Shutdown complete|

|Nov 18 21:43:10 startup|info|Hello and welcome to Prosody version 0.11.3|

|Nov 18 21:43:10 startup|info|Prosody is using the select backend for connection handling|

|Nov 18 21:43:10 certmanager|error|SSL/TLS: Failed to load ‘/etc/prosody/certs/localhost.key’: Check that the permissions allow Prosody to read this file. (for localhost)|

|Nov 18 21:43:10 localhost:tls|error|Error creating context for c2s: error loading private key (Permission denied)|

|Nov 18 21:43:10 certmanager|error|SSL/TLS: Failed to load ‘/etc/prosody/certs/localhost.key’: Previous error (see logs), or other system error. (for localhost)|

|Nov 18 21:43:10 localhost:tls|error|Error creating contexts for s2sout: error loading private key (system lib)|

|Nov 18 21:43:10 certmanager|error|SSL/TLS: Failed to load ‘/etc/prosody/certs/localhost.key’: Previous error (see logs), or other system error. (for localhost)|

|Nov 18 21:43:10 localhost:tls|error|Error creating contexts for s2sin: error loading private key (system lib)|

|Nov 18 21:43:10 portmanager|info|Activated service ‘c2s’ on :5222, [::]:5222|

|Nov 18 21:43:10 portmanager|info|Activated service ‘legacy_ssl’ on no ports|

|Nov 18 21:43:10 mod_posix|info|Prosody is about to detach from the console, disabling further console output|

|Nov 18 21:43:10 mod_posix|info|Successfully daemonized to PID 10964|

|Nov 18 21:43:10 portmanager|info|Activated service ‘s2s’ on :5269, [::]:5269|

|Nov 18 21:43:14 c2s55ab8f080200|info|Client connected|

|Nov 18 21:43:14 c2s55ab8f080200|info|Client disconnected: connection closed|

|Nov 18 21:43:20 c2s55ab8f089f90|info|Client connected|

|Nov 18 21:43:20 c2s55ab8f089f90|info|Client disconnected: connection closed|

|Nov 18 21:43:26 c2s55ab8f093240|info|Client connected|

|Nov 18 21:43:26 c2s55ab8f093240|info|Client disconnected: connection closed|

|Nov 18 21:43:32 c2s55ab8f09cda0|info|Client connected|

|Nov 18 21:43:32 c2s55ab8f09cda0|info|Client disconnected: connection closed|

|Nov 18 21:43:38 c2s55ab8f0a6a50|info|Client connected|

|Nov 18 21:43:38 c2s55ab8f0a6a50|info|Client disconnected: connection closed|

|Nov 18 21:43:44 c2s55ab8f0b0a40|info|Client connected|

|Nov 18 21:43:44 c2s55ab8f0b0a40|info|Client disconnected: connection closed|

|Nov 18 21:43:50 c2s55ab8f0ba9e0|info|Client connected|

|Nov 18 21:43:50 c2s55ab8f0ba9e0|info|Client disconnected: connection closed|

|Nov 18 21:44:26 c2s55ab8f0c74d0|info|Client connected|

|Nov 18 21:44:26 c2s55ab8f0c74d0|info|Client disconnected: connection closed|

|Nov 18 21:45:02 c2s55ab8f0d4950|info|Client connected|

|Nov 18 21:45:02 c2s55ab8f0d4950|info|Client disconnected: connection closed|

|Nov 18 21:45:38 c2s55ab8f0e2130|info|Client connected|

|Nov 18 21:45:38 c2s55ab8f0e2130|info|Client disconnected: connection closed|

|Nov 18 21:46:14 c2s55ab8f0ef680|info|Client connected|

|Nov 18 21:46:14 c2s55ab8f0ef680|info|Client disconnected: connection closed|

|Nov 18 21:46:50 c2s55ab8f0fcad0|info|Client connected|

|Nov 18 21:46:50 c2s55ab8f0fcad0|info|Client disconnected: connection closed|

|Nov 18 21:47:26 c2s55ab8f10a0c0|info|Client connected|

|Nov 18 21:47:26 c2s55ab8f10a0c0|info|Client disconnected: connection closed|

|Nov 18 21:48:22 mod_posix|warn|Received SIGTERM|

|Nov 18 21:48:22 startup|info|Shutting down: Received SIGTERM|

|Nov 18 21:48:22 general|info|Shutting down…|

|Nov 18 21:48:22 general|info|Shutdown status: Cleaning up|

|Nov 18 21:48:22 general|info|Shutdown complete|

I referenced the permissions for the certificate on a separate install and widened the permissions on this machine before restarting the Prosody service.

|Nov 18 21:48:22 startup|info|Hello and welcome to Prosody version 0.11.3|

|Nov 18 21:48:22 startup|info|Prosody is using the select backend for connection handling|

|Nov 18 21:48:22 mod_posix|info|Prosody is about to detach from the console, disabling further console output|

|Nov 18 21:48:22 mod_posix|info|Successfully daemonized to PID 11682|

|Nov 18 21:48:22 portmanager|info|Activated service ‘s2s’ on [::]:5269, :5269|

|Nov 18 21:48:22 portmanager|info|Activated service ‘c2s’ on [::]:5222, :5222|

|Nov 18 21:48:22 portmanager|info|Activated service ‘legacy_ssl’ on no ports|

Obviously it doesnt seem to be interacting at all with the jitsi-meet-prosody component.

Looking at the second option, is the external service Prosody itself? I’m a little confused on what the external service would be in this case.

Do you see an error in the client’s js console?

Here are the errors that prosody 0.11 appears to generate

XML Parsing Error: mismatched tag. Expected: .
Location: https://domain. com/http-bind?room=test
Line Number 6, Column 3: http-bind:6:3
[JitsiMeetJS.js] : UnhandledError: null Script: null Line: null Column: null StackTrace: Error: “Strophe: request id 1.1 error 502 happened”
log strophe.util.js:89
error strophe.js:2083
_onRequestStateChange strophe.js:5569
Logger.js:125:17
i Logger.js:125
getGlobalOnErrorHandler JitsiMeetJS.js:548
onerror middleware.js:100
callErrorHandler GlobalOnErrorHandler.js:61
log strophe.util.js:89
error strophe.js:2083
_onRequestStateChange strophe.js:5569
[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request id 1.1 error 502 happened Logger.js:125:17
[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request errored, status: 502, number of errors: 1 Logger.js:125:17
XML Parsing Error: mismatched tag. Expected: .
Location: https://domain. com/http-bind?room=test
Line Number 6, Column 3: http-bind:6:3
[JitsiMeetJS.js] : UnhandledError: null Script: null Line: null Column: null StackTrace: Error: “Strophe: request id 2.2 error 502 happened”
log strophe.util.js:89
error strophe.js:2083
_onRequestStateChange strophe.js:5569
Logger.js:125:17
i Logger.js:125
getGlobalOnErrorHandler JitsiMeetJS.js:548
onerror middleware.js:100
callErrorHandler GlobalOnErrorHandler.js:61
log strophe.util.js:89
error strophe.js:2083
_onRequestStateChange strophe.js:5569
[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request id 2.2 error 502 happened Logger.js:125:17
[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request errored, status: 502, number of errors: 2

Etc. etc. etc.

[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request id 4.4 error 502 happened Logger.js:125:17
i Logger.js:125
log strophe.util.js:90
error strophe.js:2083
_onRequestStateChange strophe.js:5569
[modules/xmpp/strophe.util.js] <t.a/i.Strophe.log>: Strophe: request errored, status: 502, number of errors: 4 Logger.js:125:17
[modules/xmpp/xmpp.js] : (TIME) Strophe connfail[giving-up]: 37839 Logger.js:125:17
[modules/statistics/statistics.js] <b.sendAnalyticsAndLog>: {“type”:“operational”,“action”:“connection.failed”,“attributes”:{“error_type”:“connection.otherError”,“error_message”:“giving-up”}} Logger.js:125:17

Hi Damencho,
i have tried both options:

  • Installed prosody 0.11, set muc_room_default_persistent=true
    I set a password, left the call and je-joined. Prosody will crash.

  • for the second option i think i also need prosody 0.11. In prosody 0.10 i do not find where the event muc-room-pre-create ist fired. I have no lua knowledge, but syntax is not accepted

            local pass = http.query.for.password for this event.room;
    modulemanager   error   Unable to load module 'muc_room_pre_crea
    te/mod_muc_room_pre_create': ...ules/mod_muc_room_pre_create/mod_muc_room_pre_cr
    eate.lua:3: <name> expected near 'for'

Joachim

1 Like

I think there just needs to be more documentation on the different use cases for authentication.

It’s weird that the Jitsi Devs are using a set up they wont document publicly.

The ‘secure domain’ set up is very specific. A more common set up is groups who may be given admin or user access to particular rooms. Why no set up for this documented is strange.

Hopefully we can one out soon.

2 Likes

Hello Damencho,
can you tell about were to configure the hook for muc-room-pre-create? I enabled to module at the Component “conference.***” “muc” but couln’t succeed. As far I can see the additional module is called by the hook but afterwards the password is reset to nil in password.lib.lua (the same hook).

Kind regards,
Martin

Hi,

I was looking for a solution to a similar problem and could not get this done using persistent rooms nor with the muc-room-pre-create hook. However, using the muc-room-created hook seems to work.

Here is what I came up with (sorry if thats horrible code, first time that I use Lua):

local store = module:open_store("persistent_muc_passwds", "map");
if not store then
    module:log("error","Failed to open storage.");
    return nil;
end

module:hook("muc-room-destroyed", function (event)
    local room = event.room;
    local password = room:get_password(room);

    if password then
        module:log("debug", "Room %s with pass %s about to be destroyed", room, password);
        if not store then
                module:log("debug","failed to open store on destroy");
            return nil;
        end
        
        local now = os.time();
        store:set(room.jid, "last_used", now);
        store:set(room.jid, "password" , password);
        module:log("debug", "Stored %s, %s for room %s", password, now, room);
    elseif store:get(room.jid, "password") then
	    -- moderator removed old password from restored room, delete stored entry
        store:set(room.jid, "last_used", nil);
        store:set(room.jid, "password" , nil);
        module:log("debug", "Deleted stored entries for room %s", room);
    end

    return nil; -- can be removed
end, 0);

module:hook("muc-room-created", function (event)    
    local room = event.room;
    module:log("debug","hooked room create for %s", room);

    local old_pass = store:get(room.jid, "password");
    if not old_pass then
        module:log("debug", "No password to restore for room %s", room);
	return nil;
    end

    module:log("debug", "Loaded old password '%s' for room %s", old_pass, room);
    local last_used = store:get(room.jid, "last_used");
    if not last_used then
        module:log("debug", "No stored timestamp found for room %s. Removing stored entry.", room, err);
        store:set(room.jid, "last_used", nil);
        store:set(room.jid, "password" , nil);
	return nil;
    end

    if is_room_stale(last_used) then
        -- delete entry
        store:set(room.jid, "last_used", nil);
        store:set(room.jid, "password" , nil);
	module:log("debug", "deleted password for stale room %s", room); 
	return nil;
    end

    -- restore old pass for the mucroom
    local success = room:set_password(old_pass);
    if not success then 
        module:log("warn", "Failed to set old password %s for restored room %s.", old_pass, room);
    end
    
    module:log("debug", "Set password '%s' for restored room %s.", old_pass, room);
    return nil;
end, 0);

function is_room_stale(last_used)
    local days = module:get_option_number("days_to_persist_muc_passwds", 30);
    module:log("debug", "Function is_stale() called with '%s', days is set to %s", last_used, days);
    local daysfrom = os.difftime(os.time(), last_used) / (24 * 60 * 60); 
    local roomage = math.floor(daysfrom) ;
    module:log("debug", "roomage is %s days", roomage);
    if roomage then
        return roomage > days; 
    end
    return false;
end

Save this as mod_persist_muc_passwords.lua in the modules directory and add the module to the muc’s list of enabled modules. You should also change the storage for this module to something persistent (instead of “memory”). My muc config looks like this:

Component "muc.meet.jitsi" "muc"
    --storage = "internal";
    default_storage = "memory";
    storage =  {
	    persistent_muc_passwds = "internal";
    }
    days_to_persist_muc_passwds = 7;
    modules_enabled = {
            "persist_muc_passwords";  
    }

Tested with Prosody Version: 0.11.2-1~bpo9+1 in the docker-jitsi-meet setup.

I am aware of several problems with this approach:

  • passwords are stored in plaintext
  • whoever restores the old room will be moderator and can change or remove the password
  • does not work with authentication/securedomain EDIT: apparently this works also with authentication, my config was wrong when I tested this.

Maybe somebody who has more experience with prosody, jitsi and the Lua language can help to improve this module in this regards…

7 Likes

Hi plotka,

tnx very much, this is what i looked for!

Here i have Ubuntu 19.10 / prosody 0.11.2-1
Your solution is working here together with authentification (secure domain).
Passwords are stored in files like /var/lib/prosody/conference%2emeet%2eexample%2eorg/persistent_muc_passwds/confname%40conference%2emeet%2eexample%2eorg.dat

Joachim

Hi!

Cannot get the snippet working, the ‘success’ is always nil. Please could you help out?

-- /usr/lib/prosody/modules/
module:hook("muc-room-created", function (event)
    local room = event.room;
    module:log("debug", "hooked room create for %s", tostring(room));

    local pass = randPW(8);
    local success = room:set_password(pass);

    if not success then
        module:log("warn", "Failed to set password '%s' for %s.", pass, tostring(room));
    end
    
    module:log("debug", "Set password '%s' for %s.", pass, tostring(room));
    return nil;
end, 0);

-- https://rosettacode.org/wiki/Password_generator#Lua
function randPW (length)
    local index, pw, rnd = 0, ""
    local chars = {
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
        "abcdefghijklmnopqrstuvwxyz",
        "0123456789"
    }
    repeat
        index = index + 1
        rnd = math.random(chars[index]:len())
        if math.random(2) == 1 then
            pw = pw .. chars[index]:sub(rnd, rnd)
        else
            pw = chars[index]:sub(rnd, rnd) .. pw
        end
        index = index % #chars
    until pw:len() >= length
    return pw
end


Prosody 0.9.10

# Prosody directories
Data directory:         /var/lib/prosody
Plugin directory:       /usr/share/jitsi-meet/prosody-plugins/;/usr/lib/prosody/modules/
Config directory:       /etc/prosody
Source directory:       /usr/lib/prosody

# Lua environment
Lua version:                    Lua 5.1

Lua module search paths:
  /usr/lib/prosody/?.lua
  /usr/local/share/lua/5.1/?.lua
  /usr/local/share/lua/5.1/?/init.lua
  /usr/local/lib/lua/5.1/?.lua
  /usr/local/lib/lua/5.1/?/init.lua
  /usr/share/lua/5.1/?.lua
  /usr/share/lua/5.1/?/init.lua

Lua C module search paths:
  /usr/lib/prosody/?.so
  /usr/local/lib/lua/5.1/?.so
  /usr/lib/x86_64-linux-gnu/lua/5.1/?.so
  /usr/lib/lua/5.1/?.so
  /usr/local/lib/lua/5.1/loadall.so

LuaRocks:               Not installed

# Lua module versions
lfs:            LuaFileSystem 1.6.3
lxp:            LuaExpat 1.3.0
pposix:         0.3.6
socket:         LuaSocket 3.0-rc1
ssl:            0.5.1
1 Like

That method never returns in prosody 0.9 https://github.com/bjc/prosody/blob/0.9.14/plugins/muc/muc.lib.lua#L296
While in 0.11 it returns https://github.com/bjc/prosody/blob/0.11/plugins/muc/password.lib.lua#L16

1 Like

@damencho Thanks, Damian!

module:hook("muc-room-created", function (event)
    local room = event.room;
    module:log("debug", "hooked room create for %s", tostring(room));
    local pass = tostring(randPW(8));
    local res = room:set_password(pass);
    module:log("info", "Set password '%s' for %s.", pass, tostring(room));
    return res;
end, 0);

However the hook is constantly repeated, the password is not set and in the logs I can see multiple set password events.
Please could you give a hint on what Im doing wrong?
Thanks, very much appreciated.

I believe I had some trouble when returning anything else but nil, so your return res; statement could be a problem. However, as mentioned before, I do not really know Lua, nor the prosody API.

Still the same…

-- For Prosody 0.9 /usr/lib/prosody/modules/
module:hook("muc-room-created", function (event)
    local room = event.room;
    module:log("debug", "hooked room create for %s", tostring(room));
    local pass = tostring(randPW(8));
    local res = room:set_password(pass);
    module:log("info", "Set password '%s' for %s.", pass, tostring(room));
    return nil;
end, 0);

@damencho Damian, maybe you can help?

Did you try the muc-room-pre-create hook? Did not work for me on prosody 0.11 because jicofo failed to join the room but this may be different when using prosody 0.9.

Well, you need to let jicofo in the room, as jicofo is the first one to join normally.

Well, you need to let jicofo in the room, as jicofo is the first one to join normally.

Yes, thats why I switched to using the muc-room-created hook, to set the password only after jicofo joined.

@osys Another idea: have you tried to lower the priority of your hook to some negative value instead of 0?

Merging my discussion into this as I’m now in the same situation.

1 Like