ADUFRAY

Setting up a simple Jabber service on Red Hat Enterprise Linux (or any of its derivatives) is very straight-forward. If you install the Extra Packages for Enterprise Linux (EPEL) repository, you’ll have access to jabberd2.

The Quick StartGuide for RPM documentation provided by the jabberd2 project is almost all you need to get it up and going.

# yum install jabberd
# chkconfig jabberd on

The documentation tells you to switch to MySQL, but this is unnecessary. The default configuration uses an SQLite database for user registration and session storage, so you can set that up instead. To do so, simply initialize the database:

# sqlite3 /var/lib/jabberd/db/sqlite.db < /usr/share/jabberd/db-setup.sqlite

Then all that’s left is setting up your ID entries in c2s.xml and sm.xml:

c2s.xml:
    <local>
      <id register-enable='mu' password-change='mu'>domain.com</id>
      ...
    </local>

sm.xml:
    <local>
      <id>domain.com</id>
      ...
    </local>

Now you’re all set:

# service jabberd start

This, of course, only sets up the most basic implementation. There are a few extra steps I took to have a slightly more secure setup. I also wanted to host multiple domains for friends to use. By default:

To fix the encrypted password issue, you have to use MySQL instead of SQLite. There’s an open bug on GitHub to implement this feature, but it’s still waiting for patches. It’s pretty easy to change to MySQL, though. The database setup script, /usr/share/jabberd/db-setup.mysql, wants to create the database for you, which can cause problems if you’ve already created one, so go ahead and comment out the first few lines:

-- CREATE DATABASE jabberd2;
-- USE jabberd2;

Then you can create the database, its user, and run the script:

# mysql -u root -p
> CREATE DATABASE jabber;
> GRANT ALL ON jabber.* to 'jabber'@'localhost' IDENTIFIED BY 'password';
> QUIT
# mysql -u jabber -p jabber < /usr/share/jabberd/db-setup.mysql

Then edit both c2s.xml and sm.xml again, changing the DB driver to use MySQL instead of SQLite:

c2s.xml:
    <authreg>
      ...
      <module>mysql</module>
      ...
      <mysql>
        <host>localhost</host>
        <port>3306</port>

        <dbname>jabber</dbname>

        <user>jabber</user>
        <pass>password</pass>

        <password_type>
          <crypt/>
        </password_type>
      </mysql>
      ...
    </authreg>

sm.xml:
    <storage>
      ...
      <driver>mysql</driver>
      ...
      <mysql>
        <host>localhost</host>
        <port>3306</port>

        <dbname>jabber</dbname>

        <user>jabber</user>
        <pass>password</pass>

        <transactions/>
      </mysql>
      ...
    </storage>

This gives us a strong database backend with passwords that are somewhat securely stored. The hashing method uses the crypt(3) function to generate a salted MD5 hash. Without digging into the source code, I can’t tell how many rounds of MD5 it is, but it’s sort of irrelevant: MD5 is broken and unsuited for password hashing. Still, better than plaintext — basically only keeps honest people honest. Moving on…

To set up TLS/SSL you have a decision to make: self-signed or legitimate certificate. I prefer legitimate because these days you can get SSL certificates very cheaply (or even free). It’s a bit easier to set up self-signed though:

# openssl genrsa -out ./jabber.key 2048
# openssl req -new -key ./jabber.key -x509 -sha1 -out ./jabber.crt
# cat jabber.crt jabber.key >> combined.crt
# rm jabber.crt jabber.key

jabberd2 requires that they key (and any intermediate certificates) be present in the PEM file. I don’t like this approach — the key should not be readable by anyone but root. Unfortunately the jabberd2 processes aren’t clever enough read in the key as root and then drop privileges — patches are welcome, I’m sure.

If you want to go the legitimate certificate route, create a key and certificate signing request:

# openssl genrsa -out ./jabber.key 2048
# openssl req -new -key ./jabber.key -sha1 -out ./jabber.csr

