XMPP over Tor

For a while I thought of setting a single-user XMPP server to work over Tor as an ad hoc distributed IM, as mentioned in the distributed systems note, and finally got to it now. A private server with federation is nice, but a distributed network is more reliable – yet apparently we're not getting any popular/working/usable distributed IMs anytime soon, so hacking the existing bits together is about as good as it gets. Somewhat related is XEP-0174: Serverless Messaging, but that requires client support, DNS access, and possibility of direct connections.

Usually I use jabberd2, but Prosody looks nice (even though I'm not that fond of lua), and has facilities for Tor networking already – so decided to use it. And going to write the instructions here; some of these are CentOS-specific. Tested with bitlbee and Pidgin, aiming remote connections to be s2s and over Tor only; Tor is used primarily to get routing here.

First of all, install Prosody:

sudo yum install prosody

Define a "hidden service" in /etc/tor/torrc (assuming Tor is already installed; yum install tor if not):

HiddenServiceDir /var/lib/tor/prosody/
HiddenServicePort 5269

Check the hostname in /var/lib/tor/prosody/hostname, it was dfei2rttaxykysmc.onion in my case. There is software to generate vanity .onion names if one wants to.

Adding a record into /etc/hosts may be helpful for clients, so I did that too:

sudo tee -a /etc/hosts <<< ' dfei2rttaxykysmc.onion'

Adding a user with Prosody is easier than with some of the other XMPP servers (and actually there's a few nice bits here and there):

sudo prosodyctl adduser defanor@dfei2rttaxykysmc.onion

We'll need mod_onions, so installing modules (the directory I've picked is like the one found with grep ^CFG_PLUGINDIR $(which prosody), but in /usr/local/):

sudo mkdir /usr/local/lib64/prosody/
sudo hg clone https://hg.prosody.im/prosody-modules/ \

Some clients would want encryption, and probably some servers will require it, so it's safer to just set a self-signed certificate – even though it's rather useless (alternatively, one may simply disable tls in the Prosody configuration). Storing them where Prosody will find them (it's a nice feature, by the way, and there's also Let's Encrypt support in Prosody – though the latter is not relevant in this case):

sudo certtool --generate-privkey --ecc \
  --outfile /etc/pki/prosody/dfei2rttaxykysmc.onion.key
sudo chown root:prosody /etc/pki/prosody/dfei2rttaxykysmc.onion.key
sudo chmod g+r /etc/pki/prosody/dfei2rttaxykysmc.onion.key
sudo certtool --generate-self-signed \
  --load-privkey /etc/pki/prosody/dfei2rttaxykysmc.onion.key \
  --outfile /etc/pki/prosody/dfei2rttaxykysmc.onion.crt

Some clients would care for a certificate to be signed by a CA or trusted, with no way to disable the verification, and using one of the many ways to validate it. It's a mess, but here's a couple of options:

sudo cp /etc/pki/prosody/dfei2rttaxykysmc.onion.crt \
sudo update-ca-trust extract
sudo cp /etc/pki/prosody/dfei2rttaxykysmc.onion.crt \
sudo chown $USER ~/.purple/certificates/x509/tls_peers/dfei2rttaxykysmc.onion

And the configuration (a patch, produced with sudo diff -u /etc/prosody/prosody.cfg.lua{,.orig}):

--- /etc/prosody/prosody.cfg.lua.orig	2018-01-07 05:56:32.636295975 +0300
+++ /etc/prosody/prosody.cfg.lua	2018-01-07 08:22:05.176986202 +0300
@@ -16,11 +16,13 @@
 -- Settings in this section apply to the whole server and are the default settings
 -- for any virtual hosts
+interfaces = { "" }
 -- This is a (by default, empty) list of accounts that are admins
 -- for the server. Note that you must create the accounts separately
 -- (see https://prosody.im/doc/creating_accounts for info)
 -- Example: admins = { "user1@example.com", "user2@example.net" }
-admins = { }
+admins = { "defanor@dfei2rttaxykysmc.onion" }
 -- Enable use of libevent for better performance under high load
 -- For more information see: https://prosody.im/doc/libevent
@@ -29,7 +31,7 @@
 -- Prosody will always look in its source directory for modules, but
 -- this option allows you to specify additional locations where Prosody
 -- will look for modules first. For community modules, see https://modules.prosody.im/
---plugin_paths = {}
+plugin_paths = { "/usr/local/lib64/prosody/modules/" }
 -- This is the list of modules Prosody will load on startup.
 -- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
@@ -101,7 +103,9 @@
 -- prevent servers from authenticating unless they are using encryption.
 -- Note that this is different from authentication
-s2s_require_encryption = true
+-- Tor provides encryption anyway.
+s2s_require_encryption = false
 -- Force certificate authentication for server-to-server connections?
@@ -192,6 +196,11 @@
 -- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
 -- Settings under each VirtualHost entry apply *only* to that host.
+VirtualHost "dfei2rttaxykysmc.onion"
+        modules_enabled = { "onions" }
+        onions_only = true
+        onions_tor_all = true
 --VirtualHost "example.com"
 --	certificate = "/path/to/example.crt"

Finally, run it and check that everything is well:

sudo systemctl start prosody
sudo less /var/log/prosody/prosody.log

And set bitlbee to use it (though no need to set a server if it's already set in hosts, or perhaps a proxy can be set instead):

account add jabber defanor@dfei2rttaxykysmc.onion <password>
account 1 set server localhost
account 1 on