ejabberd + Biboumi + Docker = 🌞💬

Toastie 319cb0a9b4 added missing slash in volume paths 2 年 前
.vscode b7a2093bbd initial 2 年 前
EXTRAS 286d0fd0ce fixed and cleaned up 2 年 前
data 57343f072b disabled scram as possibly not compatible with stun/turn auth 2 年 前
.env.example e08a86e094 fixed typo 2 年 前
.gitignore 8770b6a44b fixed/reworked setting hostname/ip config 2 年 前
LICENSE 3981f316eb removed (c) 2 年 前
README.md 319cb0a9b4 added missing slash in volume paths 2 年 前
RELEASE.md bddabd12f8 updated TASKS/RELEASE 2 年 前
TASKS.md fd0718a02a added turn test task 2 年 前
docker-compose.yml a35471cf7a added option to have custom config files 2 年 前
entrypoint.sh 1adc140ecd overwrite existing custom.yml 2 年 前

README.md

Dockerized XMPP with IRC gateway

This setup packs all necessary configurations to run an ejabberd XMPP Server with a Biboumi 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.

Used docker images

  • This setup uses ejabberd/ecs from the ProcessOne ejabberd developers. The underlying Dockerfile 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 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. 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 reverse proxy
  2. together with 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 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.

  • 5222:5222 Client 2 Server — bare minimum to speak with clients
  • 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.
  • 5269:5269 Server 2 Server — only needed when we want to speak with users from other servers
  • 5280:5280 Web admin (disabled), BOSH and WebSocket
  • 5443:5443 HTTP Upload to share small files between clients
  • 3478:3478/udp stun/turn
  • 5349:5349/tcp stuns/turns
  • 113:8113 Identd so IRC servers can differentiate between users
  • 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
  • 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) 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/

    1. Link the http header configuration in your reverse-proxy, see https://git.in-ulm.de/ulpeters/reverse-proxy#ejabberd

    2. Run docker-compose up and watch out for possible errors. If everything goes fine, re-run with -d.

    3. 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 <user> <domain> <password>

    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`.
    
    1. (Optional) Run the XMPP compliance tester: https://compliance.conversations.im/

    2. (Optional) Update the static website in data/www/

    3. (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 <host> List all registered users in HOST
  • unregister <user> <host> 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
Biboumi

Biboumi documentation

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 different unique node and DB name. To prevent a new DB beeing created with each container restart, use docker run --hostname <hostname> or hostname: and domainname: in the docker-compose.yml.

/opt/docker/proxy/data/certs/<hostname> 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"