http:// www.jms1.net / jabberd2 /

Installing and Configuring jabberd2

I recently upgraded my jabberd server from version 1 to version 2. If you haven't looked at the two versions, it's a fairly major upgrade. The structure of the programs is totally different- the older version runs as modules within a single process, where the new version runs as four separate processes plus an extra for each "realm" (domain name) you are hosting on the server. I currently have three domains, which means that I have a total of seven processes.

I ran into a few problems in getting it set up:

The following is a cleaned-up version of how I got it working on my own server. It does have my own domain name as the first domain on the jabber server, but the other two have been replaced with dummy values.


Finding the software

The official home of jabberd2 is http://jabberd.jabberstudio.org/. This is where to find the official documentation, and is where you should download the source code.

When I originally wrote this page, the jabberstudio web server had been cracked and all downloadable files had been taken off-line until they could be verified against the developers' original files. I was offering a copy of the jabberd2 source code from my own server, but since the files on the jabberstudio server have been verified (and the version I was offering is now out of date) I have removed the file from my server. Please download the software from the official jabberd2 site (using the link above.)

Also... and I cannot stress this strongly enough. PLEASE read through the official documentation and use my web site only to "fill in the blanks" where you may need help.


Compiling and installing the software

Before compiling the software, you need to create a userid for the jabberd2 server to run as. Most systems use "jabber" for this purpose, I see no problem with adopting the same standard.

(as root)
# groupadd jabber
# useradd -g jabber jabber

You will probably also need to install a few other libraries first. These libraries should be available through your system's normal update mechanism. In my case, running Whitebox Enterprise Linux, I was able to install most of them using a yum install command, but I had to get the source RPM for one of them (the libidn library) from a Fedora Core 3 repository, build my own binary RPM files, and install from those.

Because there are so many types of systems out there, I cannot hope to include complete directions for every system. The official documentation has links to the web sites of the packages involved, as well as detailed instructions for the OpenSSL library.

On my server I am using MySQL for both authentication and data storage. The actual instructions were...

(as root)
# yum install openssl-devel mysql-devel
...
# rpmbuild -ta libidn-0.5.12.tar.gz
...
# rpm -ivh /usr/src/redhat/RPMS/i386/libidn-*
...

Once the pre-requisite packages are installed, the next step is to compile the software. Again, please see the official documentation for detailed instructions. Below is a list of the commands I used on my own server.

Note that I create a shell script which runs the ./configure command. This is for two reasons:

  1. It saves a copy of exactly how the source code was configured, in case it ever becomes an issue in the future.
  2. It's a lot easier to check for errors when you can see the commands in a text editor beforehand.

(as a non-root user)
% tar xvzf jabberd-2.0s6.tar.gz
...
% cd jabberd-2.0s6
% nano go

The contents of "go" are...
#!/bin/sh
export CFLAGS="-I/usr/kerberos/include -I/usr/include/mysql -L/usr/lib/mysql"
./configure \ --enable-ssl \ --enable-idn \ --enable-mysql \ --enable-debug

% chmod 700 go
% ./go
...
% make
...
% su -
Password:
# make install
...
# chown -R root:jabber /usr/local/etc/jabber
# chmod -R g=u-w,o= /usr/local/etc/jabber
# exit
%

At this point, the programs which make up the server are installed and need to be configured for your system.


Initial setup

Again, the official documentation details how to do the basic configuration and testing for the server.

The first step for me was to set the host name in sm.xml and c2s.xml. I chose one of the three domain names and set it up exactly as shown in the documentation.

The next step for me was to set up the MySQL database. Again, the documentation did a good job of showing how it's done.

(as a non-root user)
% cd .../jabberd2-2.0s6/tools
% mysql -u root -p
Enter password:
...
mysql> \. db-setup.mysql
...
mysql> GRANT select,insert,delete,update ON jabberd2.*
    -> to jabberd2@localhost IDENTIFIED BY 'make-up-a-password';
mysql> \q

On my system I did have to create the symbolic link so that /tmp/mysql.sock pointed to the right place.

(as root)
# ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock

After setting up the MySQL database, I set up sm.xml to use MySQL as a storage mechanism, and then set up c2s.xml to use MySQL as the authentication source, both as shown in the documentation.

I then tested the server to make sure it worked correctly, at least for the one domain I had already set up. It didn't work correctly the first few times- it turned out I had made a few typos when editing the config files.

Also, the testing ended up taking a lot longer than it normally would because I missed one little thing... the documentation mentions this, but in my opinion does not highlight it strongly enough:

If jabberd does not start, make sure that any previous instances have stopped.

The problem I kept running into was this: I tried to start the server, and the first few processes started correctly but then another process failed to start because of a typo in the config file. When I fixed the typo and tried to start again, the first process failed because another instance of that process was already running.

Remember that jabberd2 actually consists of five or more processes, and whenever you start it using the /usr/local/bin/jabberd script, ALL of the existing processes must have stopped. Once I realized this, I was able to start the server up correctly every time.


