XMPP with ISPConfig

Discussion in 'Developers' Forum' started by theWeird, Feb 20, 2015.

  1. CubAfull

    CubAfull Member

    Which linux distro are you using? I'm on debian jessie.... You can also use "coturn". This is the modern version of
    rfc5766-turn-server.
     
  2. ccoudsi

    ccoudsi New Member

    I'm using Ubuntu 16.04 LT
    Cheers,
     
  3. CubAfull

    CubAfull Member

    Then install coturn:
    Code:
    sudo apt-get update
    sudo apt-get install coturn
     
  4. ccoudsi

    ccoudsi New Member

    Hi cubAfull,
    I installed "coturn" and I followed your guidelines everything looks ok till I got to "metronome host configuration" not sure how to map these fields to my configuration:
    Code:
    turncredentials_secret = "YoUrSecReTAuthStrinG";
    turncredentials_host = "turn.your.domain";
    turncredentials_port = "3478";
    turncredentials_ttl = "3600";
    Here's my configuration, I added port & host per STUN configuration:
    Code:
    external_services = {
            ["turn.mydomain.com"] = {
               [1] = {
                port = "3478",
                transport = "udp",
                type = "turn"
               },
    
              [2] = {
                port = "3478",
                transport = "tcp",
                type = "turn"
              }
            }
          };
    Thanks for your support.
    Cheers,
     
  5. CubAfull

    CubAfull Member

    Well, first you need to download and enable this two modules: mod_turncredentials and mod_extdisco, please note, I don't know if metronome have or work with this modules!
    With mod_turncredentials you don't have to insert "external_services = {..." because this is the purpose of this module. Advertise the STUN/TURN services and let only registered users use the TURN service.
     
  6. CubAfull

    CubAfull Member

    Last edited: Nov 26, 2016
  7. alexalouit

    alexalouit New Member

    Hello everyone,
    I am watching this thread with interest. Congrats for your work at all on the XMPP module.

    I found another small bug, but detrimental to proper functioning in multi-server environment.
    When a domain is added or modified, dns entries are pushed, but it appears that the DNS server(s) ID is not provided.

    I don't know very well the management of templates on ISPConfig, any idea of what happens?
     
  8. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    I just want to update you on "coturn" after installation and enabling all required modules, I was able to make Audio/Video/ShareScreen between 2 Jitsi windows client, In my opinion this would be really a big plus to "Metronome" server.
    Best Regards.
     
  9. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    Sorry, I just found out, the Jitsi app uses their own turn servers as soon I unchecked it, and added my turn server now I'm getting error 401 Unauthorized in my turn logs !!!
    Code:
    242303: handle_udp_packet: New UDP endpoint: local addr 192.168.0.40:3478, remote addr XXX.XXX.XXX.XXX:5020
    242303: session 001000000000000035: realm <turn.XXXXXi.com> user <>: incoming packet message processed, error 401: Unauthorized
    242303: session 001000000000000035: realm <turn.XXXXX.com> user <>: incoming packet BINDING processed, success
    242303: check_stun_auth: Cannot find credentials of user <[email protected]>
    
     
  10. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    One more update, what i found out in the JITSI client, when you edit your account under "ICE" if I uncheck (Use Jitsi's STUN server in case no other servers are available", without adding any STU servers then it is working OK, it is discovering my TURN server and authenticating.
    Can you please check if this authentication is OK??? I replaced my IP and domain with xxxxxx

    Code:
    357: handle_udp_packet: New UDP endpoint: local addr 192.168.0.40:3478, remote addr xxx.xxx.xxx.xxx:5040
    357: session 001000000000000005: realm <turn.xxxxxx.com> user <>: incoming packet BINDING processed, success
    
    Thanks .
     
  11. alexalouit

    alexalouit New Member

    fix in https://git.ispconfig.org/ispconfig/ispconfig3/commit/77eb71fca0550857e00740ff69cd8e1a08133094

     
  12. CubAfull

    CubAfull Member

    Nice catch @alexalouit :)

    @ccoudsi maybe yo need to add the proper DNS records for STUN/TURN discovery. Please check this page: https://wiki.xmpp.org/web/SRV_Records
    I reed >here< that Jitsi in't compatible with turncredentials mechanism? Also note that the prosody turncredentials module isn't compatible with metronome. Right now, in metronome, I'm testing this module modded by me. Just replace your actual mod_turncredentials code with this:

    UPDATED
    Code:
    -- * Metronome IM *
    --
    -- This file is part of the Metronome XMPP server and is released under the
    -- ISC License, please see the LICENSE file in this source package for more
    -- information about copyright and licensing.
    
    -- XEP-0215 implementation for time-limited turn credentials
    -- in Metronome XMPP server.
    
    --turncredentials_secret = "keepthissecret";
    --turncredentials = {
    --    ["turn.example.com"] = {
    --        [1] = { port = "3478", transport = "udp", type = "stun" },
    --        [2] = { port = "3478", transport = "tcp", type = "stun" },
    --        [3] = { port = "5349", transport = "tcp", type = "stuns" },
    --        [4] = { port = "3478", transport = "udp", type = "turn" },
    --        [5] = { port = "3478", transport = "tcp", type = "turn" },
    --        [6] = { port = "5349", transport = "tcp", type = "turns" }
    --    }
    --};
    
    local st = require "util.stanza";
    local ipairs, pairs = ipairs, pairs;
    
    local secret = module:get_option_string("turncredentials_secret");
    local ttl = module:get_option_number("turncredentials_ttl", 86400);
    local services = module:get_option_table("turncredentials", {});
    local hmac_sha1 = require "util.hmac".sha1;
    local base64 = require "util.encodings".base64;
    local os_time = os.time;
    
    if not (secret) then
        module:log("error", "turncredentials: missing turncredentials_secret param.");
        return;
    end
    
    local xmlns_extdisco = "urn:xmpp:extdisco:1";
    
    module:add_feature(xmlns_extdisco);
    
    local function render(host, type, info, userpart, nonce, reply)
        if not type or info.type == type then
         local data = {};
    
         data.host = host;
            data.port = info.port;
            data.transport = info.transport;
            data.type = info.type;
    
            if info.type == "turn" or info.type == "turns" then
              data.username = userpart;
              data.password = nonce;
         end
    
            module:log("debug", "turncredentials: sending reply for "..info.type.." request.");
    
            reply:tag("service", data):up();
        end
    end
    
    module:hook_global("config-reloaded", function() 
        secret = module:get_option_string("turncredentials_secret");
        ttl = module:get_option_number("turncredentials_ttl", 86400);
        services = module:get_option_table("turncredentials", {});
    end);
    
    module:hook("iq-get/host/"..xmlns_extdisco..":services", function (event)
        local origin, stanza = event.origin, event.stanza;
        local service = stanza:get_child("services", xmlns_extdisco);
        local service_type = service and service.attr.type;
        local now = os_time() + ttl;
      local userpart = tostring(now);
      local nonce = base64.encode(hmac_sha1(secret, tostring(userpart), false));
        local reply = st.reply(stanza);
        reply:tag("services", { xmlns = xmlns_extdisco });
    
        for host, service_info in pairs(services) do
            if #service_info > 0 then
                for i, info in ipairs(service_info) do 
                    render(host, service_type, info, userpart, nonce, reply);
                end
            else
                render(host, service_type, service_info, userpart, nonce, reply);
            end
        end
    
        return origin.send(reply);
    end);
    
    module:hook("iq-get/host/"..xmlns_extdisco..":credentials", function (event)
        local origin, stanza = event.origin, event.stanza;
        return origin.send(st.error_reply(
            stanza, "cancel", "feature-not-implemented", "Retrieving short-term credentials is not supported"
        ));
    end);
    
    
    With this module in metronome.conf.lua you must set for example this options:
    UPDATED
    Code:
    turncredentials_secret = "keepthissecret";
    turncredentials = {
        ["turn.example.com"] = {
            [1] = { port = "3478", transport = "udp", type = "stun" },
            [2] = { port = "3478", transport = "tcp", type = "stun" },
            [3] = { port = "5349", transport = "tcp", type = "stuns" },
            [4] = { port = "3478", transport = "udp", type = "turn" },
            [5] = { port = "3478", transport = "tcp", type = "turn" },
            [6] = { port = "5349", transport = "tcp", type = "turns" }
        }
    };
    
    Small update to the mod_auth_external (working fine here):
    Code:
    -- * Metronome IM *
    
    local nodeprep = require "util.encodings".stringprep.nodeprep;
    local lpc = require "lpc";
    
    local config = require "core.configmanager";
    local log = module._log;
    local host = module.host;
    local script_type = config.get(host, "external_auth_protocol") or "generic";
    assert(script_type == "ejabberd" or script_type == "generic");
    local command = config.get(host, "external_auth_command") or "";
    assert(type(command) == "string");
    assert(not host:find(":"));
    local new_sasl = require "util.sasl".new;
    local external_backend = module:require "sasl_aux".external_backend;
    local plain_test = module:require "sasl_aux".hashed_plain_test;
    
    local pid;
    local readfile;
    local writefile;
    
    local function send_query(text)
            if pid and lpc.wait(pid,1) ~= nil then
                log("debug","error, process died, force reopen");
                pid=nil;
            end
            if not pid then
                    log("debug", "Opening process " .. command);
                    pid, writefile, readfile = lpc.run(command);
            end
            if not pid then
                    log("debug", "Process failed to open");
                    return nil;
            end
         
            log("debug", "send_query check: " .. text);
    
            writefile:write(text);
            writefile:flush();
            if script_type == "ejabberd" then
                    return readfile:read(4);
            elseif script_type == "generic" then
                    return readfile:read();
            end
    end
    
    function do_query(kind, username, password)
            if not username then return nil, "not-acceptable"; end
            username = nodeprep(username);
            if not username then return nil, "jid-malformed"; end
    
            local query = (password and "%s:%s:%s:%s" or "%s:%s:%s"):format(kind, username, host, password);
            local len = #query
            if len > 1000 then return nil, "policy-violation"; end
    
            if script_type == "ejabberd" then
                    local lo = len % 256;
                    local hi = (len - lo) / 256;
                    query = string.char(hi, lo)..query;
            end
            if script_type == "generic" then
                    query = query..'\n';
            end
    
            local response = send_query(query);
            if (script_type == "ejabberd" and response == "\0\2\0\0") or
                    (script_type == "generic" and response == "0") then
                            return nil, "not-authorized";
            elseif (script_type == "ejabberd" and response == "\0\2\0\1") or
                    (script_type == "generic" and response == "1") then
                            return true;
            else
                    log("debug", "Nonsense back");
                    return nil, "internal-server-error";
            end
            log("debug", "response: " .. response);
    end
    
    function new_external_provider(host)
            local provider = { name = "external" };
    
            function provider.test_password(username, password)
                    log("debug", "test_password check: " .. username .. " " .. password);
                    return do_query("auth", username, password);
            end
    
            function provider.set_password(username, password)
                    log("debug", "set_password check: " .. username .. " " .. password);
                    return do_query("setpass", username, password);
            end
    
            function provider.user_exists(username)
                    log("debug", "user_exists check: " .. username);
                    return do_query("isuser", username);
            end
    
            function provider.create_user(username, password)
                    log("debug", "create_user check: " .. username .. " " .. password);
                    return nil, "Account creation/modification not available.";
            end
    
            function provider.get_sasl_handler(session)
                    local testpass_authentication_profile = {
                            external = session.secure and external_backend,
                            plain_test = plain_test,
                            session = session,
                            host = host
                    };
    
                    if session.secure then
                            testpass_authentication_profile.order = { "external", "plain_test" };
                        else
                            testpass_authentication_profile.order = { "plain_test" };
                        end
    
                    return new_sasl(module.host, testpass_authentication_profile);
            end
    
            return provider;
    end
    
    module:add_item("auth-provider", new_external_provider(host));
     
    Last edited: Nov 26, 2016
  13. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    Because I'm using internal authentication this what I did:
    I replaced your "mod_turncredentials " with my current module, and I added your "metronome.conf.lua" to my "/etc/metronome/hosts/mydomain.com.cfg.lua " the results is, I was able to do a voice call between 2 PC's using Jitsi but the quality of the voice is very bad, so I went back to my original settings and everything is OK, Audio & Video & Share Desktop !!
    I was monitoring "turn" logs in both configurations and both looks similar !!!
    How I can verify if Jitsi using my turn server for the communications ????
    From my "turn" log it looks OK example :
    Code:
    415171: handle_udp_packet: New UDP endpoint: local addr 192.168.0.40:3478, remote addr xxx.xxx.xxx.xxx:5008
    415171: session 000000000000000053: realm <turn.mydomain.com> user <>: incoming packet BINDING processed, success
    
    One more thing, I removed my "external_services" section in my "mydomain.cfg.lua" and restarted metronome and I still able to make a voice and video calls ???
    Code:
     external_services = {
             ["turn.mydomain.com"] = {
               [1] = {
                port = "3478",
                transport = "udp",
                type = "turn",
                secret = "MySecretpassword"
               },
    
              [2] = {
                port = "3478",
                transport = "tcp",
                type = "turn",
                secret= "MySecretpassword"
              }
            },
             ["stun.mydomain.com"] = {
               [1] = {
                port = "3478",
                transport = "udp",
                type = "stun",
                secret = "MySecretpassword"
               },
    
              [2] = {
                port = "3478",
                transport = "tcp",
                type = "stun",
                secret= "MySecretpassword"
              }
            }
          };
    
    FYI, I added the following services to my DNS:
    Code:
    _turn._tcp.mydomain.com
    _turns._tcp.mydomain.com
    _turn._udp.mydomain.com
    
    _stun._tcp.mydomain.com
    _stuns._tcp.mydomain.com
    _stun._udp.mydomain.com
    
    Regarding the link you sent me >here< it looks this conversation was in 2014 about "Jitsi Android" BTW, Here's the code for Jitsi Android >here< I was able to build and test on my Android phone. Because I didn't find Android app that supports "jingle".

    >>>>Ok, I just compared the files and saw your modifications I will implement external authentication and see the results

    Cheers,
     
    Last edited: Nov 24, 2016
  14. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    I updated the following files:
    My "mod_turncredentials.lua" with your modifications
    My "domain.cfg.lua" with your "metronome.conf.lua" (Basically I replaced my external section with your modifications, and I enabled External Auth)
    My "mod_auth_external.lua" with your "mod_auth_external.lua"

    The results is as follow:
    PSI (audio) ok --> turn log errors:
    Code:
     incoming packet message processed, error 401: Unauthorized
    
    JItsi (audio & video) Ok, takes more time to connect -->turn log errors:
    Code:
     incoming packet message processed, error 401: Unauthorized
    
    So, I'm confused how it works with the 401 errors ??
    Also, I noticed you removed the modifications from "mod_auth_external.lua " which suppose to make metronome work with ISPconfig XMPP users ?? so, how it is working now??
    One more thing I noticed metronome complains about some folders not found example "offline" ...

    Cheers,
     
    Last edited: Nov 26, 2016
  15. CubAfull

    CubAfull Member

    Hi,
    This is how is working right now in my servers...
    Modules from this post (updated): https://www.howtoforge.com/community/threads/xmpp-with-ispconfig.68501/page-2#post-351845
    DNS SRV records ONLY for STUN. (Jitsi use this)
    You see this "401: Unauthorized" message because Jitsi don't support XEP-0215 (time-limited turn credentials)
    When you make a call in Jitsi, go to:
    Code:
    %LOCALAPPDATA%\Jitsi\log
    You most see this (with your server IP):
    Code:
    Auto discovered harvester is STUN harvester(srvr: xxx.xxx.xxx.xxx:3478/udp)
    I use the turncredentials module only for Jappix (it support XEP-0215)
    Use the updated module I post. For me is working ok.
     
  16. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    I just updated the modules and removed "turn" services from DNS, now it is working OK, but if I use "Jitsi", it takes 20 seconds to start ringing on the other side, not sure why?? it was almost instantaneously before !!
    I noticed this error:
    Code:
    Nov 26 10:42:53 anon.MYDOMAIN.com:turncredentials error   turncredentials: missing turncredentials_secret param.
    
    I went back to my configuration and it looks ok where:
    /etc/turnserver.conf
    Code:
    lt-cred-mech
    use-auth-secret
    static-auth-secret= keepthissecret
    realm=turn.mydomain.com
    cert=/etc/metronome/certs/ mydomain.com.cert
    pkey=/etc/metronome/certs/ mydomain.com.key
    
    and I have added the following to my "
    /etc/metronome/hosts/ mydomain.com.cfg.lua"
    Code:
    turncredentials_secret = "keepthissecret";
    turncredentials = {
        ["turn.MYDOMAIN.com"] = {
            [1] = { port = "3478", transport = "udp", type = "stun" },
            [2] = { port = "3478", transport = "tcp", type = "stun" },
            [3] = { port = "5349", transport = "tcp", type = "stuns" },
            [4] = { port = "3478", transport = "udp", type = "turn" },
            [5] = { port = "3478", transport = "tcp", type = "turn" },
            [6] = { port = "5349", transport = "tcp", type = "turns" }
        }
    };
    
    Do I have the "turncredentials_secret" missing from some other file??



    Cheers,
     
    Last edited: Nov 26, 2016
  17. CubAfull

    CubAfull Member

    Hi, you need to add this params to the global config (/etc/metronome/metronome.conf.lua) before any defines host, not in the host config.
    For the mod_websockets problem, just remove the s from the word websockets in your loaded modules. Use just "websocket".
     
  18. ccoudsi

    ccoudsi New Member

    Hi CubAfull,
    Thanks for your support, I don't see any more obvious errors, the only thing left is the 20 seconds delay to start ringing on the receiver side, any ideas where should I look???Do you see this issue?? I remember when I switched from internal authentication to external this issue came up !!!
    Cheers,
     
  19. CubAfull

    CubAfull Member

    Mmmm in my case, making a call from my office to my house using Jitsi or Jappix the ringing is almost instant. When you make a call with Jitsi, do you see "Auto discovered harvester is STUN harvester(srvr: xxx.xxx.xxx.xxx:3478/udp)" in the Jitsi log?
     
  20. ccoudsi

    ccoudsi New Member

    Yes, I see "
    harvester is STUN":
    Code:
    12:10:16.983 INFO: [89] impl.protocol.jabber.IceUdpTransportManager.createIceAgent().173 Auto discovered harvester is STUN harvester(srvr: xxx.xxx.xxx.xxx:3478/udp)
    Also I see the
    harvester is TURN too later in the log:
    Code:
    13:13:12.853 INFO: [622] impl.protocol.jabber.IceUdpTransportManager.createIceAgent().173 Auto discovered harvester is TURN harvester(srvr: xxx.xxx.xxx.xxx:3478/udp)
     

Share This Page