This repository has been archived on 2026-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
infra/stacks/matrix.poneyworld.net
Bertrand Benjamin 1c05fd3578 fix(stacks): coturn annonce la bonne IP et publie 3478 en UDP
`external-ip` pointait sur 91.121.90.228, l'adresse d'admin.opytex.org, alors
que la stack tourne sur chioggia (188.165.240.182) : coturn annonçait aux
clients une machine qui n'écoute rien sur ces ports.

3478 n'était par ailleurs publié qu'en TCP, alors qu'un client TURN tente
l'UDP en premier. Les deux défauts se cumulaient, le relais était inopérant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:48:23 +02:00
..
2026-08-19 17:10:23 +02:00
2026-08-19 17:10:23 +02:00
2026-08-19 17:10:23 +02:00
2026-08-19 17:10:23 +02:00
2026-08-19 17:10:23 +02:00

Matrix — matrix.poneyworld.net

Homeserver Synapse et ses satellites. Huit conteneurs :

Service Rôle Exposé sur
synapse le homeserver synapse.poneyworld.net (fédération)
db PostgreSQL 18
redis Valkey, cache interne de Synapse (bloc redis: du homeserver)
nginx sert /_matrix (vers synapse:8008) et /.well-known/matrix/ matrix.poneyworld.net (API client)
web Element Web chat.poneyworld.net
livekit SFU pour Element Call livekit.opytex.org
lk-jwt délivre les jetons LiveKit à Element livekit.opytex.org/sfu/get, /get_token, /healthz
coturn serveur TURN (appels 1-à-1) ports 3478 / 5349 / 49160-49200

Deux entrées différentes, à ne pas confondre : matrix.poneyworld.net est le server_name et l'API client, synapse.poneyworld.net est la cible de délégation annoncée par /.well-known/matrix/server, empruntée par les serveurs fédérés. Les deux sont vivantes.

Déploiement

Depuis ce dossier, sur chioggia :

sops exec-env secrets.env.yaml 'docker compose up -d'

Les secrets sont déclarés ${VAR:?} : toute commande docker compose échoue sans l'enveloppe sops exec-env, y compris stop, logs, ps et exec. Pour en enchaîner plusieurs, une seule enveloppe suffit :

sops exec-env secrets.env.yaml '
  docker compose stop synapse &&
  docker compose exec -T db psql -U synapse synapse -c "..." &&
  docker compose up -d
'

Après une rotation de secret, docker compose up -d répond « up to date » et garde l'ancienne valeur. Il faut --force-recreate sur le service concerné.

Le nom de projet

name: matrix_poneyworld, en tête du compose, ne suit pas le nom du dossier. C'est délibéré et c'est porteur : les volumes s'appellent matrix_poneyworld_matrix_data et matrix_poneyworld_matrix_db.

Retirer cette ligne, ou la modifier, rendrait invisibles les médias, les clés de signature du serveur, homeserver.yaml et toute la base — la stack repartirait sur un homeserver vide, sans la moindre erreur.

Où vit vraiment la configuration

Presque rien de la configuration de Synapse n'est dans ce dépôt : elle est dans /data/homeserver.yaml, à l'intérieur du volume matrix_data.

Les variables SYNAPSE_* de l'env_file ne sont lues que par le mode generate de l'image, au tout premier démarrage. Elles sont inertes au runtime : SYNAPSE_ENABLE_REGISTRATION=yes y cohabite sans conséquence avec un enable_registration: False effectif dans homeserver.yaml. Ne pas s'y fier pour lire l'état du serveur.

Corollaire pour les secrets : turn_shared_secret, macaroon_secret_key, registration_shared_secret et le mot de passe de la base sont dans homeserver.yaml, donc hors SOPS et hors dépôt. Une rotation se fait des deux côtés (voir plus bas).

Secrets

Cinq valeurs dans secrets.env.yaml, injectées par sops exec-env :

Variable Consommé par
POSTGRES_PASSWORD db
LIVEKIT_KEY, LIVEKIT_SECRET livekit (via LIVEKIT_KEYS) et lk-jwt
TURN_STATIC_AUTH_SECRET, TURN_CLI_PASSWORD coturn

LIVEKIT_KEY est un identifiant plus qu'un secret, et la convention du dépôt laisserait ce genre de valeur en clair. Il est chiffré quand même : il ne se tourne jamais seul, toujours en paire avec son secret, et le séparer sur deux mécanismes rendrait la rotation facile à rater à moitié.

sops edit secrets.env.yaml

Deux mécanismes différents pour deux logiciels

Ni coturn ni LiveKit ne lisent une variable d'environnement depuis leur fichier de configuration — un fichier monté n'est pas interpolé par Compose. C'est le même problème que la clé LAPI de CrowdSec sur Traefik, mais les deux n'offrent pas la même sortie.