Configure for logging through daemontools

When daemontools runs a service, it expects the program to send any logging output to the "standard out" channel. Many programs can be configured to send their logging output to "standard error", therefore it is common to see the expression "2>&1" in the "run" script for a service.

The jabberd programs can be configured to send their log output directly to the "standard out" channel, therefore the "2>&1" syntax is not really needed (although I have added it to the "run" scripts below, it doesn't hurt anything and just in case something does happen to be sent to "standard error", it will end up in the log so you can find it.

To configure each program to send its log output to "standard out", add this line to the .xml file which controls the service (using the "s2s" service as an example here...)

<s2s>
  ...
  <log type='stdout' />
  ...
</s2s>


Setting up extra realms (domains)

The next step is to make it work with more than one domain. The documention mentions in passing that this is possible, but it does not really explain how to make it work. This shortcoming is the primary reason I'm writing this web page.

The key to using multiple domains is to realize that each "sm" (session manager) process is responsible for managing sessions for one specific domain name. If your server will be handling three domain names, you will need three "sm" processes.

Each "sm" process will need its own configuration file. You already have a good config file for the first domain, and you can copy this for each additional domain, provided you change two things at the beginning of the file: the <id> and <pidfile> values must be unique for all jabberd2 processes.

What I did is the following:

(as root)
# cd /usr/local/etc/jabber

# cp sm.xml sm-jms1.net.xml
# chown root:jabber sm-jms1.net.xml
# chmod 640 sm-jms1.net.xml
# nano sm-jms1.net.xml

Changed the <id> tag from localhost to jms1.net
Changed the <pidfile> tag from .../sm.pid to .../sm-jms1.net.pid


# cp sm.xml sm-domain1.com.xml
# chown root:jabber sm-domain1.com.xml
# chmod 640 sm-domain1.com.xml
# nano sm-domain1.com.xml

Changed the <id> tag from localhost to domain1.com
Changed the <pidfile> tag from .../sm.pid to .../sm-domain1.com.pid


# cp sm.xml sm-domain2.org.xml
# chown root:jabber sm-domain2.org.xml
# chmod 640 sm-domain2.org.xml
# nano sm-domain2.org.xml

Changed the <id> tag from localhost to domain2.org
Changed the <pidfile> tag from .../sm.pid to .../sm-domain2.org.pid

After changing the files, you have to tell the c2s process that the other domains exist, otherwise it won't allow connections to them.

(as root)
# nano c2s.xml

Find this block (about 65 lines from the top, if your c2s.xml file hasn't been extensively modified):

<!-- Local network configuration --> <local> <!-- Who we identify ourselves as. This should correspond to the ID (host) that the session manager thinks it is. You can specify more than one to support virtual hosts, as long as you have additional session manager instances on the network to handle those hosts. The realm attribute specifies the auth/reg or SASL authentication realm for the host. If the attribute is not specified, the realm will be selected by the SASL mechanism, or will be the same as the ID itself. Be aware that users are assigned to a realm, not a host, so two hosts in the same realm will have the same users. If no realm is specified, it will be set to be the same as the ID. --> <id>localhost</id> <!-- <id realm='company'>localhost</id> -->
Replace the <id>localhost</id> line with the following lines:

<id realm='jms1.net'>jms1.net</id> <id realm='domain1.com'>domain1.com</id> <id realm='domain2.org'>domain2.org</id>

The last step is to make sure that the /usr/local/bin/jabberd script knows to start the new sm processes.

# nano jabberd.cfg

Find the existing line which starts with "sm", remove it, and replace it with the following:

sm /usr/local/etc/jabberd/sm-jms1.net.xml sm /usr/local/etc/jabberd/sm-domain1.com.xml sm /usr/local/etc/jabberd/sm-domain2.org.xml

You should now be able to run /usr/local/bin/jabber as the jabber user without any error messages. While it's running you should be able to do ps axww (or ps -elf, depending on your system) and see three copies of /usr/local/bin/sm running. You should also be able to log into all three of the jabber domains.


Setting up daemontools files

My preference is to use daemontools to manage services on my machine- primarily because if a service stops, daemontools will automatically start it back up within five seconds. If you are not already familiar with daemontools, or do not wish to use it for your jabber server, you should refer to the documentation to set up the automatic startup and shutdown using RC scripts.

Otherwise, the documentation also includes instructions on how to use daemontools to start and stop the jabberd2 server. I am including sample "run" and "log/run" scripts here for your convenience.


Setting up SRV records in your DNS data

The "proper" way for a client (or another server) to find your jabber server is using SRV records defined in your DNS data. If the SRV records don't exist, it will fall back to using A records. However, if you own a domain but your jabber server is on a different machine from your web site, you need to set up SRV records so your clients and other jabber servers can find you.

SRV records (defined in RFC 2052) exist as a way to allow individual services to be delagated to not just a specific machine, but a specific port number on a machine. Most service have "assigned" port numbers- 80 for HTTP, 110 for POP3, and so forth. However, if the protocol is designed to use SRV records to locate a specific server, you can run the service on any port you like, and clients will still be able to find you. So if you want to run your jabber server with non-standard port numbers, you can put your custom port numbers into your SRV records, and your clients and other servers would be able to find the jabber server without any problems.

There are three SRV records which should exist for any jabber server:

_jabber._tcp.domain.xyz      -> host.domain:5269
_xmpp-server._tcp.domain.xyz -> host.domain:5269
_xmpp-client._tcp.domain.xyz -> host.domain:5222

The first two records are used by other jabber servers to find your server for server-to-server (s2s) connections. The third one is used by jabber clients (usually a "chat program" on somebody's desktop) for client-to-server (c2s) connections.

SRV records have two other numbers associated with them- a "priority" and a "weight".

Both of these values are unsigned 16-bit numbers, which means their ranges are 0-65535. For my single server, I just use zero for both values.

In addition, there are two TCP port numbers which are normally used by a jabber server.

In addition, the original jabber protocol supports an SSL-encrypted version of the C2S protocol, which is expected to be one number higher than the normal C2S port- so if your jabber server's C2S port is 5222, then the SSL-encrypted C2S port would be 5223.

Creating SRV records in tinydns

tinydns does not have a built-in record type for SRV records, however you can use the ":" record type to create a record by specifying the raw binary data for an SRV record. (This is documented on djb's web site.) In order to do this, you need to know the numeric record type you wish to create (which is 33 for SRV records), and the binary data to be returned.

This is an example of what the three required SRV records look like, in tinydns format:

:_jabber._tcp.domain.xyz:33:\000\000\000\000\024\225\006domain\003xyz\000
:_xmpp-server._tcp.domain.xyz:33:\000\000\000\000\024\225\006domain\003xyz\000
:_xmpp-client._tcp.domain.xyz:33:\000\000\000\000\024\146\006domain\003xyz\000

The "\nnn" notation specifies a byte, with "nnn" being the value in octal (base-eight) notation. The first "\000\000" (with the red background) is the priority value, the second "\000\000" (with the green background) is the weight value, and the third value (with the blue background) is the port number. The two bytes for each value are specified in "network order", meaning they are in order from "most significant" to "least significant".

The hostname providing the service (after the port number) is specified in "label sequence" format, as defined in RFC 1035 section 4.1.2. Each "label" consists of a single byte telling the length of the label, followed by the label itself- so the label "\003xyz" has "\003" (a byte with the value 3) followed by the three bytes "xyz". The "label sequence" ends with a label whose length is zero.

And if we do the math, we find that the port number for the last line in this example, "\024\146", is 5222 decimal:

Octal Binary Decimal 024 -> 00 010 100 -> 20 146 -> 01 100 110 -> 102 256*20 + 102 = 5120+102 = 5222

Creating SRV records in BIND

It's actually easier to create SRV records under BIND, because it knows how to handle SRV records on its own, without having to resort to building the raw binary data yourself. The records look like this:

_jabber._tcp.domain.xyz.        IN SRV   0 0 5269   jabber.domain.xyz.
_xmpp-server._tcp.domain.xyz.   IN SRV   0 0 5269   jabber.domain.xyz.
_xmpp-client._tcp.domain.xyz.   IN SRV   0 0 5222   jabber.domain.xyz.

As in the tinydns example, the number with the red background is the priority, the green background is the weight, and the blue background is the port number.

And as usual when dealing with BIND, don't forget the "." at the end of any names, make sure to increment the serial number in the SOA record, and run "ndc reload" to make the new records active.


SRV Record Generator

When I first set up my Jabber server, there was a tinydns Record Maker written by Rob Mayoff which generated the records automatically. However, when I looked again just now (2007-08-31), it didn't seem to be working- I got a page full of un-rendered PHP and HTML script instead of the web page I had seen and used previously.

So I wrote an AJAX-based tinydns SRV record generator of my own. You can enter the necessary data below, and the "Generate" button will give you the actual lines which may be copied and pasted into your tinydns "data" file. If you're interested in how it works, the AJAX logic is embedded into the source code of this page, and the server-side piece can be downloaded from this link.

Domain
This will be to the right of the "@" sign in the Jabber ID's of the people using the server.
Priority Range 0-65535, default 0
Weight Range 0-65535, default 0
C2S Port Range 0-65535, default 5222
S2S Port Range 0-65535, default 5269
Hostname
This is the hostname of the machine running the Jabber server. It should exist as an A record.

Results:

(Nothing entered yet.)

After writing this, I decided it might make more sense to also write a pure javascript version of the record generator. I am not a Javascript expert, I think the fact that it works at all is pretty remarkable. The pure javascript page generates both tinydns and BIND data, where the AJAX version above only generates tinydns data.