# Dockerized XMPP with IRC gateway This setup packs all necessary configurations to run an [ejabberd](https://docs.ejabberd.im/) XMPP Server with a [Biboumi](https://biboumi.louiz.org/) IRC Gateway. Just put your IP and hostname in the `.env` file, fire up docker-compose and you're good to go! The whole service lives in a single folder to ease backup and migration. All persistent data is stored in `./data/`. The configuration was crowd-sourced, for more details, see [EXTRAS/reference-configurations](EXTRAS/reference-configurations). ## Used docker images - This setup uses [ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/) from the ProcessOne ejabberd developers. The underlying [Dockerfile](https://github.com/processone/docker-ejabberd) builds the server from source and installs it into a Alpine image. The server runs as unprivileged user (uid 9000). This image doesn't come with many parameters or helper scripts, so all configurations are made in the ejabberd.yml configuration file. [rroemhild/docker-ejabberd](https://github.com/rroemhild/docker-ejabberd) wasn't used as it didn't run reliably for me in July 2018. Still it is worth to take a look and learn from the used parameters and helper scripts. - Biboumi runs from [louiz/biboumi](https://hub.docker.com/r/louiz/biboumi/). The image is provided by the Biboumi developer himself and only contains the binary as it is based on `FROM scratch`. All parameters are set using environment variables defined in `docker-compose.yml`. The server runs under a unprivileged user (uid 1000). ## Prerequisites It is assumed that on the docker host we're already running 1. a [jwilder/nginx-proxy](https://github.com/jwilder/nginx-proxy) reverse proxy 2. together with [nginx-proxy/acme-companion](https://github.com/nginx-proxy/acme-companion) to obtain letsencrypt TLS certificates ## Design decisions: 1. One host per instance / no vhosts 2. No cluster setup 3. Turn key, only need to set IP and hostname in .env 4. Compliance with https://compliance.conversations.im/ 5. External acme client – for more flexibility when serving other containers on the same host. (Port 80 can be bound only once, so there can be only one acme client per host.) 6. Limited administration to `ejabberdctl`. Disabled in-band registration, web administration and the web API. ## Startup process - ejabberd - The ejabberd image starts with a modified `entrypoint.sh` which generates some host-specific files based on variables set in `.env`: - `~/env.yml` with variables (macros) used in the remaining configuration - `~/custom.yml` concatenating `~/conf.custom/*.yml` - `~/.well-known/host-meta` and `~/.well-known/host-meta` required for XEP-0156 compliance - ejabberd starts with `~/conf/ejabberd.yml` which includes the configurations from `~/conf/conf.d/` and `~/ env.yml` from above - Certificates - [letsencrypt-nginx-proxy-companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion) obtains the Let's Encrypt Certificates based on the environment variables set in `docker-compose.yml`. The hostname is set in `.env`, so no need to modify `docker-compose.yml`. ## Open Ports Marked [x] ports will be opened on the Docker host, so they should not be used by another service on the same host. - [x] `5222:5222` Client 2 Server — bare minimum to speak with clients - [x] `5223:5223` XMPP over TLS is actually deprecated, instead STARTTLS on port 5222 should be used. It is active for backward compatibility or could be moved to port 443 to ease usage behind firewalls. - [x] `5269:5269` Server 2 Server — only needed when we want to speak with users from other servers - [x] `5280:5280` Web admin (disabled), BOSH and WebSocket - [x] `5443:5443` HTTP Upload to share small files between clients - [x] `3478:3478/udp` stun/turn - [x] `5349:5349/tcp` stuns/turns - [x] `113:8113` Identd so IRC servers can differentiate between users - [x] `443!8080` Integrated webserver to serve static content, such as a javascript xmpp client. The port is exposed via the reverse proxy. - [ ] `5347:5347` Only used for internal unencrypted communication with the Biboumi (IRC gateway) component - [ ] `----:4560` XMLRPC — API, disabled - [x] `7777:7777` XEP-0065: SOCKS5 Bytestreams file transfer proxy between XMPP clients *BOSH (previously known as 'HTTP binding' or "http-bind") is a technology to use XMPP over HTTP. This allows XMPP applications to run in web pages, but also any other HTTP-only environment such as behind restrictive firewalls.* ## Used volumes ejabber runs under user `ejabberd` with uid/guid `9000`. All data is stored in `/home/ejabberd/` within the container. - `./data/backup/` — place to to store DB dumps - `./data/biboumi/database/` — sqlite DB for Biboumi. The Biboumi configuration is defined via environment variables in `docker-compose.yml`. - `./data/conf/ejabberd.yml` — configuration file referring to conf.d - `./data/conf.d/` — actual configuration files - `./data/conf.custom/` — acaddtional custom configuration files - `./data/database/` — Erlang DB used to store all application data - `./data/uploads/` — files transferred between users - `./data/www/` — static web content - `/opt/docker/reverse-proxy/data/certs/${HOSTNAME}` — certificates and key material ## Installation & Configuration 1. Clone the repo and the [reverse proxy and Let's Encrypt companion)](https://git.in-ulm.de/ulpeters/reverse-proxy) to /opt/docker and prepare data folders with proper permissions. ``` inst_dir=/opt/docker/ejabberd mkdir -p $inst_dir/data/database \ $inst_dir/data/backup \ $inst_dir/data/upload \ $inst_dir/data/www \ && chown -R 9000:9000 $inst_dir/data/ mkdir -p $inst_dir/data/biboumi/database chown -R 1000:1000 $inst_dir/data/biboumi/ ``` 2. Copy `.env.template` to `.env` and setup your hostname and ip. A fixed ip is necessary to stun/turn to work. 3. Setup DNS To allow service discovery, following DNS records have to be created. ``` im 3600 IN A 203.0.113.113 *.im 3600 IN CNAME in #_service._proto.name TTL class SRV priority weight port target _xmpp-client._tcp.im.example.net. 3600 IN SRV 5 0 5222 im.example.net. _xmpp-clients._tcp.im.example.net. 3600 IN SRV 10 0 5223 im.example.net. _xmpp-server._tcp.im.example.net. 3600 IN SRV 5 0 5269 im.example.net. _stun._tcp.im.example.net. 3600 IN SRV 5 0 3478 im.example.net. _stun._udp.im.example.net. 3600 IN SRV 5 0 3478 im.example.net. _stuns._tcp.im.example.net. 3600 IN SRV 5 0 5349 im.example.net. _turn._tcp.im.example.net. 3600 IN SRV 5 0 3478 im.example.net. _turn._udp.im.example.net. 3600 IN SRV 5 0 3478 im.example.net. _turns._tcp.im.example.net. 3600 IN SRV 5 0 5349 im.example.net. ``` To check the records: https://kingant.net/check_xmpp_dns/ 4. Link the http header configuration in your reverse-proxy, see https://git.in-ulm.de/ulpeters/reverse-proxy#ejabberd 5. Run `docker-compose up` and watch out for possible errors. If everything goes fine, re-run with `-d`. 6. Create users ejabberd comes by default with no users. Privileges and ACLs for users are set in `conf/conf.d/32-access.yml`. In the default configuration file `admin` is set as privileged user already. To create new users, run from the docker host: `docker exec -it ejabberd /home/ejabberd/bin/ejabberdctl register ` We'll need to create at least a user for the IRC gateway. ``` user=ircadmin pass=`apg -q -n1 -m12` docker exec -it ejabberd /home/ejabberd/bin/ejabberdctl register $user $hostname $pass Further self-registration or LDAP authentication can be enabled in `ejabberd.yml`. ``` 7. (Optional) Run the XMPP compliance tester: https://compliance.conversations.im/ 8. (Optional) Update the static website in `data/www/` 9. (Optional) Place additional configuration files in `data/conf.custom/` ## Maintenance ##### Get a shell in the container `docker exec -it ejabberd sh` ##### Control the server `docker exec -it ejabberd /home/ejabberd/bin/ejabberdctl \` - `registered_users ` List all registered users in HOST - `unregister ` List all registered users in HOST - `modules_available` List the contributed modules available to install - `modules_installed` List the contributed modules already installed - `mnesia` Get details about the database - `reload_config` Reload ejabberd configuration file into memory (this will not start new servers) - `connected_users` list connected users with their resources - `backup /home/ejabberd/backup/ejabberd.backup` Backup database - `install_fallback /home/ejabberd/backup/ejabberd.backup` restores the db and makes it active after the next restart - `help` lists [available commands](https://docs.ejabberd.im/admin/guide/managing/#list-of-ejabberd-commands) ##### Biboumi [Biboumi documentation](https://github.com/louiz/biboumi/blob/master/doc/biboumi.1.rst) ##### Check if the IRC server certificate is valid firefox https://www.sslshopper.com/ssl-checker.html#hostname=irc.example.net:6697 #### Notes and pitfalls ##### The Node and DB name changes with the hostname Docker's randomly generated hostnames causes ejabberd to [calculate](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname) different unique node and DB name. To prevent a new DB beeing created with each container restart, use `docker run --hostname ` or `hostname:` and `domainname:` in the `docker-compose.yml`. `/opt/docker/proxy/data/certs/` should be readable by `ejabberd` user. #### Debugging Inspect the vanilla container: `docker run --rm -it --entrypoint /bin/sh ejabberd/ecs` Dump current configuration as seen by ejabberd: `docker exec ejabberd /bin/sh -c "bin/ejabberdctl dump_config ~/backup/config-dump.yml"`