From 805b1c983dd62d53b1be88dc957768e77bb0ba33 Mon Sep 17 00:00:00 2001 From: Miquel Lionel Date: Mon, 31 May 2021 21:06:34 +0100 Subject: add html files for offline reading --- index.fr.html | 932 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 855 +++++++++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 192 ++++++++++++ 3 files changed, 1979 insertions(+) create mode 100755 index.fr.html create mode 100755 index.html create mode 100755 styles.css diff --git a/index.fr.html b/index.fr.html new file mode 100755 index 0000000..f0e62dc --- /dev/null +++ b/index.fr.html @@ -0,0 +1,932 @@ + + + +Mise en place d'un client web XMPP avec liens de création de compte (conversejs+prosody) + + + + +

Mise en place d'un client web XMPP avec liens de création de compte (conversejs+prosody)

+[lienbillet seul] +

maj 28/05/2021: corrections des iptables (régles dupliqués), ajout de liens de +documentation, ajout d'une entrée de DNS pour websocket. http_altconnect rajouté en module prosody +pour éviter les erreurs dans la console du navigateur +

maj 22/05/2021: OMEMO par pas défaut au final, on laisse le choix à +l'utilisateur de l'activer dans ses discussions, et c'est mieux niveau historique des messages +sur plusieurs appareils. On améliore aussi la commande apt (-y --no-install-recommends).

+

Bonjour à tous,

+

Aujourd'hui je vais vous montrer comment mettre en place un serveur XMPP avec un +client Web sur la même machine, avec la +possibilité +d'envoyer des liens d'invitations de création de comptes ou d'ajout +en contact pour faciliter l'embarquement de vos proches et amis. +Je ne fais pas de présentation du protocole XMPP, c'est hors de la +portée de ce guide et des gens l'explique mieux que moi, je pense +surtout à cette + suite d'articles de chez goffi.org.
+pavé césar gépalu lol: c'est un protocole +composé d'une myriade de standards permettant +la construction de réseaux de messagerie instantanée +décentralisés et sécurisés. Possibilité d'avoir +des salons publics et privés. +

+ +

+ Sommaire: +
    +
  1. Prérequis
  2. +
  3. Configuration zone DNS
  4. +
  5. Configuration pare-feu
  6. +
  7. Installation des + paquets
  8. +
  9. Installation des + certificats
  10. +
  11. Configuration des + paquets
  12. +
  13. Connexion à l'interface web
  14. +
  15. Génération des liens d'invitation
  16. + +
  17. Remarques
  18. +
  19. Documentations
  20. +
  21. Remerciements
  22. +
+
+

+ +

1. +Prérequis

+ +

Trêve de bavardages, voici ce que l'on va devoir installer: +


+ + +

Mettez-vous d'accord sur votre nom de domaine: vous remplacerez les +occurences de exemple.fr dans ce guide par votre +nom de domaine. On va aussi utiliser un sous-domaine pour +l'interface web, chat.exemple.fr, un qui contiendra les +salons publics et privés, salons.exemple.fr et un +autre pour le serveur d'envoi de fichiers, +f.exemple.fr.
+N'oubliez pas de rajouter des entrées DNS type A pour ces +sous-domaines. +

+ +

2. Configuration zone DNS

+

Tout d'abord, rendez-vous dans la zone DNS de votre nom de domaine, on va +rajouter quelques entrées de type SRV et TXT pour faciliter la +communication des clients et serveurs XMPP avec nous (plus de +détails en anglais par là ou encore ici): +

+_xmpp-client._tcp.exemple.fr 86400 SRV 1 1 5222 exemple.fr.
+_xmpps-client._tcp.exemple.fr 86400 SRV 1 1 5223 exemple.fr.
+_xmpp-server._tcp.exemple.fr 86400 SRV 1 1 5269 exemple.fr.
+_xmppconnect.exemple.fr TXT _xmpp-client-xbosh=https://chat.exemple.fr/http-bind
+_xmppconnect.exemple.fr TXT _xmpp-client-websocket=wss://chat.example.fr/xmpp-websocket
+
+ +

À noter: le exemple.fr à de gauche +reprénte le domaine XMPP, celui de droite est le serveur qui +répondra aux requêtes et une entrée DNS de type A +doit exister pour celui-ci, pas de raccourci type CNAME autorisé. +C'est pratique quand vous voulez des adresses utilisateurs "propres" tel +que vous@exemple.fr mais que le serveur est en fait sur le +sous-domaine xmpp.exemple.fr. Si c'est le cas, changez +alors le exemple.fr de droite par +xmpp.exemple.fr.

+ + +

3. Configuration pare-feu

+

IMPORTANT:Connectez-vous en tant que root avant d'exécutez les +commandes contenues dans +ce tutoriel. On va ensuite configurer des règles +iptables pour éviter de se mordre les doigts +de rage par la suite. Je pars du principe que vous avez déjà + configuré quelques règles vous permettant de SSH tranquillou. + +

+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5222 -j ACCEPT # on accepte les connexions client à serveur
+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5223 -j ACCEPT # pareil que le dessus mais celles qui sont chiffrés.
+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5269 -j ACCEPT # connexions serveur à serveur.
+iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # HTTP pour l'interface web
+iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT # HTTPS
+iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT # pour envoyer des requête DNS
+iptables -A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT # en output aussi si des serveurs utilisant l'HTTP upload pour les pjs
+iptables -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
+
+iptables-save > /etc/iptables/iptables.rules
+
+
+# pareil pour l'ipv6
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5222 -j ACCEPT
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5223 -j ACCEPT
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5269 -j ACCEPT
+ip6tables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # HTTP pour l'interface web
+ip6tables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT # HTTPS
+ip6tables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT # pour envoyer des requête DNS
+ip6tables -A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT # en output aussi si des serveurs utilise l'HTTP upload pour les pjs
+ip6tables -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
+
+ip6tables-save > /etc/iptables/ip6tables.rules
+systemctl enable --now iptables # activation du service iptables si pas
+# déjà fait pour garder la config du pare-feu après reboot.
+

+ +

4. Installation des paquets

+ +

Après ceci, installez prosody depuis les sources ou +votre installeur de +paquets. On en profitera pour installer les autres composants requis +à ce guide. Sur un Debian classique celà donnera:
+

sudo apt-get install -y --no-install-recommends prosody nginx-full \ 
+certbot python3-certbot-nginx \
+mercurial libjs-bootstrap4 libjs-jquery

+ +

On va maintenant cloner les prosody-modules depuis la +source. Il n'est pas toujours à jour dans les dépôts de +paquets: +

+hg clone https://hg.prosody.im/prosody-modules/ /usr/lib/prosody/modules/
+
+

On prépare en même temps un script pour mettre +à automatiquement via la crontab. Créez le fichier +/usr/local/sbin/maj_prosodymods.sh avec le contenu suivant: +

+#!/bin/sh
+MODDIR="/usr/lib/prosody/modules/"
+
+if test -d $MODDIR; then
+    cd $MODDIR
+    hg pull --update
+else
+    mkdir -p /usr/lib/prosody
+    hg clone https://hg.prosody.im/prosody-modules/ $MODDIR
+fi
+
+

N'oubliez pas de faire un coup de chmod +x dessus... +

+ +

On va ensuite télécharger conversejs. +À ce jour sur Debian Buster, il n'y a pas de paquet à jour +officiel pour conversejs, donc on va faire un script qu'on +intégrera dans notre crontab.
+Insérez le contenu suivant +dans le fichier /usr/local/sbin/update_conversejs.sh puis +rendez-le exécutable: +

