[TUTORIAL] How to make your own private XMPP server federated over tor and i2p
Introduction
In this tutorial, I will show how you can make your own private XMPP server, where you will have full control of your user data, messages, groups, files, all of that federated over anonymizing networks such as tor and i2p.
Requirements
XMPP over i2p
I2pd tunnel configuration
To make your prosody server federate over i2p, you need to create the necessary tunnels.
-
Find where are your tunnels.conf file (see here for more info) or create one in your i2pd directory and add these lines to the file:
[prosody-s2s] type=server host=127.0.0.1 port=5269 inport=5269 inbound.quantity = 2 outbound.quantity = 2 keys=prosody.dat [prosody-c2s] type=server host=127.0.0.1 port=5222 inport=5222 inbound.quantity = 2 outbound.quantity = 2 keys=prosody.dat [prosody-s2s-muc] type=server host=127.0.0.1 port=5269 inport=5269 inbound.quantity = 2 outbound.quantity = 2 keys=prosodymuc.dat [prosody-c2s-muc] type=server host=127.0.0.1 port=5222 inport=5222 inbound.quantity = 2 outbound.quantity = 2 keys=prosodymuc.dat [prosody-http] type=server host=127.0.0.1 port=5280 inport=5280 keys=prosodyfile.dat [prosody-https] type=server host=127.0.0.1 port=5281 inport=5281 keys=prosodyfile.dat
We're creating 3 different b32 addresses, which the keys are stored in prosody.dat, prosodymuc.dat and prosodyfile.dat, I will explain each address.
[prosody-s2s] type=server host=127.0.0.1 port=5269 inport=5269 inbound.quantity = 2 outbound.quantity = 2 keys=prosody.dat [prosody-c2s] type=server host=127.0.0.1 port=5222 inport=5222 inbound.quantity = 2 outbound.quantity = 2 keys=prosody.dat
This first address is the main address, each user in the server will have this address in their JID, for example: user@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p.
The port 5269 will be used for server-to-server connections, this is where your server will be able to talk to other servers federated in i2p network.
The port 5222 will be used for client-to-server connections, this is where your xmpp client (in this tutorial, monocles chat) will talk to your server.
The inbound/outbound quantity are the amount of tunnels that this b32 address will have for receiving/sending data over i2p network, since this b32 address will not have a huge flow of data, I decided to configure less tunnels to avoid wasting i2p network resources.
[prosody-s2s-muc] type=server host=127.0.0.1 port=5269 inport=5269 inbound.quantity = 2 outbound.quantity = 2 keys=prosodymuc.dat [prosody-c2s-muc] type=server host=127.0.0.1 port=5222 inport=5222 inbound.quantity = 2 outbound.quantity = 2 keys=prosodymuc.dat
This second address is used for groups, also called MUC (Multi-User Chat), each group in your server will have this format: group@yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p.
The ports 5269 and 5222 do essentially the same as in the first address.
[prosody-http] type=server host=127.0.0.1 port=5280 inport=5280 keys=prosodyfile.dat [prosody-https] type=server host=127.0.0.1 port=5281 inport=5281 keys=prosodyfile.dat
This third address will be used for prosody file sharing server, every time a user uploads a file it will be sent to this server, and other people in the chat will be able to download the file.
The port 5280 is used for http, and the port 5281 is used for https.
- Run i2pd using this configuration.
- See the addresses generated in i2pd webconsole (http://127.0.0.1:7070/?page=i2p_tunnels). You will use these addresses in prosody configuration.
Prosody configuration
-
Find the directories your prosody server will use, just type this in the terminal:
prosodyctl about
-
Go the path of "Data directory" and make a new folder inside called "custom_plugins", then git clone the plugin needed to prosody work over i2p:
mkdir custom_plugins cd custom_plugins git clone https://github.com/majestrate/mod_darknet
-
Go to the path of "Config directory" shown in the first step and open the "prosody.cfg.lua" file.
You will find a line with this configuration:VirtualHost "localhost"
Change this line with:
VirtualHost "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p"
modules_enabled = { "darknet" };
darknet_only = true;
disco_items = {
{ "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p", "Public Chatrooms" };
{ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.b32.i2p", "Upload Files" };
}
Component "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p" "muc"
modules_enabled = { "darknet" };
darknet_only = true;
Component "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.b32.i2p" "http_file_share"
modules_enabled = { "darknet" };
darknet_only = true;
Replacing xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p with prosody-s2s/prosody-c2s b32 address you got in i2pd webconsole.
Replacing yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p with prosody-s2s-muc/prosody-c2s-muc b32 address you got in i2pd webconsole.
Replacing zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.b32.i2p with prosody-http/prosody-https b32 address you got in i2pd webconsole.
-
Generate certificates for each b32 address:
prosodyctl cert generate xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p prosodyctl cert generate yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p prosodyctl cert generate zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.b32.i2p
In each certificate generation it will ask some information, you can skip a field with "." in the input.
-
Move the certificate files from the "Data directory" to the folder "certs" in "Config directory":
cd path/to/data/ mv *.b32.i2p.* path/to/config/certs
-
Check everything with:
prosodyctl check
If you got "All checks passed, congratulations!", you are almost ready to go!
Adding your user
Create your user with:
prosodyctl adduser your_username_here@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p
It will ask for a password, remember to put a strong password!
Running prosody
You are ready! Just start the server:
prosodyctl start
XMPP over tor
Tor hidden service configuration
Making your prosody server federate over tor is similar to i2p (it is actually a bit easier!).
- Create a folder for your hidden service, in this example I will use a folder called "xmpp_hidden_service".
-
Create your hidden service, go to your torrc file and add these lines:
HiddenServiceDir /path/to/xmpp_hidden_service HiddenServicePort 5222 127.0.0.1:5222 HiddenServicePort 5269 127.0.0.1:5269 HiddenServicePort 5280 127.0.0.1:5280 HiddenServicePort 5281 127.0.0.1:5281
-
Run tor using this torrc file and check your hidden service folder, you will find a file called "hostname", this is your onion address, and it will look like this:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion
Each port used in the torrc config serves the same purposes as the ones in i2p config.
Prosody configuration
-
Install "mod_onions" plugin to be able to federate over tor.
prosodyctl install --server=https://modules.prosody.im/rocks/ mod_onions
-
Go to your prosody.cfg.lua file (remember you can find your config file typing "prosodyctl about") and add these lines after the i2p configuration:
VirtualHost "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" modules_enabled = { "onions" }; onions_only = true; disco_items = { { "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion", "Public Chatrooms" }; { "upload.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion", "Upload Files" }; } Component "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "muc" modules_enabled = { "onions" }; onions_only = true; Component "upload.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "http_file_share" modules_enabled = { "onions" }; onions_only = true;Replacing
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onionwith the onion address you got on "hostname" file from your hidden service folder. -
Generate the certificate for your onion address:
prosodyctl cert generate aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion
-
Move the certificate files from the "Data directory" to the folder "certs" in "Config directory":
cd path/to/data/ mv *.onion.* path/to/config/certs
-
Check everything with:
prosodyctl check
Adding your user
Create your user with:
prosodyctl adduser your_username_here@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion
Running prosody
You are ready! Just start the server:
prosodyctl start
Login to your user in monocles chat
I2p
- Turn on your i2pd router in your phone, you can use their app for this.
- Open monocles chat and go to the login page.
- Click "I have an account".
- Go to the config button ("⋮") and go to configurations, then select "Expert settings" and check "Connect via I2P".
-
Go back to login, put in "hostname" your main b32 address, and in "ID Jabber" put your JID, like this:
hostname: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p ID Jabber: user@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p
- Put your password and click "Next".
- Accept the certificate of your server and you are ready to go!
Tor
- Turn on tor, you can use orbot for this.
- Open monocles and go to login page.
- Click "I have an account".
- Go to the config button ("⋮") and go to configurations, then select "Expert settings" and check "Connect via Tor".
-
Go back to login, put in "hostname" your onion address, and in "ID Jabber" put your JID, like this:
hostname: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion ID Jabber: user@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion
- Put your password and click "Next".
- Accept the certificate of your server and you are ready to go!
Optional: other prosody configurations that you may want to change
Message archiving (mod_mam)
Uncomment mod_mam to make your server store messages.
change this line:
--"mam"; -- Store recent messages to allow multi-device synchronization
to this:
"mam"; -- Store recent messages to allow multi-device synchronization
Change this config to set the expiration time of your messages:
archive_expires_after = "1w" -- Remove archived messages after 1 week
You can set the value to any time you want, including "never" to never delete your messages (this is YOUR private server, if you have good amount of storage, go ahead, you have FULL CONTROL over your data! ;D).
Check more about mod_mam here.
Server limits (mod_limits)
Change the bandwidth limits by your choice in these lines:
limits = {
c2s = {
rate = "10kb/s";
};
s2sin = {
rate = "30kb/s";
};
}
Check more about mod_limits here.
Multi-User Chat archiving (mod_muc_mam)
Go to your MUC component and add mod_muc_mam to "modules_enabled":
tor:
Component "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "muc"
modules_enabled = { "onions", "muc_mam" };
onions_only = true;
i2p:
Component "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p" "muc"
modules_enabled = { "darknet", "muc_mam" };
darknet_only = true;
Like in mod_mam, you can set the expiration time of the messages in MUCs, for example:
tor:
Component "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "muc"
modules_enabled = { "onions", "muc_mam" };
onions_only = true;
muc_log_expires_after = "1w"
i2p:
Component "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p" "muc"
modules_enabled = { "darknet", "muc_mam" };
darknet_only = true;
muc_log_expires_after = "1w"
Check more about mod_muc_mam here.
Files archiving (mod_http_file_share)
Go to your http_file_share component and add the config lines you want, for example:
tor:
Component "upload.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "http_file_share"
modules_enabled = { "onions" };
onions_only = true;
http_file_share_daily_quota = 100*1024*1024; -- 100 MiB
http_file_share_expires_after = 7 * 86400; -- one week in seconds
http_file_share_size_limit = 10*1024*1024; -- 10 MiB
i2p:
Component "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.b32.i2p" "http_file_share"
modules_enabled = { "darknet" };
darknet_only = true;
http_file_share_daily_quota = 100*1024*1024; -- 100 MiB
http_file_share_expires_after = 7 * 86400; -- one week in seconds
http_file_share_size_limit = 10*1024*1024; -- 10 MiB
Check all the configurations of mod_http_file_share here.
Group photo (mod_vcard_muc)
-
Install the required plugin:
prosodyctl install --server=https://modules.prosody.im/rocks/ mod_vcard_muc
-
Add "mod_vcard_muc" to "modules_enabled" in your MUC component:
tor: Component "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "muc" modules_enabled = { "onions", "vcard_muc" }; onions_only = true; i2p: Component "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p" "muc" modules_enabled = { "darknet", "vcard_muc" }; darknet_only = true;
Message moderation in groups (mod_muc_moderation)
-
Install the required plugin:
prosodyctl install --server=https://modules.prosody.im/rocks/ mod_muc_moderation
-
Add "mod_muc_moderation" to "modules_enabled" in your MUC component:
tor: Component "conference.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion" "muc" modules_enabled = { "onions", "muc_moderation" }; onions_only = true; i2p: Component "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.b32.i2p" "muc" modules_enabled = { "darknet", "muc_moderation" }; darknet_only = true;
Optional: make your server public
Prosody is private by default to avoid abuse of spammers, meaning that with the current setup showed in this tutorial, the only user of your server will be you.
If you have a good hardware, you may want to run a public server for other people to join, or just want a server for your private community.
You have 2 ways to allow other people to register in your server.
-
Create a register invite for each participant you want to join your server.
First, add these 2 lines to your prosody.cfg.lua before the lines with "VirtualHost":
allow_registration = true registration_invite_only = true
Then, create a register invite in your terminal:
tor: prosodyctl mod_invites generate aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion i2p: prosodyctl mod_invites generate xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.b32.i2p
You will get something like:
xmpp:your_address?register;preauth=random_tokenCreate a QR Code with this invite and send to the person you want to register. For this method, the person needs to have a xmpp client installed that recognizes this invite (like Monocles chat).
The invite will work for only one account, so you will need to generate a invite for every person you want to register in your server. -
Allow ANYONE to register in your server (be careful with this option).
First, add these 2 lines to your prosody.cfg.lua before the lines with "VirtualHost":
allow_registration = true registration_invite_only = false
Anyone with a xmpp client will be able to register in your server, so you need to be careful with spammers.
Check more about register here.
Check more about invites here.
Check best practices for public servers here.
Pros and Cons of making your own XMPP server over tor/i2p (In my opinion)
Pros:
- Full control of your data.
- Anyone can make a server, since tor/i2p domains are free.
- Very lightweight server.
- Location of your server and user will be hidden.
- Having the benefits of a p2p messenger (like briar), without the problems of p2p messenger (like group synchronization problems).
Cons:
- You will only be able to talk with other people that has a similar setup inside tor/i2p. (Good reason to share this tutorial to more people :D)
- Not all XMPP clients will work with this setup, but if this setup gets more popular, hopefully this will change. (For example, Dino does not handle onion/i2p addresses, gajim works with DMs and MUCs, but fails with file download)
- Slow connection, mostly with sharing files. (In monocles chat, for example, I frequently got "timeout error" when trying to send videos, but maybe this can be fixed setting a higher timeout when using tor and i2p in their source code)
- You need some kind of knowledge to setup this, and a hardware running 24/7.
Test your setup!
If you were able to create your server successfully, I created a group in my server in tor and i2p, so people can join and test their setup.
Tor:
test@conference.fs5l6swbq4poj4w5otvl5qqrmhpk5xklhlibe4tksmd63mlievka6vid.onion
I2p:
test@sdmmzcx2b7t4rhqqa5qczjao2sqgys3hdlmamzth4vjozeafblea.b32.i2p