Prosody is a nice XMPP/Jabber server, but after unknown events connected with migrating to the new home server machine, it broke. Specifically, when the client connects, the server is supposed to propose SASL authentication mechanisms, which it does, but the number in the list is zero.
I put a fair amount of work into debugging this, with no solution. A big
problem with Prosody is that it is stuck with Lua5.1 (back version), although
the package on the SuSE Build Service seems to be kind of a hybrid of 5.1 and
5.2. I would not be at all surprised if version skew is causing my SASL
problems. So I've decided to attempt installing DJabberd. The operative
word here is attempt
.
The biggest problem with DJabberd is its skimpy documentation. Basically you have to read each Perl module. Fortunately the modules have embedded PODs. Here is what I did to install DJabberd.
Installation dependencies. All of this group are either in the main distro, or I had already obtained them from the SuSE Build Service, or I did so for this purpose.
Additional material was needed:
DJabberd::Config is not installed and not on SBS. CPAN tarball with complete DJabberd source including ./lib/DJabberd/Config.pod . I copied that file to /usr/lib/perl5/site_perl/DJabberd/Config.pod . Now perldoc can find it. But the entire content is shown on the CPAN page for DJabberd.
You need DJabberd::SASL from https://github.com/djabberd/DJabberd/blob/master/lib/DJabberd/SASL.pm and you need some methods. Actually this file is in the CPAN tarball: lib/DJabberd/SASL.pm lib/DJabberd/Stanza/SASL.pm . I copied them into /usr/lib/perl5/site_perl/DJabberd .
You apparently also need to recursively copy lib/DJabberd/SASL into /usr/lib/perl5/site_perl/DJabberd/ .
I really want GSSAPI, but DJabberd is fixated on Authen::SASL::Perl which does not have GSSAPI, whereas Authen::SASL::XS (or Cyrus) does. I'm deferring hacks to activate GSSAPI.
The SuSE package installs the promised sample server as /usr/share/doc/packages/perl-DJabberd/djabberd . It is identical to djabberd found in the root of the CPAN tarball.
/etc/djabberd/log.conf has logging configurations. I omitted this file, assuming I'll get default logging. This puts messages on stderr, in color.
/etc/djabberd/djabberd.conf is the main configuration file. It is described in Config.pod. I put together an initial attempt, but the final file is shown later.
Starting the sample server by hand:
There is an undocumented dependency on perl-Unicode-Stringprep (added to the dependency list above).
The syntax of /etc/djabberd/djabberd.conf resembles an Apache conf file, but unlike in Apache, the configured paths must not be quoted; quotes are taken as part of the filename. There is no assurance that boolean or integer values should or should not be quoted, but I'm going to remove all quotes.
Remember to stop the old XMPP server, if it's running, so the new one can open the sockets.
The VHost statement should name the domain, not the server host,
even though terminology usually refers to this as a host
.
The domain may have a SRV record pointing to a server with an
unrelated name and it will work.
With no authentication backend it will just hang when the client connects. perldoc -t DJabberd::Authen::LDAP reveals the configuration options for LDAP authentication. Can we get away with binding to localhost? Try this later.
Can't call method "vhost" on an undefined value at /usr/lib/perl5/vendor_perl/5.16.2/DJabberd/Authen/LDAP.pm line 151. The object for the connection, passwd to check_cleartext, was undef.
I'm supposed to be using SASL authentication, not LDAP. De-configure this and go through every known plugin and find its configuration options.
Configuring all the plugins:
DJabberd::SASL::AuthenSASL -- needs Mechanisms
list.
DJabberd::Authen::LDAP -- Requires LDAPURI, LDAPBaseDN and LDAPFilter, but I'm suppressing this Authen plugin in favor of SASL.
DJabberd::Delivery::Local -- No configuration.
DJabberd::PresenceChecker::Local -- No configuration.
So I have all the DJabberd configuration that I need.
Oh, crap, yesterday I was starting the server when $PWD = the CPAN sources. When I start it with $PWD = another directory, it behaves differently, because it *is* different. New strategy: toss the SuSE packages and use the CPAN stuff directly. Starting over…
On the first connection it eventually reads /usr/lib/perl5/vendor_perl/5.16.2/Authen/SASL/Perl/PLAIN.pm gets the UID and GID, sends nothing to the client (which it needs to do to start the SASL negotiation), then waits forever (epoll) on a file descriptor opened at global level, before the connection was made. This is the FD returned by epoll_create. The program adds FD 3 and 4 to the set using epoll_ctl; these FDs are sockets bound and listening to ports 5222 and 5269 resp. Later the client connection FD is added.
I'm not going to debug this. Conclusion: DJabberd is too poorly documented and too fragile, so I will not be able to get it to work in the time allowed.
Having had some problems with Tigase, possibly fixable, and having learned
some things while installing it, I decided to give DJabberd another try.
This time I put the CPAN package in /m1/DJabberd-0.85 with a symlink to there
from /usr/local/DJabberd, and a script in /usr/local/sbin that says basically
cd /usr/local/DJabberd; ./djabberd $*
. I did compile the program
but the compilation steps were basically cp -p lib/file.pl blib/lib/file.pl
.
And I ran the tests (successfully).
A proper LSB startup script will come later.
Starting the server by hand. It makes a TLS connection. It reports the requested cert, jfcarter.net. It commits to PLAIN auth, the client asks for a password, and everyone sits there without closing the connection. This user is in LDAP but perhaps is not known to SASL. Something I learned: the SASL module's purpose is to deal with the wire protocol; this may not be a backend client-type module.
Let's turn LDAP back on. Can't find DJabberd/Authen/LDAP.pm. It's on CPAN. It even has a spec file, which looks functional. Just drop it into the DJabberd directory. It depends on Net::LDAP which we have installed.
So which storage backend is turned on by default and where do the files go? InMemoryOnly.pm, how lameass! SQLite is available; see DJabberd::RosterStorage::SQLite. One config option: Database $filename. There's a warning that database access happens on the main thread, which has to be shared with message passing. For a big site, i.e. thousands of users active at once, the other backends, e.g. PostgreSQL, MySQL, etc., are smarter about not blocking the main thread.
Now it makes the TLS connection (jfcarter.net), commits to PLAIN auth.
Authen.pm calls DJabberd/Authen/LDAP.pm which calls undef->vhost().
This is a bug in DJabberd::SASL::Manager::AuthenSASL.pm and apparently
most Authen modules do not trigger it, but LDAP.pm does. Fixed. If your
version hasn't received the patch yet, in line 50 where it sets args
,
append , conn => $conn
within the brackets (comma separated list).
OK, I'm on. DJabberd lacks a plugin to respond to ping. It's on CPAN, DJabberd::Plugin::Ping
Jimc and Alice are both on but each shows the other as offline. Requesting buddy authorization, jimc goes first. And we get presence updates. Now both of us log off and the server is restarted. We're still buddies, and receive the appropriate presence updates. This is a very positive development; this is the step where Tigase failed to remember the buddy relation.
Here is /etc/djabberd/djabberd.conf that gave this successful outcome:
# /etc/djabberd/djabberd.conf # Created for CouchNet by jimc. Revision history: # 2013-08-01 jimc Trying again to make DJabberd work. # 2013-07-24 jimc Initial setup # According to the docs the SSL parameters belong in the global section, # and they refer to the hostname of the server that clients (in all # domains/VHosts) connect to. However, when Pidgin does TLSv3 it sends over # the domain as the cert it wants to see. These are the domain's credentials. SSLCertificateKeyFile /etc/ssl/private/jfcarter.key SSLCertificateFile /etc/ssl/hostcerts/jfcarter.crt # There is some ambiguity about what should be in the chain file. The # one specified starts with the private key, then the host cert, then # the intermediate cert, then the root cert. Docs say that you must also # specify SSLCertificateFile and SSLCertificateKeyFile even though they are # in the chain file also. SSLCertificateChainFile /etc/ssl/private/jfcarter.pem # ClientPort 5222 and ServerPort 5269 are the defaults. PidFile /var/run/djabberd.pid <VHost jfcarter.net> # Allows new users to register InBandReg on # Server to server communication, default is off S2S off # Require SSL before passwords are sent from the client RequireSSL true # How to get the client to give us a credential. Uses Authen::SASL::Perl <Plugin DJabberd::SASL::AuthenSASL> Optional yes # DIGEST-MD5 is also available Mechanisms PLAIN LOGIN </Plugin> # Backend auth: what to do with the Jabber ID and password? Uses Net::LDAP <Plugin DJabberd::Authen::LDAP> LDAPURI ldap://jacinth.cft.ca.us/ LDAPBaseDN ou=people,dc=cft,dc=ca,dc=us LDAPFilter (uid=%u) </Plugin> # Roster (buddy list) storage <Plugin DJabberd::RosterStorage::SQLite> Database /var/lib/xmpp/djabberd.sqlite </Plugin> # Handles Ping packets (XEP 0199) <Plugin DJabberd::Plugin::Ping /> # Private XML Storage (XEP 0049), often used by clients for settings # and buddy lists. <Plugin DJabberd::Plugin::PrivateStorage::DBI> Datasource SQLite:dbname=/var/lib/xmpp/private-0049.sqlite </Plugin> </VHost>
Now I'm going to housebreak DJabberd for production operation.
Figure out the format of the log configuration and make one up. This is /etc/djabberd/log.conf. There's a sample in the install dir at etc/log.conf.default. For the file format see perldoc -t Log::Log4perl (lots of info here) .
I'm putting logs in /var/log/xmpp (to be used by the IM server du jour) owned by xmpp:xmpp. Log file(s) will have an extension of .log. [Done.]
Here is my /etc/djabberd/log.conf:
# /etc/djabberd/log.conf -- Configuration for Log::Log4perl # Revision history: # 2013-08-02 jimc Initial setup # It puts all logs into /var/log/xmpp/djabberd.log owned by xmpp:xmpp. # /etc/logrotate.d/xmpp.J rotates this log file. # Modified from the defaults in ${INSTDIR}/etc/log.conf.default log4perl.logger.DJabberd = INFO, ToFile log4perl.logger.DJabberd.Hook = WARN # This psuedo class is used to control if raw XML is to be shown or not # at DEBUG it shows all raw traffic # at INFO it censors out the actual data log4perl.logger.DJabberd.Connection.XML = WARN # Don't want to hear about pings. log4perl.logger.DJabberd.Plugin.Ping = WARN log4perl.appender.ToFile=Log::Log4perl::Appender::File log4perl.appender.ToFile.filename=/var/log/xmpp/djabberd.log log4perl.appender.ToFile.mode=append # recreate means if the file is rotated, auto change to the new file, but # the check happens at most every 30 secs. log4perl.appender.ToFile.recreate=1 log4perl.appender.ToFile.layout=PatternLayout log4perl.appender.ToFile.layout.ConversionPattern=%d %m%n
And we need /etc/logrotate.d/xmpp. [Done.]
Created /etc/sysconfig/djabberd which contains directory names etc. that the startup script can use. This is how it's done in OpenSuSE. [Done.]
LSB startup script that runs it as non-root. I created a user and group xmpp:xmpp to use with all the instant message servers that I'm trying.
Change the ownership of the database. [Done.]
Add DJabberd to the list of scripts enabled at boot time. [Done.]
Check if it can communicate via IPv6, which is preferred on my home net. No, it is only listening on 0.0.0.0 (IPv4). I wonder if this can be hacked -- Perl has an IPv6 net module which is supposed to be a drop-in replacement.
Check if it can successfully do TLS to alternate interfaces of the multi-homed server host. No. It presents the jacinth.jfcarter.net certificate, and if you connect to the IP address or to jacinth.cft.ca.us (the internal LAN interface name), the client complains about an invalid certificate.
I'm guessing that if the client (Pidgin) specifies a connect
server, it expects a cert for that server. If it relies on a SRV
record or falls back to server == domain, it wants to see the domain in
the cert. (Because tcpdump showed that in TLSv3 it sent the domain
name in the initial packet.) I got a XMPP
cert from Startcom,
and the Common Name is the server's own name (jacinth.jfcarter.net),
but it has a SAN for jfcarter.net, so this is acceptable.
Can DJabberd set up TLS with both GnuTLS and Mozilla NSS (Network Security Services)? (I don't have a client that uses OpenSSL.) Yes, with either. Tigase could not connect if the client used GnuTLS. This was tested by chmod 000 /usr/lib64/purple-2/ssl-gnutls.so to prevent it from being used, and similarly for ssl-nss.so. openssl s_client with starttls on port 5222 hangs when connecting to DJabberd, same as to Tigase and Openfire. DJabberd does not listen on 5223 (deprecated).
Oinkage: After modest but typical use, DJabberd occupies 116Mb of virtual address space, and a resident set of 36Mb, of which 31Mb is private and 5Mb is shared, i.e. the Perl executable program and the shared libraries.
Here are some issues I want to investigate. But later.
Can we provide a vCard service (XEP 0054)? And Private XML Storage (XEP 0049)? There's got to be a plugin for both of these. The latter stores arbitrary XML formatted data and is often used by clients to store their settings and/or buddy lists. I got Private XML Storage working, but the plugin for the vCard (DJabberd::Plugin::VCard) doesn't use standard configuration options and I couldn't make it work.
See if the IPv6 network module can be used.
Investigate DJabberd::Authen::PAM to replace LDAP authentication.
If DJabberd::SASL::AuthenSASL could use Authen::SASL::XS (vs. Perl), could GSSAPI be done from within DJabberd?
Search for a true SASL backend and see if it can do GSSAPI.