+#!/bin/sh
+TEMPDIR="$(mktemp -d)"
+LOG=/var/log/update_conversejs.log
+WWWDIR='/var/www/chat.exemple.fr'
+WWWUSER='www-data' # cette valeur sera sûrement différente
+                   # si vous n'êtes pas sur Debian avec nginx.
+                   # par ex. ça sera 'http' sous Arch avec nginx.
+
+mkdir -p $WWWDIR/dist
+cd $TEMPDIR
+printf "\n\n$(date) - INFO - Starting updating conversejs..." | tee -a $LOG
+CURL_ERR=$(curl -s \
+    https://api.github.com/repos/conversejs/converse.js/releases/latest | \
+    grep -o "https://.*\.tgz" | \
+    grep converse\.js- | \
+    xargs curl -fsOJL) || \
+    (printf "\n$(date) - ERR - Updating conversejs failed." | tee -a $LOG && exit)
+
+# on installe libsignal pour pouvoir utiliser OMEMO
+# dans le client web
+if test -e "$WWWDIR/dist/libsignal-protocol.min.js"; then
+    printf "\n$(date) - TOK - Libsignal already installed, skipping." | tee -a $LOG
+else
+    curl -fsOJL \
+    https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js || \
+    (printf "\n$(date) - ERR - Updating libsignal-protocol-javascript failed." | \
+    tee -a $LOG)
+    cp libsignal*.js $WWWDIR/dist/
+fi
+
+tar xzf *.tgz
+cp -rf package/dist  $WWWDIR/
+sed "s/fullscreen\.html/index\.html/g" package/manifest.json > $WWWDIR/manifest.json
+chown $WWWUSER:$WWWUSER -R $WWWDIR/
+chmod 755 -R $WWWDIR/
+rm -rf $TEMPDIR
+printf "\n$(date) - TOK - Done." | tee -a $LOG
+
+

Exécutez ce fichier pour gagner du temps dans l'étape 6, vous n'aurez pas à +créer le $WWWDIR. + +

5. Installation des +certificats

+ + +

On va ensuite lancer certbot pour générer des certificats. +Je pars du principe que vous avez configuré les entrées de +type A pour exemple.fr, chat.exemple.fr +et f.exemple.fr dans votre zone DNS: +

certbot certonly --agree-tos --nginx --deploy-hook "prosodyctl --root cert import /etc/letsencrypt/live" -d chat.exemple.fr -d exemple.fr -d f.exemple.fr -d salons.exemple.fr 
+

Une fois que cette commande s'exécute avec succés, des +dossiers correspondants aux domaines demandés devrait +apparaître dans /etc/letsencrypt/live/. +

+ +

6. Configuration des +paquets

+ +

Le moment est venu! On va tâter â prosody et sa +configuration.
+Ouvrez le fichier /usr/lib/prosody/net/http/server.lua, on va simplifier la +connexion entre l'interface HTTP de prosody (BOSH) et nginx.
+Dans le +fichier, trouver la ligne suivante: +

+headers = { date = date_header, connection = response_conn_header };
+
+

et remplaçez la par: +

+headers = { date = date_header, connection = response_conn_header, 
+            access_control_allow_origin = "exemple.fr" };
+-- si 'exemple.fr' ne résout pas le soucis de connexion, mettez
+-- un '*' à la place. Pour rappel, c'est l'adresse du serveur XMPP
+-- qui faut mettre, et non celle du domaine.
+

+ +

Ensuite, on va s'occuper du script qui permettra d'envoyer des fichiers +à d'autres utilisateurs. Exécutez ceci: +

+mkdir -p /var/www/upload
+chown www-data:www-data /var/www/upload # l'utilisateur de nginx peut
+                                        # différer selon la distrib, faites gaffe
+mkdir -p /usr/local/lib/perl
+wget -O /usr/local/lib/perl/upload.pm https://raw.githubusercontent.com/weiss/ngx_http_upload/master/upload.pm
+
+

Ouvrez le fichier upload.pm, cherchez un bout de texte +ressemblant à my $external_secret = 'it-is-secret'; +et changez le it-is-secret par un mot de passe fort, de +préfèrence sans apostrophe ou antislash pour éviter +que le programme en Perl plantouille quand on va tout démarrer.
+Notez-vous ce mot de passe sur un bout de papier, on va en avoir besoin +derechef.

+ +

Passons à la configuration de prosody: éditez le fichier + /etc/prosody/prosody.cfg.lua avec les informations +suivantes: +

