Ejabberd
This document is still a work in progress!
Ejabberd is a high performance XMPP server easily scalable to large public servers, but not too heavy for self-hosting. It can be installed from Linux distribution repositories relatively easily and the default configuration is suitable for a reasonably well working XMPP server. However, these default settings tend to be quite conservative and if you want to have better support for modern XMPP features it requires some tweaking. The official documentation for all the Ejabberd module settings can be found here.
Tutorial
Prerequisites:
- A domain name
- A server
This tutorial begins with a single virtualhost1. For example purposes, example.net
is the domain. Replace all instances of example.net
with your own domain.
Step 1: Set up the DNS records
DNS A/AAAA records and SRV records are required for each service on the XMPP server.
A records:
example.net
muc.example.net
(for group chats)upload.example.net
(for HTTP file upload)pubsub.example.net
(for the pubsub node)proxy.example.net
(for file transfer proxy)turn.example.net
(for STUN/TURN)
Each pointing to the IP address of the server that is going to run Ejabberd.
Create SRV records for each, pointing to a domain that resolves to the server, as so:
(All records are in the form
_service._proto.name IN SRV priority weight port target
)
_xmpp-client._tcp IN SRV 5 0 5222 example.net.
_xmpps-client._tcp IN SRV 5 0 5223 example.net.
_xmpp-server._tcp IN SRV 5 0 5269 example.net.
_xmpps-server._tcp IN SRV 5 0 5270 example.net.
and
_xmpp-client._tcp.muc IN SRV 5 0 5222 example.net.
_xmpps-client._tcp.muc IN SRV 5 0 5223 example.net.
_xmpp-server._tcp.muc IN SRV 5 0 5269 example.net.
_xmpps-server._tcp.muc IN SRV 5 0 5270 example.net.
for each of the subdomains (starting with muc
). Exclude turn.example.net
.
Lastly, add one set of SRV records, for STUN/TURN.
_stun._udp IN SRV 5 0 3478 turn.example.net.
_stun._tcp IN SRV 5 0 3478 turn.example.net.
_stuns._tcp IN SRV 5 0 5349 turn.example.net.
_turn._udp IN SRV 5 0 3478 turn.example.net.
_turn._tcp IN SRV 5 0 3478 turn.example.net.
_turns._tcp IN SRV 5 0 5349 turn.example.net.
Extra info: as a result of these SRV delegation records, hosting XMPP on a server other than the one at example.net
is an option (i.e. if splitting services on one domain across servers). Further info can be found at XEP-0368.
Step 2: Open the firewall ports
Open ports:
TCP: 80
443
5222
5223
5269
5270
3478
5349
49152-65535
UDP: 3478
49152-65535
80
& 443
are for the web server, 5222
, 5223
, 5269
and 5270
are for XMPP, and the rest are for STUN/TURN.
Step 3: Set up the web server and get your SSL certificates
-
get (an) SSL certificate(s) for your domain, as well as several subdomains, all in all:
example.net
muc.example.net
upload.example.net
pubsub.example.net
proxy.example.net
turn.example.net
-
proxypass http://127.0.0.1:5443 through to:
- https://example.net/xmpp
- https://example.net/.well-known/host-meta
- https://example.net/.well-known/host-meta.json
Make sure that for /xmpp you have what is necessary to proxy websockets too. If you're using Nginx, increase
client_max_body_size
for HTTP uploads. -
make sure the certificate files are readable and/or in a place that is readable by the
ejabberd
user.
To avoid using something like Nginx + Certbot, use the built-in Ejabberd acme module, but this article assumes it is desired to host other web services on the same system, in which case each HTTP service would be reverse proxied to a single HTTPS web service.
Step 4: Install Ejabberd
Finally install the system package. Ensure the build has PostgreSQL support.
Confirm the file /etc/ejabberd/ejabberd.yml
exists, and is readable by the user that runs Ejabberd (almost definitely ejabberd
), by, if necessary, copying over the example ejabberd.yml
or wget
/curl
ing it from the github repo. If it is obtained from the repo, make sure the version corresponds to the version of Ejabberd packaged by your OS.
Step 5: Set up PostgreSQL database
A separate database should be created for each virtualhost, as this makes things clearer, and in addition, easier to migrate individual virtualhosts in the future. However, the ability to use only one, as described here, now exists as well.
Follow standard PostgreSQL installation instructions for your OS. Once this has been done, connect to the database as an admin and:
- create an Ejabberd database user with
CREATE USER ejabberd WITH PASSWORD 'your_password';
. Don't forget to change the password, and note it down. - create a database for the virtualhost with
CREATE DATABASE ejabberd_example OWNER ejabberd;
. Replaceexample
with something corresponding to the virtualhost. - quit the
psql
shell, and import the database schema from GitHub with the commandcurl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | sudo -u ejabberd psql ejabberd_example
(once again replaceexample
).
Step 6: Ejabberd configuration
Begin by replacing localhost
under hosts
with the virtualhost (e.g. example.net
), then list the certfiles previously obtained under certfiles
.
hosts:
- example.net
certfiles:
- "/etc/ejabberd/certs/*/*"
Now set default_db: sql
at the root level of the YAML file. This should be followed by host_config
and the database configuration for your virtualhost, as shown below. Customise each value to according to the setup.
host_config:
example.net:
sql_type: pgsql
sql_server: "localhost"
sql_port: 5432
sql_username: "ejabberd"
sql_password: "postgres_password"
sql_database: "ejabberd_example"
auth_method: sql
auth_password_format: scram
Under listen
, ensure all the correct services are enabled on each port, including s2s TLS on port 5270
(not default):
listen:
-
port: 5222
module: ejabberd_c2s
max_stanza_size: 262144
shaper: c2s_shaper
access: c2s
starttls_required: true
-
port: 5223
tls: true
module: ejabberd_c2s
max_stanza_size: 262144
shaper: c2s_shaper
access: c2s
starttls_required: true
-
port: 5269
module: ejabberd_s2s_in
max_stanza_size: 524288
-
port: 5270
tls: true
module: ejabberd_s2s_in
max_stanza_size: 524288
Next, enable the HTTP server and the STUN/TURN server modules. Set turn_ipv4_address
and ip
to the server's IPv4 address. TLS will be off for the HTTP server as it is reverse proxied through the previously set-up web server.
-
port: 5443
module: ejabberd_http
request_handlers:
/xmpp/admin: ejabberd_web_admin
/xmpp/bosh: mod_bosh
/xmpp/upload: mod_http_upload
/xmpp/ws: ejabberd_http_ws
/.well-known/host-meta: mod_host_meta
/.well-known/host-meta.json: mod_host_meta
-
port: 3478
transport: udp
module: ejabberd_stun
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
# The server's public IPv4 address:
turn_ipv4_address: 0.0.0.0
-
port: 5349
transport: tcp
module: ejabberd_stun
use_turn: true
tls: true
turn_min_port: 49152
turn_max_port: 65535
ip: 0.0.0.0
turn_ipv4_address: 0.0.0.0
Set s2s_use_starttls: required
at the root.
At this point it is possible to set up some ACLs. acls
are just the access control lists, set up access_rules
corresponding to your needs, which will be what are passed to module settings. At the minimum an admin user is recommended. Example:
acl:
admin:
user: juliet@example.net
capulet:
- user: juliet@example.net
- user: tybalt@example.net
nurse:
- user: angelica@example.net
access_rules:
household:
allow: capulet
allow: nurse
Modules
Add abuse addresses under mod_disco
. It is also possible to add other contact addresses according to XEP-0157:
modules:
# ...
mod_disco:
server_info:
-
modules: all
name: "abuse-addresses"
urls: ["mailto:abuse@example.net"]
Add mod_host_meta
:
mod_host_meta:
bosh_service_url: "https://@HOST@/xmpp/bosh"
websocket_url: "wss://@HOST@/xmpp/ws"
Edit mod_mam
, and change assume_mam_usage
to false
and default
to never
if it is not desireable to default to archiving messages on the server:
mod_mam:
db_type: sql
assume_mam_usage: never
default: never
Add mod_stun_disco
to advertise the STUN service to clients, changing 0.0.0.0
and example.net
to the server's IP and hostname respectively:
mod_stun_disco:
credentials_lifetime: 12h
services:
-
host: 0.0.0.0
port: 3478
type: stun
transport: udp
restricted: false
-
host: 0.0.0.0
port: 3478
type: turn
transport: udp
restricted: true
-
host: turn.example.net
port: 5349
type: stuns
transport: tcp
restricted: false
-
host: turn.example.net
port: 5349
type: turns
transport: tcp
restricted: true
MUCs:
Set the host to the muc
subdomain, otherwise it will attempt to use conference.example.net
. Setting mam: false
in default_room_options
will disable server-side message archiving by default.
mod_muc:
host: muc.example.net
access:
- allow
access_admin:
- allow: admin
access_create: muc_create
access_persistent: muc_create
access_mam:
- allow
default_room_options:
mam: false
File proxy:
mod_proxy65:
access: local
max_connections: 5
HTTP file upload:
mod_http_upload:
put_url: https://@HOST@/xmpp/upload
docroot: /var/www/ejabberdupload
max_size: 1073741824
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
"Access-Control-Allow-Headers": "Content-Type"
Create the folder for the docroot
, and ensure it is owned by the ejabberd
user. Change max_size
(the max upload size) to whatever is preferred.
PubSub:
mod_pubsub:
access_createnode: pubsub_createnode
plugins:
- flat
- pep
force_node_config:
## Avoid buggy clients to make their bookmarks public
storage:bookmarks:
access_model: whitelist
Step 7: Start server and create admin user
start the Ejabberd server!
Use sudo -u ejabberd ejabberdctl register admin example.net password
to register admin@example.net
with the password password
.
There is a compliance tester at compliance.conversations.im to test the server. After everything has been set up correctly, optionally change the loglevel
at the root of the config.
There will be an admin page accessible at https://example.net/xmpp/admin.
Extra goodies!
Web client
It is possible to set up conversejs using mod_conversejs
. It may be required to update the web server config to proxy the new endpoint (/chat
below).
listen:
-
port: 5443
module: ejabberd_http
request_handlers:
/xmpp/bosh: mod_bosh
/xmpp/ws: ejabberd_http_ws
/chat: mod_conversejs
modules:
mod_conversejs:
websocket_url: "ws://@HOST@/xmpp/ws"
bosh_service_url: "https://@HOST@/xmpp/bosh"
Further virtualhosts
For each further virtualhost a new database should be created, and added to the database part of the config. e.g.:
host_config:
example.net:
sql_type: pgsql
sql_server: "localhost"
sql_port: 5432
sql_username: "ejabberd"
sql_password: "postgres_password"
sql_database: "ejabberd_net"
auth_method: sql
auth_password_format: scram
example.org:
sql_type: pgsql
sql_server: "localhost"
sql_port: 5432
sql_username: "ejabberd"
sql_password: "postgres_password"
sql_database: "ejabberd_org"
auth_method: sql
auth_password_format: scram
There cannot be conflicts between declarations in the config file, so if mod_muc
, mod_proxy65
, mod_http_upload
and mod_pubsub
are declared under modules
at the root, they (as well as other configuration differences between virtualhosts) must be deleted and replicated for each virtualhost under append_host_config
, at the root. Example as so:
append_host_config:
example.org:
modules:
mod_muc:
host: muc.example.org
access_create: org_users
access_persistent: org_users
access:
- allow
access_admin:
- allow: admin
default_room_options:
mam: false
mod_proxy65:
access: org_users
max_connections: 5
mod_http_upload:
access: org_users
put_url: https://@HOST@/xmpp/upload
docroot: /var/www/ejabberdupload
max_size: 1073741824
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
"Access-Control-Allow-Headers": "Content-Type"
mod_pubsub:
access_createnode: org_users
plugins:
- flat
- pep
force_node_config:
## Avoid buggy clients to make their bookmarks public
storage:bookmarks:
access_model: whitelist
example.net:
modules:
mod_muc:
hosts:
- muc.example.net
access_create: net_users
access_persistent: net_users
access:
- allow
access_admin:
- allow: admin
default_room_options:
mam: false
mod_proxy65:
access: net_users
max_connections: 5
mod_http_upload:
access: net_users
put_url: https://@HOST@/xmpp/upload
docroot: /var/www/ejabberdupload
max_size: 1073741824
custom_headers:
"Access-Control-Allow-Origin": "https://@HOST@"
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
"Access-Control-Allow-Headers": "Content-Type"
mod_pubsub:
access_createnode: net_users
plugins:
- flat
- pep
force_node_config:
## Avoid buggy clients to make their bookmarks public
storage:bookmarks:
access_model: whitelist
As above, it is possible to disable access to certain services per virtualhost using ACLs, in order to e.g. prevent users on example.net
from creating MUCs on muc.example.org
.
Separate TURN server (Coturn)
In this case, change mod_stun_disco
to this, and don't enable the listen
opts for STUN/TURN. Generate an auth secret and share it with the TURN server instance.
mod_stun_disco:
secret: "auth_secret"
services:
-
host: turn.example.net
type: stun
-
host: turn.example.net
type: turn
Originally adapted from: blos.sm
Support chat
This is obviously not an exhaustive list and if you have a really good recommendation please contact us here: servers@joinjabber.org (web chat) (xmpp)
-
Virtualhosts allow more than one XMPP service to be run on one server. For example, one XMPP service with the domain
example.net
and another XMPP service with the domainexample.org
, both somewhat separated from each other as if they were run on different servers. ↩︎