Send the CSR to your certificate authority. They should send you back a certificate and one or more intermediate / root certificates. Your combined certificate should look something like this:

# cat certificate.crt intermediate.crt root.crt key.key > combined.crt

To install the SSL certificate (either self-signed or legitimate), you need to edit the <id> tag in c2s.xml:

<local>
  <id register-enable='mu' pemfile='/path/to/combined.crt' require-starttls='mu' password-change='mu'>domain.com</id>
  ...
</local>

My server requires all client connections to be encrypted. If you want to support TLS, but don’t care whether or not the connections are encrypted, just remove the require-starttls parameter.

Next up is setting the IP address to listen on. This is a very quick edit to c2s.xml and s2s.xml:

<local>
  ...
  <ip>0.0.0.0</ip>
  ...
</local>

Adding an administrator is similarly easy. Just edit the aci section of sm.xml:

<aci>
  ...
  <acl type='all'>
    <jid>admin@domain.com</jid>
  <acl>
  ...
</aci>

Securing the inter-service communication is fairly easy, but a little cumbersome. You need to edit all the service configuration files (c2s.xml, s2s.xml, and sm.xml) as well as the router configuration (router.xml) and the router users file (router-users.xml).

For each service, create a user entry in router-users.xml:

<users>
  <user>
    <name>c2s</name>
    <secret>random_password</secret>
  </user>
  <user>
    <name>s2s</name>
    <secret>random_password</secret>
  </user>
  <user>
    <name>sm</name>
    <secret>random_password</secret>
  </user>
</users>

Then edit router.xml to give access to each of these users:

<aci>
  <acl type='all'>
    <user>c2s</user>
    <user>s2s</user>
    <user>sm</user>
  </acl>
  ...
</aci>

While you’re there, go ahead and enable SSL:

<local>
  ...
  <pemfile>/path/to/combined.crt</pemfile>
  ...
</local>

You could probably be more granular in the ACLs, but I don’t know enough about what permissions are needed for each process. Next, you have to edit each service’s configuration file and change the user & password settings — make sure you match the passwords correctly to what you defined in router-users.xml:

<router>
  ...
  <user>c2s OR s2s OR sm</user>
  <pass>corresponding password</pass>
  <pemfile>/path/to/combined.crt</pemfile>
  ...
</router>

Now all your services should be using a unique password to communicate. In addition, the traffic will be encrypted.

All that remains is to set up multiple domains. Searching around the net I couldn’t find very much documentation, but thankfully it’s very easy. In previous versions of jabberd2, you needed to run an sm process for each separate domain. That no longer seems to be the case. You just need to edit c2s.xml, sm.xml, and configure appropriate SRV records. In c2s.xml, just add another ID entry for the second domain:

<local>
  ...
  <id register-enable='mu' pemfile='/path/to/combined.crt' require-starttls='mu' password-change='mu'>domain1.com</id>
  <id register-enable='mu' pemfile='/path/to/combined.crt' require-starttls='mu' password-change='mu'>domain2.com</id>
  ...
</local>

Pretty much the same thing in sm.xml:

<local>
  <id>domain1.com</id>
  <id>domain2.com</id>
</local>

Then add the SRV records to your DNS:

_xmpp-client._tcp.domain1.com. IN    SRV 5    5 5222 domain1.com.
_xmpp-server._tcp.domain1.com. IN    SRV 5    5 5269 domain1.com.

Do the same thing for domain2.com, leaving domain1.com as the destination at the end:

_xmpp-client._tcp.domain2.com. IN    SRV 5    5 5222 domain1.com.
_xmpp-server._tcp.domain2.com. IN    SRV 5    5 5269 domain1.com.

This configuration will let you use Jabber IDs for either domain1.com or domain2.com. Just make sure in your client’s configuration you put in the correct value for the Connect Server — in this example, that would be domain1.com.

Once you’ve created your accounts using a Jabber client, you might consider going back to c2s.xml and removing the register-enable parameter from the id tag. Otherwise any Internet user can create accounts on your server.