+admins = { "vous@exemple.fr" }
+
+-- For more information see: https://prosody.im/doc/libevent
+-- use_libevent = true
+
+plugin_paths = { "/usr/lib/prosody/modules" }
+
+modules_enabled = {
+
+    -- Generally required
+    "roster"; -- Allow users to have a roster. Recommended ;)
+    "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+    "tls"; -- Add support for secure TLS on c2s/s2s connections
+    "dialback"; -- s2s dialback support
+    "disco"; -- Service discovery
+
+    -- Not essential, but recommended
+    "carbons"; -- Keep multiple clients in sync
+    "carbons_copies";
+    "carbons_copies_adhoc";
+    "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
+    "private"; -- Private XML storage (for room bookmarks, etc.)
+    "blocklist"; -- Allow users to block communications with other users
+    "vcard4"; -- User profiles (stored in PEP)
+    "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
+    "smacks";
+    "bookmarks"; -- vieux module mais compatible avec la
+    -- majorité des clients XMPP contrairement
+    -- à bookmarks2
+    --"bookmarks2";
+    "presence"; -- voir l'état de l'utilisateur (en ligne, hors
+    -- ligne, etc...)
+    "offline";
+
+    -- Nice to have
+    "version"; -- Replies to server version requests
+    "uptime"; -- Report how long server has been running
+    "time"; -- Let others know the time here on this server
+    "ping"; -- Replies to XMPP pings with pongs
+    "register"; -- Allow users to register on this server using a client and change passwords
+    "mam"; -- Store messages in an archive and allow users to access it
+    "csi";
+    "csi_simple"; -- Simple Mobile optimizations
+    "csi_battery_saver";
+    "vjud"; -- recherche d'utilisateurs dans les salons
+
+    -- Admin interfaces
+    "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
+
+    -- HTTP modules
+    "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
+    "websocket"; -- XMPP over WebSockets
+    "http_altconnect";
+
+    -- Other specific functionality
+    "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+    "limits"; -- Enable bandwidth limiting for XMPP connections
+    "groups"; -- Shared roster support
+    "server_contact_info"; -- Publish contact information for this service
+    "announce"; -- Send announcement to all online users
+    "welcome"; -- Welcome users who register accounts
+    "watchregistrations"; -- Alert admins of registrations
+    "motd"; -- Send a message to users when they log in
+    --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
+    --"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
+}
+
+-- These modules are auto-loaded, but should you want
+-- to disable them then uncomment them here:
+modules_disabled = {
+    -- "offline"; -- Store offline messages
+    -- "c2s"; -- Handle client connections
+    -- "s2s"; -- Handle server-to-server connections
+}
+
+motd_text = [[Bonjour à tous ! Bienvenue sur mon serveur XMPP. Clavardez heureux !]]
+welcome_message = "C'est ta première connexion, $username. Bienvenue à toi."
+
+daemonize = false;
+pidfile = "/run/prosody/prosody.pid";
+trusted_proxies = { "127.0.0.1", "::1" }
+
+-- Force certificate authentication for server-to-server connections
+c2s_require_encryption = true -- chiffrement requis pour connexion client à serveur
+s2s_require_encryption = true -- chiffrement requis pour connexion entre serveurs
+s2s_secure_auth = true 
+authentication = "internal_hashed"
+
+-- mam settings
+archive_expires_after = "never" -- historique permanent des chats
+
+log = {
+    -- Log files (change 'info' to 'debug' for debug logs):
+    info = "/var/log/prosody/prosody.log";
+    error = "/var/log/prosody/prosody.err";
+    -- Syslog:
+    { levels = { "error" }; to = "syslog";  };
+}
+
+-- http and certificate shenanigans
+certificates = "certs"
+
+-- Include "conf.d/*.cfg.lua"
+
+legacy_ssl_ports =  { 5223 }
+-- http_ports =  { 5280 }
+-- http_interface = { "*" }
+-- https_ports = { 5281 }
+-- https_interfaces { "*" }
+
+
+cross_domain_bosh = { "https://chat.exemple.fr" }
+cross_domain_websocket = { "https://chat.exemple.fr" }
+consider_bosh_secure = true
+consider_websocket_secure = true
+allow_registration = true -- nécessaire pour mod_invites
+registration_invite_only = true -- inscription autorisé seulement avec les invitations
+vjud_mode = "opt-in" -- l'utilisateur doit consentir pour que la recherche vjud 
+                     -- le fasse remonter dans les résultats.
+
+-- https://prosody.im/security/advisory_20210512/
+gc = {
+    speed = 500;
+}
+c2s_stanza_size_limit = 256 * 1024
+s2s_stanza_size_limit = 512 * 1024
+
+limits = {
+    c2s = {
+        rate = "10kb/s";
+    };
+    s2sin = {
+        rate = "3kb/s";
+    };
+}
+-- https://prosody.im/security/advisory_20210512/
+
+ssl = { 
+    key = "certs/exemple.fr.key";
+    certificate = "certs/exemple.fr.crt";
+}
+
+VirtualHost "exemple.fr"
+    invites_page = "https://chat.exemple.fr/invite?{invite.token}"
+    webchat_url = "https://chat.exemple.fr/"
+    http_external_url = "https://chat.exemple.fr/"
+    invite_expiry = 86400 * 7 -- 7 jours avant qu'un lien d'invitation expire
+    http_paths = {
+        invites_page = "/invite";
+        invites_register_web = "/register";
+    }
+
+    modules_enabled = {
+        "invites";
+        "invites_adhoc";
+        "invites_page";
+        "invites_register";
+        "invites_register_web";
+        "http_libjs";
+    }
+
+    contact_info = {
+        abuse = { "mailto:vous@exemple.fr", "xmpp:vous@exemple.fr" };
+        admin = { "mailto:vous@exemple.fr", "xmpp:vous@exemple.fr" };
+        security = { "mailto:vous@exemple.fr", "xmpp:vous@exemple.fr" };
+        support = { "mailto:vous@exemple.fr", "xmpp:vous@exemple.fr" };
+    };
+
+    https_certificate = "certs/exemple.fr.crt";
+    ssl = { 
+        key = "certs/exemple.fr.key";
+        certificate = "certs/exemple.fr.crt";
+    }
+
+    Component "f.exemple.fr" "http_upload_external"
+        http_upload_external_base_url = "https://f.exemple.fr/"
+        http_upload_external_secret = "its-a-secret"
+        http_upload_external_file_size_limit = 104857600 -- limite de à 100Mo pour les envois de pjs
+        ssl = { 
+            key = "certs/f.exemple.fr.key";
+            certificate = "certs/f.exemple.fr.crt";
+        }
+
+    Component "salons.exemple.fr" "muc"
+        name = "Salons (chatrooms) chez exemple.fr"
+        modules_enabled = { "muc_mam", "vcard_muc" }
+        muc_room_default_language = "fr"
+        muc_log_expires_after = "never" -- histo permanent des groupes de
+                                        -- chats
+        log_all_rooms = true
+        muc_log_by_default = true
+        muc_log_presences = false
+        restrict_room_creation = "admin" -- seul l'admin peut créer des salons
+        ssl = { 
+            key = "certs/salons.exemple.fr.key";
+            certificate = "certs/salons.exemple.fr.crt";
+        }
+
+

Oui, il faut remplaçer its-a-secret par le +mot de passe que vous avez noté tout à l'heure.
+Certains auront aussi remarqué qu'on a défini un utilisateur +administrateur au début du fichier de config, +vous@exemple.fr. On va donc créer l'utilisateur avec la +commande: +

+prosodyctl check # ça va vérifier si y'a des soucis dans la configuration
+prosodyctl adduser vous@exemple.fr
+

+ +

Créez le fichier +/var/www/chat.exemple.fr/index.html avec le texte suivant: +

+<!DOCTYPE html>
+<html class="no-js" lang="en">
+    <head>
+        <title>Converse</title>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+        <meta name="description" content="Converse XMPP/Jabber Chat"/>
+        <meta name="keywords" content="xmpp chat webchat converse.js" />
+        <link rel="manifest" href="/manifest.json">
+        <link type="text/css" rel="stylesheet" media="screen" href="/dist/converse.min.css" />
+        <script src="/dist/libsignal-protocol.min.js"></script>
+        <script src="/dist/converse.min.js"></script>
+    </head>
+    <body class="converse-fullscreen">
+        <noscript>You need to enable JavaScript to run the Converse.js chat app.</noscript>
+        <div id="conversejs-bg"></div>
+            <script>
+                /*
+                @licstart
+                This is free and unencumbered software released into the public domain.
+
+                Anyone is free to copy, modify, publish, use, compile, sell, or
+                distribute this software, either in source code form or as a compiled
+                binary, for any purpose, commercial or non-commercial, and by any
+                means.
+
+                In jurisdictions that recognize copyright laws, the author or authors
+                of this software dedicate any and all copyright interest in the
+                software to the public domain. We make this dedication for the benefit
+                of the public at large and to the detriment of our heirs and
+                successors. We intend this dedication to be an overt act of
+                relinquishment in perpetuity of all present and future rights to this
+                software under copyright law.
+
+                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+                EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+                MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+                IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+                ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+                OTHER DEALINGS IN THE SOFTWARE.
+
+                For more information, please refer to <http://unlicense.org/>
+                @licend
+                */
+                converse.initialize({
+                    auto_away: 300, //absent au bout de 5minutes
+                    auto_list_rooms: true,
+                    auto_reconnect: true,
+                    auto_xa: 600, //absence prolongée au bout de 10minutes
+                    bosh_service_url: 'https://chat.exemple.fr/http-bind/',
+                    csi_waiting_time: 60,
+                    enable_smacks: true,
+                    i18n: 'fr', // pour avoir l'interface en français
+                    locked_domain: exemple.fr, // on verouille le domaine autoriséà se connecter
+                    message_archiving: 'always',
+                    persistent_store: 'IndexedDB', // jcbrand a dit qu'en 8.0.0 
+                    play_sounds: true,
+                    // ça va aller plus vite avec l'IndexedDB
+                    theme: 'concord',
+                    view_mode: 'fullscreen',
+                    websocket_url: 'wss://chat.exemple.fr/xmpp-websocket',
+                });
+            </script>
+    </body>
+</html>
+

+ +

C'est presque la fin de ce guide ! On va maintenant créer les +fichiers de configuration nginx.
+Créons /etc/nginx/sites-enabled/exemple.fr.conf pour +commencer: +

