aboutsummaryrefslogtreecommitdiffstats
path: root/blog/ejabberd.md
diff options
context:
space:
mode:
Diffstat (limited to 'blog/ejabberd.md')
-rw-r--r--blog/ejabberd.md513
1 files changed, 513 insertions, 0 deletions
diff --git a/blog/ejabberd.md b/blog/ejabberd.md
new file mode 100644
index 0000000..7831432
--- /dev/null
+++ b/blog/ejabberd.md
@@ -0,0 +1,513 @@
++++
+title = "cozy ejabberd xmpp chat server"
+published_at = "2023-09-16T21:07:40+0100"
+tags = ["tech", "sysadmin"]
++++
+
+today we are going to do the **ultimate** *cozy* ejabberd server guide. the only prerequisites for this guide are, one, having already set up some form of server, whether it be a vps, or an old laptop behind your fridge, it doesn't matter, because this isn't the email network, and two, ownership of a domain name, e.g. `blos.sm`.
+
+we shall begin with a single virtualhost[^1]. for example purposes, we will use `example.slay` (let's imagine this is a real tld because why not) as the domain. just replace all instances of `example.slay` with your own domain.
+
+[^1]: virtualhosts allow more than one xmpp service to be run on one server. for example, you could have one xmpp service with the domain `example.slay` and another xmpp service with the domain `example.flop`, both somewhat separated from each other as if they were run on different servers.
+
+<div>
+<div class="panel checklist">
+checklist!:
+<input type="checkbox" id="hide-checklist"><label for="hide-checklist">hide</label>
+
+- [ ] add dns records
+- [ ] open firewall ports
+- [ ] set up web server
+- [ ] get ssl certificates
+- [ ] install ejabberd
+- [ ] set up postgres database
+- [ ] write/edit configuration
+- [ ] start service
+- [ ] create admin user
+- [ ] change loglevel
+
+</div>
+
+## step 1: set up your dns records
+
+you will need dns A/AAAA records and srv records for each service on your xmpp server.
+
+you will need a records for:
+
+- `example.slay`
+- `muc.example.slay` (for group chats)
+- `upload.example.slay` (for http file upload)
+- `pubsub.example.slay` (for the pubsub node)
+- `proxy.example.slay` (for file transfer proxy)
+- `turn.example.slay` (for stun/turn)
+
+each pointing to the ip address of your server that is going to run ejabberd. the last two are technically optional, but i would recommend them.
+
+as well as srv records for each, pointing to a domain that resolves to your 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.slay.
+_xmpps-client._tcp IN SRV 5 0 5223 example.slay.
+_xmpp-server._tcp IN SRV 5 0 5269 example.slay.
+_xmpps-server._tcp IN SRV 5 0 5270 example.slay.
+```
+
+and
+
+```
+_xmpp-client._tcp.muc IN SRV 5 0 5222 example.slay.
+_xmpps-client._tcp.muc IN SRV 5 0 5223 example.slay.
+_xmpp-server._tcp.muc IN SRV 5 0 5269 example.slay.
+_xmpps-server._tcp.muc IN SRV 5 0 5270 example.slay.
+```
+
+for each of the subdomains (starting with muc). exclude `turn.example.slay`.
+
+you will then have to add one last set of srv records for stun/turn.
+
+```
+_stun._udp IN SRV 5 0 3478 turn.example.slay.
+_stun._tcp IN SRV 5 0 3478 turn.example.slay.
+_stuns._tcp IN SRV 5 0 5349 turn.example.slay.
+
+_turn._udp IN SRV 5 0 3478 turn.example.slay.
+_turn._tcp IN SRV 5 0 3478 turn.example.slay.
+_turns._tcp IN SRV 5 0 5349 turn.example.slay.
+```
+
+extra info: as a result of these records, you could technically play around with hosting xmpp on a server other than the one at `example.slay` (i.e. if you were splitting services across servers on one domain) by using the srv delegation. further info can be found at [XEP-0368](https://xmpp.org/extensions/xep-0368.html).
+
+## step 2: open your 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 your web server and get your ssl certificates
+
+you need to:
+
+- get (an) ssl certificate(s) for your domain, as well as several subdomains, all in all:
+ - `example.slay`
+ - `muc.example.slay`
+ - `upload.example.slay`
+ - `pubsub.example.slay`
+ - `proxy.example.slay`
+ - `turn.example.slay`
+- proxypass http://127.0.0.1:5443 through to:
+ - https://example.slay/xmpp
+ - https://example.slay/.well-known/host-meta
+ - https://example.slay/.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
+
+you can technically avoid using something like nginx + certbot, and use the built-in [ejabberd acme module](https://docs.ejabberd.im/admin/configuration/basic/#acme), thereby skipping this, but i am assuming you may also want to host other web services on your system, in which case you would want to reverse proxy each http service to a single https web service.
+
+## step 4: install ejabberd
+
+now you should finally install the [system package](https://docs.ejabberd.im/admin/installation/#operating-system-packages). make sure that your build has postgresql support.
+
+make sure that the file `/etc/ejabberd/ejabberd.yml` exists, and of course, 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](https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example). if you are getting it from the repo, make sure the version corresponds to the version of ejabberd packaged by your os.
+
+## step 5: set up postgres database
+
+we will be creating a separate database for each virtualhost, as i feel this makes things clearer, and also it easier to migrate individual virtualhosts in the future. however, there is also now the ability to only have one, as described [here](https://docs.ejabberd.im/admin/configuration/database/#default-and-new-schemas).
+
+you should follow standard postgresql installation instructions for your OS. once this has been done, you should connect to your database as an admin and:
+
+1. create an ejabberd database user with `CREATE USER ejabberd WITH PASSWORD 'your_password';`. don't forget to change the password, and note it down.
+2. create a database for your virtualhost with `CREATE DATABASE ejabberd_example OWNER ejabberd;`. replace `example` with something corresponding to your virtualhost.
+3. quit psql, and import the database schema from github with the command `curl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | sudo -u ejabberd psql ejabberd_example` (once again replace `example`).
+
+## step 6: ejabberd configuration
+
+begin by replacing `localhost` under `hosts` with your virtualhost (e.g. `example.slay`), then list the certfiles you have obtained under `certfiles`.
+
+```
+hosts:
+ - example.slay
+
+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 your setup.
+
+```
+host_config:
+ example.slay:
+ 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 tls s2s 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
+```
+
+we will also be enabling the http server and the stun/turn server modules. make sure you have set `turn_ipv4_address` and `ip` to your server's ipv4 address. tls will be off for the http server as we are reverse proxying it through our 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
+```
+
+now set `s2s_use_starttls: required` at the root.
+
+at this point you can set up some ACLs. `acls` are just the access control lists, you can also set up `access_rules` corresponding to your needs, which will be what are passed to module settings. you should at the minimum add an admin user. example:
+
+```
+acl:
+ admin:
+ user: admin@example.slay
+ flop:
+ - user: flop@example.slay
+ clown:
+ - user: clown@example.slay
+
+access_rules:
+ queens:
+ allow: flop
+ allow: clown
+```
+
+next, we will be adding some modules under `modules` and changing some settings.
+
+add abuse addresses under `mod_disco`. you can also add other contact addresses according to [XEP-0157](https://xmpp.org/extensions/xep-0157.html):
+
+```
+modules:
+# ...
+ mod_disco:
+ server_info:
+ -
+ modules: all
+ name: "abuse-addresses"
+ urls: ["mailto:abuse@example.slay"]
+```
+
+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 you wish not to archive 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.slay` to your 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.slay
+ port: 5349
+ type: stuns
+ transport: tcp
+ restricted: false
+ -
+ host: turn.example.slay
+ port: 5349
+ type: turns
+ transport: tcp
+ restricted: true
+```
+
+now it is time to enable MUCs (multi-user chatrooms), file proxy, http upload, and pubsub.
+
+### mucs:
+
+make sure that you set the host to your muc subdomain, otherwise it will attempt to use `conference.example.slay`. you can also set `mam: false` in `default_room_options` to disable server-side message archiving by default.
+
+```
+ mod_muc:
+ host: muc.example.slay
+ 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 make sure it is owned by the `ejabberd` user. you can also change `max_size` (the max upload size) to whatever you prefer.
+
+### 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.slay password` to register `admin@example.slay` with the password `password`.
+
+there is a compliance tester at [compliance.conversations.im](https://compliance.conversations.im) if you wish to test your server. once you are done and believe everything has been set up correctly, you can optionally change the [`loglevel`](https://docs.ejabberd.im/admin/configuration/toplevel/#loglevel) at the root of the config.
+
+there will be an admin page accessible at [https://example.slay/xmpp/admin](https://example.slay/xmpp/admin).
+
+</div>
+
+<hr>
+
+# extra goodies!
+
+## web client
+
+you can set up conversejs using [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs). you will also need to possibly update your web server config to proxy the new endpoint, as so:
+
+```
+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 further virtualhosts you should create a new database for each, and add them to the database part of the config. e.g.:
+
+```
+host_config:
+ example.slay:
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_port: 5432
+ sql_username: "ejabberd"
+ sql_password: "postgres_password"
+ sql_database: "ejabberd_slay"
+ auth_method: sql
+ auth_password_format: scram
+ example.flop:
+ sql_type: pgsql
+ sql_server: "localhost"
+ sql_port: 5432
+ sql_username: "ejabberd"
+ sql_password: "postgres_password"
+ sql_database: "ejabberd_flop"
+ auth_method: sql
+ auth_password_format: scram
+```
+
+there cannot be conflicts between declarations in the config file, so if you have `mod_muc`, `mod_proxy65`, `mod_http_upload` and `mod_pubsub` 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.flop:
+ modules:
+ mod_muc:
+ host: muc.example.flop
+ access_create: flops
+ access_persistent: flops
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ default_room_options:
+ mam: false
+ mod_proxy65:
+ access: flops
+ max_connections: 5
+ mod_http_upload:
+ access: flops
+ 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: flops
+ plugins:
+ - flat
+ - pep
+ force_node_config:
+ ## Avoid buggy clients to make their bookmarks public
+ storage:bookmarks:
+ access_model: whitelist
+ example.flop:
+ modules:
+ mod_muc:
+ hosts:
+ - muc.example.flop
+ access_create: local
+ access_persistent: local
+ access:
+ - allow
+ access_admin:
+ - allow: admin
+ default_room_options:
+ mam: false
+ mod_proxy65:
+ access: local
+ max_connections: 5
+ 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"
+ 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
+```
+
+as you can see above, you may also want to disable access to certain services per virtualhost using ACLs, in order to e.g. prevent users on `example.slay` from creating MUCs on `muc.example.flop`.
+
+## 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 your turn server instance.
+
+```
+ mod_stun_disco:
+ secret: "auth_secret"
+ services:
+ -
+ host: turn.example.slay
+ type: stun
+ -
+ host: turn.example.slay
+ type: turn
+```
+