diff options
Diffstat (limited to 'articles')
| -rw-r--r-- | articles/ejabberd.md | 467 | 
1 files changed, 467 insertions, 0 deletions
diff --git a/articles/ejabberd.md b/articles/ejabberd.md new file mode 100644 index 0000000..52b1047 --- /dev/null +++ b/articles/ejabberd.md @@ -0,0 +1,467 @@ ++++ +title = "cozy ejabberd xmpp chat server" +created_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!: +- [ ] add dns records +- [ ] open firewall ports +- [ ] set up web server +- [ ] get ssl certificates +- [ ] set up postgres database +- [ ] install ejabberd +- [ ] write configuration +- [ ] start service + +</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) +- `stun.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 `stun.example.slay`. + +you will then have to add one last set of srv records for stun/turn. + +``` +_stun._udp   IN SRV  5 0 3478 stun.example.slay. +_stun._tcp   IN SRV  5 0 3478 stun.example.slay. +_stuns._tcp  IN SRV  5 0 5349 stun.example.slay. + +_turn._udp   IN SRV  5 0 3478 stun.example.slay. +_turn._tcp   IN SRV  5 0 3478 stun.example.slay. +_turns._tcp  IN SRV  5 0 5349 stun.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` +  - `stun.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: 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. at the time of writing, the database driver for ejabberd does [not support](https://github.com/processone/p1_pgsql/issues/6) `scram-sha-256`, so you will need to [set](https://www.postgresql.org/docs/16/runtime-config-connection.html#GUC-PASSWORD-ENCRYPTION) `password_encryption = 'md5'` in your `postgresql.conf` file. + +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. +2. create a database for your virtualhost with `CREATE DATABASE ejabberd_example OWNER ejabberd;`. replace `example` with something corresponding to your virtualhost. +3. import the database schema from github with the command `su -c "curl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | psql ejabberd_example" ejabberd` (once again replace `example`). + +note down the postgres ejabberd user password. + +## step 5: 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 6: ejabberd configuration + +- loglevel last +- create docroot for uploads + +begin by replacing `localhost` under `hosts` with your virtualhost (e.g. `example.slay`), then list the certfiles you have obtained under `certfiles`. + +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. + +``` +  - +    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. 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`: + +``` +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: stun.example.slay +          port: 5349 +          type: stuns +          transport: tcp +          restricted: false +        - +          host: stun.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. + +### 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 the ejabberd 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. + +</div> + +<hr> + +# extra goodies! + +## 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`. +  | 