+server {
+    listen 80;
+    server_name exemple.fr;
+
+    location / {
+        return 301 https://$host$uri;
+    }
+}
+
+server {
+    listen 443 ssl;
+    server_nam  exemple.fr;
+    ssl_certificate /etc/letsencrypt/live/exemple.fr/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/exemple.fr/privkey.pem;
+
+    # this block is for the prosody's http_altconnect module
+    location ~* host-(meta|meta\.json) {
+        proxy_pass http://example.com:5280$uri;
+        proxy_http_version 1.1;
+    }
+}
+

+ +

Ensuite, créez +/etc/nginx/sites-enabled/f.exemple.fr.conf, ce sera pour le +composant +qui gérera les pièces jointes: +

+perl_modules /usr/local/lib/perl; # Path to upload.pm.
+perl_require upload.pm;
+
+server {
+    listen 80;
+    server_name f.exemple.fr;
+    location / {
+        return 301 https://$host$request_uri;	
+    }
+}
+
+server {
+    # Specify directives such as "listen", "server_name", and TLS-related
+    # settings for the "server" that handles the uploads. 
+    listen 443 ssl http2;
+    server_name f.exemple.fr;
+
+    ssl_certificate /etc/letsencrypt/live/f.exemple.fr/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/f.exemple.fr/privkey.pem; 
+    # Uploaded files will be stored below the "root" directory. To minimize
+    # disk I/O, make sure the specified path is on the same file system as
+    # the directory used by Nginx to store temporary files holding request
+    # bodies ("client_body_temp_path", often some directory below /var).
+    root /var/www/upload;
+    index index.html;
+    # Specify this "location" block (if you don't use "/", see below):
+    location / {
+        perl upload::handle;
+    }
+
+    # Upload file size limit (default: 1m), also specified in your XMPP
+    # server's upload module configuration (see below):
+    client_max_body_size 100m;
+}
+
+

Exécutez nginx -t pour tester la configuration. Si +aucune erreur n'est détectée, créer le fichier +/etc/nginx/sites-enabled/chat.exemple.fr.conf: +

