====== Déploiement Jitsi Meet (paquets Debian) derrière Caddy ======
Guide d'installation d'une instance Jitsi Meet auto-hébergée pour réunions clients, sans Docker, avec Caddy en reverse proxy frontal et TURN configuré proprement.
* **Cible** : réunions clients en ligne (2 à ~20 participants)
* **OS** : Debian 12
* **Reverse proxy** : Caddy (gère le TLS)
* **FQDN d'exemple** : ''meet.makeitsimple.be''
===== 1. Prérequis =====
^ Élément ^ Détail ^
| FQDN | ''meet.makeitsimple.be'' pointant vers l'IP publique |
| Port 443/tcp | Web + WebSocket (via Caddy) |
| Port 10000/udp | Média JVB — **direct vers la VM**, ne passe PAS par Caddy |
| Port 5349/tcp+udp | TURN over TLS (fallback NAT restrictif) |
| Ressources | 4 vCPU / 4–8 Go RAM recommandés |
Le média audio/vidéo (10000/udp) et le TURN ne transitent **jamais** par Caddy. Caddy ne proxifie que le HTTP/WebSocket. C'est la source de confusion n°1.
===== 2. Préparation système =====
hostnamectl set-hostname meet.makeitsimple.be
# Vérifier /etc/hosts :
# 127.0.0.1 meet.makeitsimple.be meet
apt update && apt upgrade -y
apt install -y gnupg2 curl
===== 3. Dépôts Jitsi =====
curl -sL https://download.jitsi.org/jitsi-key.gpg.key \
| gpg --dearmor -o /usr/share/keyrings/jitsi-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/jitsi-keyring.gpg] https://download.jitsi.org stable/" \
> /etc/apt/sources.list.d/jitsi-stable.list
apt update
===== 4. Installation =====
apt install -y jitsi-meet
Pendant l'installation :
- **Hostname** : saisir ''meet.makeitsimple.be''
- **Certificat** : choisir **« I want to use my own certificate »** — c'est Caddy qui gère le TLS, il ne faut PAS laisser Jitsi tenter Let's Encrypt. On peut pointer vers un certificat existant ou bidon, il sera contourné.
Cette commande installe : ''jitsi-meet'' (web), ''jicofo'' (focus/orchestration), ''jitsi-videobridge2'' (JVB/média), ''prosody'' (XMPP), un **nginx local** et un **coturn local**.
===== 5. Basculer le web Jitsi en HTTP local =====
Par défaut nginx écoute en 443 et se battra avec Caddy. On le fait écouter en local uniquement.
Éditer ''/etc/nginx/sites-available/meet.makeitsimple.be.conf'' :
server {
listen 127.0.0.1:8000;
server_name meet.makeitsimple.be;
# ... conserver tout le reste de la conf générée par Jitsi ...
# (locations /, /http-bind, /xmpp-websocket, etc.)
}
* Faire écouter nginx sur ''127.0.0.1:8000'' uniquement.
* Supprimer / commenter les blocs ''listen 443 ssl'' et ''listen [::]:443 ssl''.
nginx -t && systemctl restart nginx
===== 6. Caddy en frontal =====
Dans le ''Caddyfile'' :
meet.makeitsimple.be {
reverse_proxy 127.0.0.1:8000
}
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy
Caddy récupère le certificat Let's Encrypt automatiquement et proxifie HTTP + WebSocket (BOSH/XMPP-WS) vers Jitsi.
===== 7. Configuration du média (JVB) =====
**Point de rupture le plus fréquent.** Le JVB doit annoncer les bonnes adresses IP pour la négociation ICE.
Éditer ''/etc/jitsi/videobridge/sip-communicator.properties'' :
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=
* **VM derrière NAT** : renseigner l'IP privée locale ET l'IP publique.
* **VM en IP publique directe** : les deux valeurs peuvent être identiques (l'IP publique).
Ouvrir **10000/udp** de l'extérieur vers la VM (pare-feu + NAT éventuel).
systemctl restart jitsi-videobridge2
===== 8. Configuration TURN (coturn) — TURN/UDP sans TLS =====
Le TURN sert de **relais média** quand un participant est derrière un NAT/firewall restrictif qui bloque l'UDP direct (10000/udp). Typiquement : **clients en réseau d'entreprise**. Sans TURN fonctionnel, ces participants se connectent mais n'ont **ni son ni vidéo**.
Cette section décrit un TURN **sans TLS**, en TURN/UDP sur le port **3478** uniquement. Avantage : **aucun certificat à gérer** côté coturn (pas de synchro depuis Caddy, pas de renouvellement à automatiser). Le TURN/UDP 3478 couvre la grande majorité des NAT restrictifs.
**Limite** : le TURN over TLS (5349) n'est utile que pour traverser les firewalls corporate les plus verrouillés, qui n'autorisent que le trafic HTTPS sortant. Ceux-là ne seront pas couverts par cette config. Si un client réel se retrouve bloqué malgré 3478/udp, il faudra ajouter un TURN/TLS 5349 (nécessite alors un certificat).
==== 8.1 Config coturn ====
Éditer ''/etc/turnserver.conf'' (généré par Jitsi). Vérifier / ajuster :
listening-port=3478
listening-ip=
relay-ip=
external-ip=/
# Auth partagée avec Jitsi
use-auth-secret
static-auth-secret=
realm=meet.makeitsimple.be
# Pas de TLS : on ne définit ni tls-listening-port, ni cert, ni pkey
no-tls
no-dtls
# Durcissement
no-tcp-relay
no-multicast-peers
no-cli
Le ''static-auth-secret'' est généré à l'install. Le récupérer dans ''/etc/turnserver.conf'' (existant) ou dans ''/etc/prosody/conf.d/meet.makeitsimple.be.cfg.lua'' (bloc ''turncredentials''). Commenter ou supprimer les lignes ''tls-listening-port'', ''cert'' et ''pkey'' générées par défaut si elles sont présentes.
==== 8.2 Pointer Jitsi vers le TURN/UDP ====
Vérifier dans ''/etc/prosody/conf.d/meet.makeitsimple.be.cfg.lua'' que le bloc ''turncredentials'' n'annonce **que** STUN et TURN/UDP sur 3478 (retirer toute entrée ''turns'' / 5349) :
turncredentials = {
{ type = "stun", host = "meet.makeitsimple.be", port = "3478" },
{ type = "turn", host = "meet.makeitsimple.be", port = "3478", transport = "udp" }
}
Ouvrir **3478/udp** de l'extérieur vers la VM.
systemctl restart coturn prosody
===== 9. Redémarrage complet =====
systemctl restart prosody jicofo jitsi-videobridge2 coturn nginx
systemctl reload caddy
===== 10. Récapitulatif pare-feu =====
^ Port ^ Proto ^ Sens ^ Rôle ^
| 443 | tcp | Externe → VM | Web + WebSocket (Caddy) |
| 80 | tcp | Externe → VM | ACME / redirection (Caddy) |
| 10000 | udp | Externe → VM | Média JVB (direct) |
| 3478 | udp | Externe → VM | STUN / TURN (relais fallback) |
===== 11. Checklist de test de bout en bout =====
- [ ] **DNS** : ''dig +short meet.makeitsimple.be'' renvoie bien l'IP publique.
- [ ] **HTTPS** : la page d'accueil Jitsi charge en ''https://'' sans alerte de certificat (Caddy).
- [ ] **Création de salle** : créer une réunion, la page se charge, caméra/micro détectés.
- [ ] **Test à 2 participants** sur réseaux différents (ex. un en 4G, un en fibre) → audio + vidéo OK dans les deux sens.
- [ ] **Test NAT restrictif** : un participant sur réseau d'entreprise / VPN corporate → valide que le TURN prend le relais (audio/vidéo présents).
- [ ] **Partage d'écran** fonctionnel.
- [ ] **Invité sans compte** : ouvrir le lien en navigation privée → accès direct sans login.
- [ ] **Vérif média** : sur ''https://webrtc.github.io/samples/'' ou via les stats Jitsi (bouton i), confirmer que la connexion passe en ''udp'' direct (host/srflx) et bascule en ''relay'' seulement si nécessaire.
Pour diagnostiquer un problème "je vois mais pas de son" : ouvrir les stats de connexion dans Jitsi (icône ''i'' en haut à gauche d'un participant). Si le type de connexion reste bloqué ou vide, c'est côté JVB (10000/udp) ou TURN.
===== 12. Vérification des logs =====
journalctl -u jitsi-videobridge2 -f # média
journalctl -u jicofo -f # orchestration des conférences
journalctl -u prosody -f # XMPP / auth
journalctl -u coturn -f # relais TURN
journalctl -u caddy -f # reverse proxy / TLS
===== 13. Restreindre la création de réunions (secure domain) =====
Objectif : seuls les **hôtes authentifiés** peuvent créer une réunion. Les invités rejoignent librement une salle **déjà ouverte** par un hôte, sans compte.
**Comportement :**
* Un invité qui tente de créer une salle → écran « en attente de l'hôte ».
* Un hôte se connecte (login/mot de passe) → la salle s'ouvre → les invités entrent.
==== 13.1 Prosody ====
Éditer ''/etc/prosody/conf.d/meet.makeitsimple.be.cfg.lua''.
Le VirtualHost principal passe en authentification interne :
VirtualHost "meet.makeitsimple.be"
authentication = "internal_hashed"
Ajouter un VirtualHost anonyme pour les invités (à créer s'il n'existe pas) :
VirtualHost "guest.meet.makeitsimple.be"
authentication = "anonymous"
c2s_require_encryption = false
''guest.meet.makeitsimple.be'' est un domaine **XMPP interne**, pas un vrai DNS : aucun enregistrement DNS ni certificat n'est requis pour lui.
==== 13.2 Jicofo ====
Éditer ''/etc/jitsi/jicofo/jicofo.conf'' :
jicofo {
authentication {
enabled = true
type = XMPP
login-url = "meet.makeitsimple.be"
}
}
==== 13.3 Config web ====
Éditer ''/etc/jitsi/meet/meet.makeitsimple.be-config.js'' :
var config = {
hosts: {
domain: 'meet.makeitsimple.be',
anonymousdomain: 'guest.meet.makeitsimple.be',
// ...
},
// ...
};
==== 13.4 Créer les comptes hôtes ====
prosodyctl register vincent meet.makeitsimple.be
* Un hôte = un compte.
* Révoquer un hôte : ''prosodyctl deluser vincent@meet.makeitsimple.be''
* Changer un mot de passe : ''prosodyctl passwd vincent@meet.makeitsimple.be''
==== 13.5 Redémarrer ====
systemctl restart prosody jicofo jitsi-videobridge2
==== 13.6 Test ====
- [ ] En navigation privée (non authentifié), créer une salle → écran « en attente de l'hôte ».
- [ ] Se connecter avec un compte hôte → la salle s'ouvre.
- [ ] Un second onglet anonyme rejoint la salle ouverte → accès direct sans login.
===== 14. Cacher le formulaire de la page d'accueil =====
Objectif : sur la racine ''meet.makeitsimple.be'', ne plus afficher le champ « nom de la salle » qui invite un passant à créer/rejoindre une réunion au hasard. Mesure **cosmétique** (elle n'empêche pas d'accéder à une salle dont on connaît l'URL — cf. mot de passe de salle / lobby pour ça), mais elle évite d'exposer le formulaire publiquement.
Éditer ''/etc/jitsi/meet/meet.makeitsimple.be-config.js'' et ajouter (ou décommenter) :
// Masque la zone de saisie / le bouton de création sur la page d'accueil
enableWelcomePage: false,
* ''enableWelcomePage: false'' : la page d'accueil avec le formulaire ne s'affiche plus. Une salle reste accessible directement par son URL (''https://meet.makeitsimple.be/NomDeLaSalle'').
Variante douce si tu veux garder une page d'accueil mais sans le champ de création aléatoire : conserver ''enableWelcomePage: true'' et désactiver la liste des salles récentes / le générateur de nom via ''hideAddRoomButton'' et un ''MEET_ROOM_NAME'' vide selon la version. La bascule à ''false'' reste la plus simple et la plus robuste.
Rediriger éventuellement la racine vers ta vitrine (au niveau Caddy) si tu veux que ''/'' ne montre rien du tout :
meet.makeitsimple.be {
@root path /
redir @root https://www.makeitsimple.be 302
handle {
reverse_proxy 127.0.0.1:8000
}
}
Aucun redémarrage de service nécessaire pour ''config.js'' (rechargé côté client) ; recharger Caddy si tu ajoutes la redirection :
systemctl reload caddy
===== 15. Personnalisation (logo, taille, favicon, nom) =====
==== 15.1 Remplacer le logo (watermark en haut à gauche) ====
Par défaut Jitsi utilise ''/usr/share/jitsi-meet/images/watermark.svg'' (ou ''.png'' selon version).
# Sauvegarde de l'original
cp /usr/share/jitsi-meet/images/watermark.svg /usr/share/jitsi-meet/images/watermark.svg.bak
# Déployer le logo (garder une copie source HORS du dossier packagé, cf. 15.5)
cp /opt/mis-branding/watermark.svg /usr/share/jitsi-meet/images/watermark.svg
==== 15.2 Lien, affichage et logo par défaut ====
Éditer ''/usr/share/jitsi-meet/interface_config.js'' :
var interfaceConfig = {
JITSI_WATERMARK_LINK: 'https://www.makeitsimple.be',
SHOW_JITSI_WATERMARK: true,
DEFAULT_LOGO_URL: 'images/watermark.svg',
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg',
// ...
};
==== 15.3 Modifier la taille du logo ====
La taille du watermark est **contrôlée en CSS**, pas par les dimensions du fichier image. Le watermark est affiché par la classe ''.watermark .leftwatermark''.
Créer un CSS de surcharge, ex. ''/usr/share/jitsi-meet/css/custom.css'' :
.watermark.leftwatermark {
width: 160px !important; /* défaut ~= 140px */
height: 60px !important;
background-size: contain !important;
background-repeat: no-repeat !important;
}
Puis référencer ce CSS dans ''/usr/share/jitsi-meet/base.html'' (juste avant '''') :
Ajuster ''width''/''height'' selon le ratio de ton logo. ''background-size: contain'' évite la déformation. Vider le cache navigateur pour voir le changement.
==== 15.4 Favicon, nom de l'application ====
Dans ''interface_config.js'' :
APP_NAME: 'Make IT Simple Meet',
NATIVE_APP_NAME: 'Make IT Simple Meet',
PROVIDER_NAME: 'Make IT Simple',
Favicon : remplacer ''/usr/share/jitsi-meet/images/favicon.ico''.
cp /opt/mis-branding/favicon.ico /usr/share/jitsi-meet/images/favicon.ico
==== 15.5 Préserver la personnalisation aux mises à jour ====
''interface_config.js'', ''base.html'', ''css/'' et ''images/'' appartiennent au paquet ''jitsi-meet-web''. Un ''apt upgrade'' **peut écraser** ces fichiers. Il faut versionner tes fichiers custom et les redéployer après upgrade.
Garder les sources de branding dans un dossier dédié (idéalement versionné en git) :
/opt/mis-branding/
├── watermark.svg
├── favicon.ico
├── custom.css
└── redeploy.sh
Script ''/opt/mis-branding/redeploy.sh'' :
#!/bin/bash
set -e
DST=/usr/share/jitsi-meet
cp /opt/mis-branding/watermark.svg "$DST/images/watermark.svg"
cp /opt/mis-branding/favicon.ico "$DST/images/favicon.ico"
cp /opt/mis-branding/custom.css "$DST/css/custom.css"
# Ré-injecter le lien CSS dans base.html s'il a été écrasé
grep -q 'css/custom.css' "$DST/base.html" || \
sed -i 's##\n#' "$DST/base.html"
echo "Branding redéployé."
chmod +x /opt/mis-branding/redeploy.sh
# À relancer après chaque mise à jour de jitsi-meet-web
/opt/mis-branding/redeploy.sh
Aucun redémarrage de service : les fichiers sont statiques (servis par nginx → Caddy) et rechargés côté navigateur. Vider le cache pour tester.
===== Notes de maintenance =====
* **TURN** : configuration en TURN/UDP sans TLS (section 8) → **aucun certificat coturn à gérer ni à renouveler**. Si un client tombe sur un firewall corporate bloquant l'UDP, prévoir l'ajout d'un TURN/TLS 5349 (nécessitera alors un certificat dédié, ex. via certbot en ''webroot'' ou DNS-01, indépendant de Caddy).
* **Personnalisation** : après chaque ''apt upgrade'' de ''jitsi-meet-web'', relancer ''/opt/mis-branding/redeploy.sh'' (section 15.5) pour restaurer logo/favicon/CSS.
* **Montée en charge** : au-delà de ~20–25 participants simultanés sur une salle, envisager un JVB supplémentaire (scaling horizontal).
* **Mises à jour** : ''apt update && apt upgrade'' — les paquets Jitsi sont mis à jour régulièrement, tester après upgrade majeur.