LiveKit accepte la paire par variable d'environnement : LIVEKIT_KEYS, au format clé: secret. livekit.yaml n'a donc pas de bloc keys:, et la valeur est composée dans environment: du compose, où l'interpolation ${...} s'applique. lk-jwt consomme les deux mêmes variables.

coturn n'offre rien d'équivalent : pas d'interpolation dans turnserver.conf, pas d'option ...File, pas de lecture d'environnement. La seule voie sans bricolage est la ligne de commande :

command:
  - -c
  - /etc/coturn/turnserver.conf
  - --static-auth-secret=${TURN_STATIC_AUTH_SECRET:?}
  - --cli-password=${TURN_CLI_PASSWORD:?}

Le compromis est explicite : les valeurs ne sont pas dans un fichier versionnable, mais elles sont visibles dans docker inspect coturn et dans la table des processus de l'hôte. C'est strictement mieux qu'un fichier versionné, ce n'est pas aussi bon qu'un secret Compose.

turnserver.conf ne doit donc jamais reprendre de lignes static-auth-secret= ou cli-password= : ce serait à la fois un secret en clair dans le dépôt et une seconde source concurrente.

Rotation

turn_shared_secret ne se change pas seul : la même valeur doit être identique dans homeserver.yaml (Synapse la signe) et côté coturn (il la vérifie). Deux valeurs différentes ne produisent aucune erreur au démarrage — les appels échouent simplement à établir un relais.

sops edit secrets.env.yaml                             # TURN_STATIC_AUTH_SECRET
docker compose exec synapse vi /data/homeserver.yaml   # turn_shared_secret
sops exec-env secrets.env.yaml 'docker compose up -d --force-recreate coturn'
docker compose restart synapse

macaroon_secret_key (dans homeserver.yaml) signe les jetons d'accès : le changer déconnecte tous les utilisateurs, sur tous leurs appareils. Ce n'est pas une opération anodine, c'est une opération annoncée.

registration_shared_secret (dans homeserver.yaml) n'est utilisé que par register_new_matrix_user. Il se change librement.

POSTGRES_PASSWORD ne se change pas seul non plus : PostgreSQL ignore la variable sur un PGDATA déjà initialisé. Il faut un ALTER USER dans la base et la même valeur dans le bloc database: de homeserver.yaml.

Volumes et sauvegarde

Volume Contenu Sauvegarde
matrix_poneyworld_matrix_data médias, clé de signature du serveur, homeserver.yaml label backup.enable=true + MANUAL_VOLUMES
matrix_poneyworld_matrix_db base PostgreSQL dump SQL (backup.type=postgres sur db)

Les labels de volume ne s'appliquent qu'à la création : en ajouter ou en retirer un ne changerait rien au volume réel, mais ferait diverger sa déclaration, et Compose proposerait alors de le recréer. Ne pas toucher au bloc volumes: du compose.

Pourquoi POSTGRES_DB est indispensable

backup_docker.sh détermine la base à dumper ainsi :

db_name=$(docker exec "$container" printenv POSTGRES_DB 2>/dev/null || echo "postgres")

Sans POSTGRES_DB dans l'environnement du conteneur db, printenv sort en erreur, le repli s'applique, et le script dumpe postgres — la base système, vide — avec succès et sans le moindre avertissement. Le dump obtenu fait quelques centaines d'octets et ne contient aucune table.

POSTGRES_DB=synapse dans l'env_file est ce qui garantit que le dump porte sur la bonne base. La variable n'est lue par PostgreSQL qu'à l'initialisation du cluster : elle est sans effet sur une base existante, sa seule fonction ici est d'être visible par printenv.

Vérification, après tout changement touchant db :

zcat /other/backups/db-dumps/matrix_poneyworld.sql.gz | grep -c "CREATE TABLE"

Un 0 signifie que la sauvegarde est vide.

L'IP publique est écrite en dur, à deux endroits

coturn/turnserver.conf (external-ip) et livekit/livekit.yaml (node_ip) portent tous deux l'adresse publique de chioggia, 188.165.240.182. Les deux services tournent en bridge : ils ne voient qu'une IP privée et ne peuvent pas deviner celle qu'il faut annoncer aux clients.

Si la machine change d'adresse, ces deux fichiers doivent changer avec elle. Aucun des deux ne le signalera : coturn et LiveKit démarrent normalement en annonçant une adresse erronée, et seuls les appels échouent — sans erreur dans les logs, puisque du point de vue des serveurs tout va bien.

C'est déjà arrivé : external-ip a pointé pendant des mois sur 91.121.90.228, l'adresse d'admin.opytex.org.

Défauts connus

Trois images en tag flottant : nginx:latest, livekit/livekit-server:latest, ghcr.io/element-hq/lk-jwt-service:latest. La convention du dépôt veut une version en dur, et Renovate ne sait rien faire d'un latest.

Références :