+server {
+    listen 80;
+    server_name chat.exemple.fr;
+
+    location / {
+        return 301 https://$host$uri;
+    }
+}
+
+server {
+    listen 443 ssl http2;
+    ssl_certificate /etc/letsencrypt/live/chat.exemple.fr/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/chat.exemple.fr/privkey.pem;
+    root /var/www/chat.exemple.fr;
+    index index.html;
+
+    # XMPP BOSH
+    location ^~ /http-bind {
+        proxy_pass https://exemple.fr:5281/http-bind;
+        proxy_http_version 1.1;
+        proxy_set_header Host exemple.fr;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP HTTP-Upload
+    location ^~ /upload {
+        proxy_pass https://f.exemple.fr; proxy_http_version 1.1;
+        proxy_set_header Host exemple.fr;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP Websockets
+    location /xmpp-websocket {
+        proxy_pass http://exemple.fr:5280/xmpp-websocket;
+        proxy_http_version 1.1;
+        proxy_buffering off;
+        proxy_set_header Connection "Upgrade";
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_read_timeout 900s;
+    }
+
+    # XMPP Account invite
+    location ^~ /invite {
+        proxy_pass https://exemple.fr:5281/invite;
+        proxy_http_version 1.1;
+        proxy_set_header Host exemple.fr;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP account register
+    location ^~ /register {
+        proxy_pass https://exemple.fr:5281/register;
+        proxy_http_version 1.1;
+        proxy_set_header Host exemple.fr;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # sur mon vps debian j'ai eu besoin de ça pour
+    # que les pages d'invitation soit bien
+    # formatées
+    location = /share/bootstrap4/css/bootstrap.min.css {
+        alias /usr/lib/nodejs/bootstrap/dist/css/bootstrap.min.css; 
+    }
+
+    location = /share/jquery/jquery.min.js {
+        alias /usr/lib/nodejs/jquery/dist/jquery.min.js;
+    }
+
+    location = /share/bootstrap4/js/bootstrap.min.js {
+        alias /usr/lib/nodejs/bootstrap/dist/js/bootstrap.min.js;
+    }
+
+}
+
+

Exécutez de nouveau nginx -t pour détecter +d'éventuels problèmes de configuration. Une fois que tout +est OK, ajoutez la ligne 127.0.0.1 exemple.fr +dans le fichier /etc/hosts.
+Redémarrez le serveur Prosody et Nginx: +

+systemctl start prosody nginx
+# ou
+prosodyctl start && nginx -s reload
+

+ +

7. Connexion à l'interface web:

+

Rendez-vous maintenant sur la page web du chat: pour notre +guide ça serait https://chat.exemple.fr +, adaptez avec ce que vous avez.
+Vous devriez voir quelque chose comme ça:
+ + +

Petite astuce: si vous voulez vous connecter avec l'utilisateur +vous@exemple.fr vous pouvez omettre la partie droite +de votre adresse, comme ceci:
+ + +

C'est grâce à la configuration de +locked_domain lors de l'initialisation de Converse.js.
+Une fois connecté, l'interface ressemblera a ceci:
+ + +

Vous pouvez alors commencer à utiliser XMPP et rejoindre ou +créer des salons, ajouter des contacts et bien sûr discuter depuis +cette interface web !

+ +

+ +

La chose qui nous intéresse surtout c'est la création +de liens d'invitation accessible depuis le Web.
+Pour ce faire, cliquez gauche sur la roue dentée en haut à gauche +à côté de votre nom, cliquez sur Commandes. +Dans le champ "Sur quelle entité voulez-vous lancer des +commandes ?" rentrez le nom de votre serveur XMPP, puis cliquez sur +"Lister les commandes disponibles". Vous devez avoir quelque chose +de similaire à ceci:
+
+ +

Descendez un peu jusqu'à voir "Create new contact invite" +et cliquez dessus:
+
+Une invitation s'est créée ! Vous pouvez alors copier le texte +contenu dans "Invite web page" et l'envoyer à l'un de vos amis, +proches, etc...
+Une fois ouvert, le lien amène sur une page de ce type:
+
+

La page détecte automatiquement votre plateforme et vous propose +des applications XMPP natives en fonction. Dans le cas ou la plateforme +est un smartphone, ça aménera sur la page de +téléchargement de l'application puis ouvrira celle-ci tout +en continuant l'inscription.
+Le lien entouré de rouge +en bas avec le texte "register an account manually" permet de +s'inscrire via un formulaire web. Pratique pour juste créer le +compte et le tester plus tard. Voici à quoi ça +ressemble:
+
+

Vous pouvez voir que j'ai commencé a remplir le formulaire: une +fois l'inscription envoyée en cliquant sur le bouton +"Submit", vous serez accueilli par l'écran +suivant:
+
+

Une page vous confirmant votre inscription s'affiche, avec la +possibilité de montrer de nouveau le mot de passe saisie lors de +l'inscription ou cas où (le bouton "Show").
+Le bouton entoûré de rouge, "Log in via web", +permettra +à l'utilisateur inscrit de se connecter à l'interface web +configurée pendant ce tutoriel, et il vous aura en tant que +contact.
+
+ +

9. Remarques

+

Le guide se finit ici! Je reviendrais pour corriger quelques fautes, +étourderies et oublis si j'en aperçois. +J'ai des remarques à faire sur le logiciel: +

+ +

10. +Documentations

+

Ces liens m'ont bien aidé: +

+ +

11. +Remerciements

+

Tout ceci n'aurait pas été possible sans les logiciels +libres et leurs collaborateurs. Merci à vous.
+Plus spécifiquement: +

    +
  1. laxu.de, l'auteur de + https://blog.laxu.de/2018/09/08/conversejs-prosody/, pour le patch de + net/http/server.lua et les bouts de configuration nginx
  2. +
  3. qorg pour son guide sur XMPP, plus + particulièrement l'"HTTP upload" + (https://kill-9.xyz/guides/xmpp_server)
  4. +
  5. jcbrand, l'auteur et + développeur principal de Converse.js. Très réactif aux + questions concernant XMPP et Converse.js dans le salon discuss@conference.conversejs.org.
  6. +
  7. Holger Weiß, + développeur travaillant sur le serveur XMPP ejabberd et admin du salon + ejabberd@conference.process-one.net + qui a pris de son temps pour me dépanner (le + problème en question).
  8. +

+ +

À bientôt, passez une excellente journée et si vous +avez des questions, remarques, suggestions ou améliorations +à me faire part, n'hésitez pas à me contacter via +mail ou XMPP à l'adresse lionel ( @ ) les-miquelots ( . ) +net. +

+ + + diff --git a/index.html b/index.html new file mode 100755 index 0000000..ab6b6e2 --- /dev/null +++ b/index.html @@ -0,0 +1,855 @@ + + + +Setting up a XMPP server with a web client (converse.js+nginx+prosody on Debian) + + + + +

Setting up a XMPP server with a web client (converse.js+nginx+prosody on Debian)

+[linkstandalone] +

27/05/2021: add the http_altconnect module to prosody config to avoid errors whining about /.well-known/host-meta not being present
+

22/05/2021: don't enable OMEMO by default, we let the users make its own choices and it can be better +for multi-device history. I improved the apt command too (with -y --no-install-recommends).

+ +

Hi everyone,

+

Today we're going to install a XMPP server with a web client that is capable to +send invites links to ease the onboarding of your friends and family.
+I won't explain what is XMPP in great details, because it's outside the scope of this +guide and some people will explain faaar better than me. I'm thinking about these +articles on goffi.org (french blog). For english readers, the official wiki is a good start.
+tl;dr: it's a protocol made of various standards and it's possible to build + secure decentralized instant messaging networks with it. There are possibilities + to create public and private rooms too, like any popular modern instant messaging software. +

+ +

+ Table of matters: +
    +
  1. Prerequisites
  2. +
  3. DNS zone configuration
  4. +
  5. Firewall configuration
  6. +
  7. Packages setup
  8. +
  9. Certificates setup
  10. +
  11. Packages configuration
  12. +
  13. Discovering the webclient
  14. +
  15. Invite links generation
  16. + +
  17. Remarks
  18. +
  19. Documentations
  20. +
  21. Thanks
  22. + + +
+
+

+ +

1. +Prerequisites

+ +

Enough talking, this is what we're going to install: +


+ +

Agree on a domain name: you'll replace mentions of example.com in this +guide by your chosen domain name. You'll also need subdomains: one for the web client, chat.example.com +, one for groups chats, salons.example.com, and finally another for uploading files f.example.com.
+Don't forget to add type A records for these subdomains too. +

+ +

2. DNS zone configuration

+

First, stay on the DNS zone configuration of your registrar, we'll add some SRV and TXT type entries +to ease XMPP clients and servers communications with us (more details about this +on the XMPP wiki or prosody docs): +

+_xmpp-client._tcp.example.com 86400 SRV 1 1 5222 example.com.
+_xmpps-client._tcp.example.com 86400 SRV 1 1 5223 example.com.
+_xmpp-server._tcp.example.com 86400 SRV 1 1 5269 example.com.
+_xmppconnect.example.com TXT _xmpp-client-xbosh=https://chat.example.com/http-bind
+_xmppconnect.example.com TXT _xmpp-client-websocket=wss://chat.example.com/xmpp-websocket
+
+ +

NB: the left example.com is the XMPP domain, the one +on the right is the server that'll actually answer requests and you'll need a +type A record for it, not a CNAME. These records can come in handy when you want +"clean" users adresses like you@example.com but example.com is used for something +else, for example a website or mailserver, and the XMPP server is set up on xmpp.example.com .
+If that's the case, change the example.com on the right to xmpp.example.com.

+ + +

3. Firewall configuration

+

IMPORTANT:Log in as root before running the commands in this tutorial. +We'll then configure the iptables rules to avoid eating our fingers +in rage and confusion later. I'm assuming your default iptables policy is set to +DROP and you have some rules set allowing you to SSH into your server: +

+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5222 -j ACCEPT # client<=>server comms
+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5223 -j ACCEPT # same as above but for encrypted ones
+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5269 -j ACCEPT # server<=>servers comms
+iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT # for access to webclient
+iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT # HTTPS
+iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT # DNS requests
+iptables -A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT # useful for updating packages and letsencrypt
+iptables -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT # same as above but we HTTPS
+
+iptables-save > /etc/iptables/iptables.rules
+
+
+# pareil pour l'ipv6
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5222 -j ACCEPT
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5223 -j ACCEPT
+ip6tables -A INPUT -p tcp -m state --state NEW -m tcp --dport 5269 -j ACCEPT
+ip6tables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
+ip6tables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
+ip6tables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
+ip6tables -A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
+ip6tables -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
+
+ip6tables-save > /etc/iptables/ip6tables.rules
+systemctl enable --now iptables # enable iptables services so rules persists after reboots
+

+ +

4. Packages setup

+ +

Next, install prosody from + sources or +your package manager. +Two birds one stone, we'll install other needed packages too: +

sudo apt-get install -y --no-install-recommends prosody nginx-full \ 
+certbot python3-certbot-nginx \
+mercurial libjs-bootstrap4 libjs-jquery

+ +

We'll now clone the prosody-modules repo from source. Some distro package managers haven't always this one up to date (looking at you, Debian, stable branch). Execute the following: +

+hg clone https://hg.prosody.im/prosody-modules/ /usr/lib/prosody/modules/
+
+

And here below is an updating script that you can put on your /etc/cron.daily/ folder if it exists, or root crontab. On my server, I created the following file at /usr/local/sbin/maj_prosodymods.sh: +

+#!/bin/sh
+MODDIR="/usr/lib/prosody/modules/"
+
+if test -d $MODDIR; then
+    cd $MODDIR
+    hg pull --update
+else
+    mkdir -p /usr/lib/prosody
+    hg clone https://hg.prosody.im/prosody-modules/ $MODDIR
+fi
+
+

Don't forget to flip the executable bit with chmod +x... +

+ +

Then, we'll download converse.js. +On Debian Buster as of 23-05-2021 there isn't an official +package for it, so we'll whip up another script that we can +put in a crontab. On my server, I created the file /usr/local/sbin/update_conversejs.sh with the following: +

+#!/bin/sh
+TEMPDIR="$(mktemp -d)"
+LOG=/var/log/update_conversejs.log # update log path
+WWWDIR='/var/www/chat.example.com' # where static and js files will be downloaded
+WWWUSER='www-data' # this value will differ on the
+                   # distro you're using: it'll be 'http' for
+                   # Arch Linux nginx for example.
+
+mkdir -p $WWWDIR/dist
+cd $TEMPDIR
+printf "\n\n$(date) - INFO - Starting updating conversejs..." | tee -a $LOG
+CURL_ERR=$(curl -s \ # get latest release tarball
+    https://api.github.com/repos/conversejs/converse.js/releases/latest | \
+    grep -o "https://.*\.tgz" | \
+    grep converse\.js- | \
+    xargs curl -fsOJL) || \
+    (printf "\n$(date) - ERR - Updating conversejs failed." | tee -a $LOG && exit)
+
+# download libsignal for OMEMO support in conversejs webclient
+if test -e "$WWWDIR/dist/libsignal-protocol.min.js"; then
+    printf "\n$(date) - TOK - Libsignal already installed, skipping." | tee -a $LOG
+else
+    curl -fsOJL \
+    https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js || \
+    (printf "\n$(date) - ERR - Updating libsignal-protocol-javascript failed." | \
+    tee -a $LOG)
+    cp libsignal*.js $WWWDIR/dist/
+fi
+
+tar xzf *.tgz
+cp -rf package/dist  $WWWDIR/
+sed "s/fullscreen\.html/index\.html/g" package/manifest.json > $WWWDIR/manifest.json
+chown $WWWUSER:$WWWUSER -R $WWWDIR/
+chmod 755 -R $WWWDIR/
+rm -rf $TEMPDIR
+printf "\n$(date) - TOK - Done." | tee -a $LOG
+
+

Execute this script after fitting it to your taste, it'll make you +save time for step 6 because you won't have to create the $WWWDIR + +

5. Certificates setup

+ +

It's time to launch certbot to get our certificates. +I'm assuming and I hope that you already have set up A records for +the main domain example.com and subdomains too (chat.example.com, f.example.com and salons.example.com) in your registrar DNS zone: +

certbot certonly --agree-tos --nginx --deploy-hook "prosodyctl --root cert import /etc/letsencrypt/live" -d chat.example.com -d example.com -d f.example.com -d salons.example.com 
+

Once it executes successfully, the certificates should be in +/etc/letsencrypt/live. +

+ + +

6. Packages configuration

+ +

It's time we dive into prosody config files.
+Open the file /usr/lib/prosody/net/http/server.lua, we'll modify it +in order to ease connecting nginx with the http interface of prosody (BOSH). In the +file, find the following line: +

+headers = { date = date_header, connection = response_conn_header };
+
+

and replace it by: +

+headers = { date = date_header, connection = response_conn_header, 
+            access_control_allow_origin = "example.com" };
+-- if 'example.com' dont fix it, replace it by '*'. Remember that your
+-- XMPP server domain name goes here.
+

+ +

Then, we'll configure the script that'll allow us to send files to others. +Execute this: +

+mkdir -p /var/www/upload
+chown www-data:www-data /var/www/upload # l'utilisateur de nginx peut
+                                        # différer selon la distrib, faites gaffe
+mkdir -p /usr/local/lib/perl
+wget -O /usr/local/lib/perl/upload.pm https://raw.githubusercontent.com/weiss/ngx_http_upload/master/upload.pm
+
+

Open the downloaded file upload.pm and search for +a line like this: my $external_secret = 'it-is-secret'. +Replace it-is-secret by a strong passphrase without backslashes +or quotes to avoid crashing the script due to syntax error later.
+Save or write it down somewhere, it'll be useful very soon. +

+ +

Let's now get to write the prosody configuration file +/etc/prosody/prosody.cfg.lua with the following: +

+admins = { "you@example.com" }
+
+-- For more information see: https://prosody.im/doc/libevent
+-- use_libevent = true
+
+plugin_paths = { "/usr/lib/prosody/modules" }
+
+modules_enabled = {
+
+    -- Generally required
+    "roster"; -- Allow users to have a roster. Recommended ;)
+    "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+    "tls"; -- Add support for secure TLS on c2s/s2s connections
+    "dialback"; -- s2s dialback support
+    "disco"; -- Service discovery
+
+    -- Not essential, but recommended
+    "carbons"; -- Keep multiple clients in sync
+    "carbons_copies";
+    "carbons_copies_adhoc";
+    "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
+    "private"; -- Private XML storage (for room bookmarks, etc.)
+    "blocklist"; -- Allow users to block communications with other users
+    "vcard4"; -- User profiles (stored in PEP)
+    "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
+    "smacks";
+    "bookmarks"; -- old module but compatible with most clients
+    --"bookmarks2";
+    "presence"; -- see user status (online, offline, etc)
+    "offline";
+
+    -- Nice to have
+    "version"; -- Replies to server version requests
+    "uptime"; -- Report how long server has been running
+    "time"; -- Let others know the time here on this server
+    "ping"; -- Replies to XMPP pings with pongs
+    "register"; -- Allow users to register on this server using a client and change passwords
+    "mam"; -- Store messages in an archive and allow users to access it
+    "csi";
+    "csi_simple"; -- Simple Mobile optimizations
+    "csi_battery_saver";
+    "vjud"; -- recherche d'utilisateurs dans les salons
+
+    -- Admin interfaces
+    "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
+
+    -- HTTP modules
+    "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
+    "websocket"; -- XMPP over WebSockets
+    "http_altconnect"; -- BOSH and WebSocket connection endpoints discoverable via HTTP
+
+    -- Other specific functionality
+    "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+    "limits"; -- Enable bandwidth limiting for XMPP connections
+    "groups"; -- Shared roster support
+    "server_contact_info"; -- Publish contact information for this service
+    "announce"; -- Send announcement to all online users
+    "welcome"; -- Welcome users who register accounts
+    "watchregistrations"; -- Alert admins of registrations
+    "motd"; -- Send a message to users when they log in
+    --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
+    --"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
+}
+
+-- These modules are auto-loaded, but should you want
+-- to disable them then uncomment them here:
+modules_disabled = {
+    -- "offline"; -- Store offline messages
+    -- "c2s"; -- Handle client connections
+    -- "s2s"; -- Handle server-to-server connections
+}
+
+motd_text = [[Hi everyone ! Welcome to my XMPP server. Have an happy chat !]]
+welcome_message = "It seems it's your first logging in, $username. Welcome and enjoy your stay at $host."
+
+daemonize = false;
+pidfile = "/run/prosody/prosody.pid";
+trusted_proxies = { "127.0.0.1", "::1" }
+
+-- Force certificate authentication for server-to-server connections
+c2s_require_encryption = true -- client to server require encryption
+s2s_require_encryption = true -- server to server require encryption
+s2s_secure_auth = true 
+authentication = "internal_hashed"
+
+-- mam settings
+archive_expires_after = "never" -- permanent message history
+
+log = {
+    -- Log files (change 'info' to 'debug' for debug logs):
+    info = "/var/log/prosody/prosody.log";
+    error = "/var/log/prosody/prosody.err";
+    -- Syslog:
+    { levels = { "error" }; to = "syslog";  };
+}
+
+-- http and certificate shenanigans
+certificates = "certs"
+
+-- Include "conf.d/*.cfg.lua"
+
+legacy_ssl_ports =  { 5223 }
+-- http_ports =  { 5280 }
+-- http_interface = { "*" }
+-- https_ports = { 5281 }
+-- https_interfaces { "*" }
+
+
+cross_domain_bosh = { "https://chat.example.com" }
+cross_domain_websocket = { "https://chat.example.com" }
+consider_bosh_secure = true
+consider_websocket_secure = true
+allow_registration = true -- needed for mod_invites
+registration_invite_only = true -- registration is only permitted through invites
+vjud_mode = "opt-in" -- public search is opt-in
+
+-- https://prosody.im/security/advisory_20210512/
+gc = {
+    speed = 500;
+}
+c2s_stanza_size_limit = 256 * 1024
+s2s_stanza_size_limit = 512 * 1024
+
+limits = {
+    c2s = {
+        rate = "10kb/s";
+    };
+    s2sin = {
+        rate = "3kb/s";
+    };
+}
+-- https://prosody.im/security/advisory_20210512/
+
+ssl = { 
+    key = "certs/example.com.key";
+    certificate = "certs/example.com.crt";
+}
+
+VirtualHost "example.com"
+    invites_page = "https://chat.example.com/invite?{invite.token}"
+    webchat_url = "https://chat.example.com/"
+    http_external_url = "https://chat.example.com/"
+    invite_expiry = 86400 * 7
+    http_paths = {
+        invites_page = "/invite";
+        invites_register_web = "/register";
+    }
+
+    modules_enabled = {
+        "invites";
+        "invites_adhoc";
+        "invites_page";
+        "invites_register";
+        "invites_register_web";
+        "http_libjs";
+    }
+
+    contact_info = {
+        abuse = { "mailto:you@example.com", "xmpp:you@example.com" };
+        admin = { "mailto:you@example.com", "xmpp:you@example.com" };
+        security = { "mailto:you@example.com", "xmpp:you@example.com" };
+        support = { "mailto:you@example.com", "xmpp:you@example.com" };
+    };
+
+    https_certificate = "certs/example.com.crt";
+    ssl = { 
+        key = "certs/example.com.key";
+        certificate = "certs/example.com.crt";
+    }
+
+    Component "f.example.com" "http_upload_external"
+        http_upload_external_base_url = "https://f.example.com/"
+        http_upload_external_secret = "its-a-secret"
+        http_upload_external_file_size_limit = 104857600 -- limite de à 100Mo pour les envois de pjs
+        ssl = { 
+            key = "certs/f.example.com.key";
+            certificate = "certs/f.example.com.crt";
+        }
+
+    Component "salons.example.com" "muc"
+        name = "Salons (chatrooms) chez example.com"
+        modules_enabled = { "muc_mam", "vcard_muc" }
+        muc_room_default_language = "fr"
+        muc_log_expires_after = "never" -- perm hist for gcs
+        log_all_rooms = true
+        muc_log_by_default = true
+        muc_log_presences = false
+        restrict_room_creation = "admin" -- only admin can create gcs
+        ssl = { 
+            key = "certs/salons.example.com.key";
+            certificate = "certs/salons.example.com.crt";
+        }
+
+

As you can guess the its-a-secret value there must be replaced +by the passphrase you wrote down earlier.
+Some of you probably have noticed that the user you@example.com +was listed as an admin, so let's create him with the following command line: +

+prosodyctl check # checks config file
+prosodyctl adduser you@example.com # adapt to your username@domain
+

+ +

We'll now start to configure our web client, converse.js. Create + /var/www/chat.example.com/index.html with the following text: +

+<!DOCTYPE html>
+<html class="no-js" lang="en">
+    <head>
+        <title>Converse</title>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+        <meta name="description" content="Converse XMPP/Jabber Chat"/>
+        <meta name="keywords" content="xmpp chat webchat converse.js" />
+        <link rel="manifest" href="/manifest.json">
+        <link type="text/css" rel="stylesheet" media="screen" href="/dist/converse.min.css" />
+        <script src="/dist/libsignal-protocol.min.js"></script>
+        <script src="/dist/converse.min.js"></script>
+    </head>
+    <body class="converse-fullscreen">
+        <noscript>You need to enable JavaScript to run the Converse.js chat app.</noscript>
+        <div id="conversejs-bg"></div>
+            <script>
+                /*
+                @licstart
+                This is free and unencumbered software released into the public domain.
+
+                Anyone is free to copy, modify, publish, use, compile, sell, or
+                distribute this software, either in source code form or as a compiled
+                binary, for any purpose, commercial or non-commercial, and by any
+                means.
+
+                In jurisdictions that recognize copyright laws, the author or authors
+                of this software dedicate any and all copyright interest in the
+                software to the public domain. We make this dedication for the benefit
+                of the public at large and to the detriment of our heirs and
+                successors. We intend this dedication to be an overt act of
+                relinquishment in perpetuity of all present and future rights to this
+                software under copyright law.
+
+                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+                EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+                MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+                IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+                ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+                OTHER DEALINGS IN THE SOFTWARE.
+
+                For more information, please refer to <http://unlicense.org/>
+                @licend
+                */
+                converse.initialize({
+                    auto_away: 300, // in seconds
+                    auto_list_rooms: true,
+                    auto_reconnect: true,
+                    auto_xa: 600, // extended away, in seconds
+                    bosh_service_url: 'https://chat.example.com/http-bind/',
+                    csi_waiting_time: 60, // smartphone battery optimizations
+                    enable_smacks: true,
+                    locked_domain: example.com, // xmpp server that the client will only be allowed to connect to
+                    message_archiving: 'always',
+                    persistent_store: 'IndexedDB', // jcbrand said it'll be fast in 8.0.0 and better than localStorage
+                    play_sounds: true,
+                    theme: 'concord', // dark theme
+                    view_mode: 'fullscreen',
+                    websocket_url: 'wss://chat.example.com/xmpp-websocket', // can be unstable, if you got problems
+                    // don't hesitate to comment it
+                });
+            </script>
+    </body>
+</html>
+

+ +

This guide is almost done ! We now need to do the nginx config files.
+Let's start with /etc/nginx/sites-enabled/example.com.conf: +

+server {
+    listen 80;
+    server_name example.com;
+
+    location / {
+        return 301 https://$host$uri;
+    }
+}
+
+server {
+    listen 443 ssl;
+    server_nam  example.com;
+    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
+
+    proxy_set_header Access-Control-Allow-Origin 'chat.example.com';
+
+    # this block is for the prosody's http_altconnect module
+    location ~* host-(meta|meta\.json) {
+        proxy_pass http://example.com:5280$uri;
+        proxy_http_version 1.1;
+    }
+}
+

+ +

Next, let's continue with + /etc/nginx/sites-enabled/f.example.com.conf, it's the config file for + uploading files: +

+perl_modules /usr/local/lib/perl; # Path to upload.pm.
+perl_require upload.pm;
+
+server {
+    listen 80;
+    server_name f.example.com;
+    location / {
+        return 301 https://$host$request_uri;	
+    }
+}
+
+server {
+    # Specify directives such as "listen", "server_name", and TLS-related
+    # settings for the "server" that handles the uploads. 
+    listen 443 ssl http2;
+    server_name f.example.com;
+
+    ssl_certificate /etc/letsencrypt/live/f.example.com/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/f.example.com/privkey.pem; 
+    # Uploaded files will be stored below the "root" directory. To minimize
+    # disk I/O, make sure the specified path is on the same file system as
+    # the directory used by Nginx to store temporary files holding request
+    # bodies ("client_body_temp_path", often some directory below /var).
+    root /var/www/upload;
+    index index.html;
+    # Specify this "location" block (if you don't use "/", see below):
+    location / {
+        perl upload::handle;
+    }
+
+    # Upload file size limit (default: 1m), also specified in your XMPP
+    # server's upload module configuration (see below):
+    client_max_body_size 100m;
+}
+
+ +

Execute nginx -t to test the config files created so far. +Once no errors are detected create + /etc/nginx/sites-enabled/chat.example.com.conf: +

+server {
+    listen 80;
+    server_name chat.example.com;
+
+    location / {
+        return 301 https://$host$uri;
+    }
+}
+
+server {
+    listen 443 ssl http2;
+    ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
+    root /var/www/chat.example.com;
+    index index.html;
+
+    # XMPP BOSH
+    location ^~ /http-bind {
+        proxy_pass https://example.com:5281/http-bind;
+        proxy_http_version 1.1;
+        proxy_set_header Host example.com;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP HTTP-Upload
+    location ^~ /upload {
+        proxy_pass https://f.example.com; proxy_http_version 1.1;
+        proxy_set_header Host example.com;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP Websockets
+    location /xmpp-websocket {
+        proxy_pass http://example.com:5280/xmpp-websocket;
+        proxy_http_version 1.1;
+        proxy_buffering off;
+        proxy_set_header Connection "Upgrade";
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_read_timeout 900s;
+    }
+
+    # XMPP Account invite
+    location ^~ /invite {
+        proxy_pass https://example.com:5281/invite;
+        proxy_http_version 1.1;
+        proxy_set_header Host example.com;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # XMPP account register
+    location ^~ /register {
+        proxy_pass https://example.com:5281/register;
+        proxy_http_version 1.1;
+        proxy_set_header Host example.com;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_set_header X-Forwarded-For $remote_addr;
+        proxy_buffering off;
+        tcp_nodelay on; 
+    }
+
+    # sur mon vps debian j'ai eu besoin de ça pour
+    # que les pages d'invitation soit bien
+    # formatées
+    location = /share/bootstrap4/css/bootstrap.min.css {
+        alias /usr/lib/nodejs/bootstrap/dist/css/bootstrap.min.css; 
+    }
+
+    location = /share/jquery/jquery.min.js {
+        alias /usr/lib/nodejs/jquery/dist/jquery.min.js;
+    }
+
+    location = /share/bootstrap4/js/bootstrap.min.js {
+        alias /usr/lib/nodejs/bootstrap/dist/js/bootstrap.min.js;
+    }
+
+}
+
+

Execute again nginx -t to spot any troubles with your +config files. Once all is OK, add the line +

+# XMPP server domain name here, i.e it 
+# could be xmpp.example.com in your case
+127.0.0.1 example.com 
+
+ +

in your /etc/hosts.
+Restart nginx and prosody: +

+systemctl start prosody nginx
+# or
+prosodyctl start && nginx -s reload
+

+ +

7. Discovering the webclient

+ +

Let's see the results of our work! Open a web browser and navigate +to https://chat.example.com, YMMV. +You should see something like this: + + +

Small trick: if you want to login as +you@example.com, you can omit the right part of your adress like +this:
+ +

It's thanks to the +locked_domain parameter.
+ +Once logged in your view should be like this:
+ + +

You can then start to use XMPP and join or create group chats, add friends and chat +with them from there !

+ +

+ +

The really interesting thing is that we can create invite links from +Converse.js thanks to the admin_adhoc module.
+To do so, left click on the gear icon on the top left next to your +username, then click Commands. +In the 'On which entity do you want to run commands?' field, type +in your XMPP server domain name and left click 'List available commands'. +You should have something similar to this:
+
+ +

Scroll down a bit and left click on "Create new contact invite":
+
+ +

An invite link has been created ! You can now copy and send it to your +friends, family, etc...
+Once opened, the link will lead to this kind of page: +
+

The web page auto-detects your OS platform and will +suggest you compatible XMPP software to download. In the +case you're on a smartphone, the links will download the apps +on their respective stores and continue the registration +after opening thanks to the Invite URI.
+The text squared in red in the bottom left, 'register an account manually', +allows you to register via the web browser. It's handy when you want to create +an account and test it later. Here how it looks:
+
+ +

As you can see I started to fill in the form: once submitted succesfully, + you'll be greeted with this: +
+ +

A page confirming your registration is shown, with the possibility to view your chat adress and password again to +write them down somewhere. The user can then log in with +Converse.js by clicking on the "Log in via web" button. +Once logged in, you will be added to his roster (because we did a contact invite) and he can start to chat with you.
+
+ +

9. Remarks

+

The guide ends there! I'll get back from time to time to fix some typos and inconstencies when I spot them. +I've got some notes and remarks: +

+ + +

10. Documentations

+ +

Those links greatly helped me: +

+ + +

11. +Thanks

+ +

This wouldn't have been possible without free software and their contributors. Thanks you so much.
+To be more specific: +

    +
  1. laxu.de, author of + + https://blog.laxu.de/2018/09/08/conversejs-prosody/ which walks through the patching of net/http/server.lua and gives some related nginx configuration snippets
  2. +
  3. qorg for his XMPP guide, in particular the part dealing about mod_http_upload_external +
  4. jcbrand, creator and main developer of Converse.js. Very quick to respond to questions + relating to XMPP and Converse.js in discuss@conference.conversejs.org.
  5. +
  6. Holger Weiß, + a developer working on the ejabberd XMPP server and admin of + ejabberd@conference.process-one.net who took his time + to help me (problem and fix here).
  7. +

+ +

See you soon and have a great day ! If you have any questions, remarks, suggestions or improvements to point out, don't hesitate to message me via XMPP or email at lionel ( @ ) les-miquelots ( . ) net. +

+ + + diff --git a/styles.css b/styles.css new file mode 100755 index 0000000..4ca9777 --- /dev/null +++ b/styles.css @@ -0,0 +1,192 @@ +*{ + line-height:24px; +} + +img{ + padding:10px; + height:60%; + width:70%; +} + +#spinthonk{ + padding:1px; + height:50px; + width:50px; +} + +#gitimg, #rssimg, #rssimg2 { + height:24px; + width:24px; + padding:2px; + +} + +.pagetoplink { + position: fixed; + width: 20%; + bottom: 0; + left: 0; + padding: 0.5em; +} + + +#omemo_fpts { +margin-left:17%; +} + +#guide-sommaire{ + padding:5px; + border:1px solid black; +} + +body{ + background-color:beige; + color:#222222; + font-family: Sans-Serif; + font-style: normal; + font-weight: 375; + font-size:12pt; +} + +h1,h2,h3{ + line-height:30px; + text-align:center; + margin:auto; + width:70%; +} + +h2,h3{ + text-align:left; + margin-top:2.5%; +} + +p,table,pre,ol,ul,#guide-sommaire{ + color: #222222; + width:70%; + margin:auto; +} + +p{ +text-indent:3%; + +} + +#footnotes { + font-size:0.85em; +} + +pre{ + overflow:auto; + padding:10px; + border-radius:10px; + border:1px solid grey; + line-height:15px; + color:white; + background-color:#222236; + font-size:0.9em; +} + +pre, code{ + font-family:monospace; +} + +pre a { + color:cyan; +} + +table{ + width:85%; + padding:5px; +} + +td,tr{ + width:25%; + text-align:center; + padding:5px; +} + +td,th{ + border-bottom:1px solid black; +} + +th{ + border:2px solid #222222; + border-width:2px; +} + +li{ + width:90%; + margin:auto; +} + +a{ + color:teal; + text-decoration:none; +} + +a:hover{ + text-decoration:underline; +} + +.blogentry{ + width:65%; + margin:auto; +} + +.blogentry h2, .blogentry p{ + width:100%; +} + +footer{ + width:70%; + margin:auto; + padding-top:15px; +} + +.blogentry small, body small{ + width:100%; + display:block; + text-align:center; +} + + +blockquote{ + width:65%; + margin:auto; + padding:10px; + margin-top:10px; + margin-bottom:10px; + border-left:3px solid grey; + text-align:none; +} + +blockquote small{ + text-align:inherit; +} + +/*ripped from mozilla MDN, thx lol*/ +kbd { + background-color: #eee; + border-radius: 3px; + border: 1px solid #b4b4b4; + box-shadow: 0 1px 1px rgba(0, 0, 0, .2), 0 2px 0 0 rgba(255, + 255, 255, .7) inset; + color: #333; + display: inline-block; + font-size: .85em; + font-weight: 700; + line-height: 1; + padding: 2px 4px; + white-space: nowrap; +} +figcaption { + text-align: center; + font-style: italic; + font-size: 90%; +} + +@media screen and (max-width: 740px) { + #omemo_fpts { + margin-left:25%; + } +} -- cgit v1.2.3-54-g00ecf