feat(matrix): add homeserver and whatsapp bridge

Introduces the Matrix homeserver (Synapse) and the mautrix-whatsapp bridge.
Configures Synapse with PostgreSQL, including specific collation requirements.
Sets up secret generation for both services and manages their startup order.
This commit is contained in:
2026-09-22 10:26:37 -04:00
parent a7ee328388
commit 094a348497
3 changed files with 158 additions and 0 deletions
+3
View File
@@ -19,6 +19,8 @@
./wayvnc
# ./dnscrypt
./adguard
./matrix
./matrix-whatsapp
./navidrome
./tor
# ./minio
@@ -27,6 +29,7 @@
./discord-bot
./eternal-terminal
./nextcloud-mcp
./amazon-mcp
# ./gotify
./zitadel
# ./kanidm
+72
View File
@@ -0,0 +1,72 @@
# services/matrix-whatsapp/default.nix
# Pont Matrix <-> WhatsApp (mautrix-whatsapp), serveur : ../matrix
{
pkgs,
lib,
...
}: let
dataDir = "/var/lib/mautrix-whatsapp";
in {
# mautrix-whatsapp dépend de libolm (goolm) pour le chiffrement megolm,
# marqué insecure dans nixpkgs (CVE-2024-4519x, side-channel théorique).
# C'est la dépendance crypto standard de TOUS les bridges mautrix.
nixpkgs.config.permittedInsecurePackages = ["olm-3.2.16"];
services.mautrix-whatsapp = {
enable = true;
settings = {
homeserver = {
address = "http://127.0.0.1:8008";
domain = "matrix.marcpartensky.com";
};
appservice = {
# bind localhost uniquement + URL de registration joignable par synapse
hostname = "127.0.0.1";
address = "http://127.0.0.1:29318";
ephemeral_events = false;
};
bridge = {
permissions = {
# le défaut du module "*": "relay" est neutralisé par relay.enabled=false
"matrix.marcpartensky.com" = "user";
"@marc:matrix.marcpartensky.com" = "admin";
};
relay.enabled = false;
};
encryption = {
allow = true;
default = true;
# require=false : ne pas dropper les messages si une room reste non chiffrée
require = false;
# $VAR substituée par envsubst dans le preStart du module (valeur exportée ci-dessous)
pickle_key = "$MAUTRIX_WHATSAPP_PICKLE_KEY";
};
backfill.enabled = true;
# base sqlite dans /var/lib/mautrix-whatsapp (défaut du module), suffisant ici
};
# Le module ajoute matrix-synapse.service aux deps par défaut ; on inverse
# l'ordre : c'est synapse qui attend le bridge (cf. services/matrix),
# car le bridge génère sa registration dans son preStart.
serviceDependencies = lib.mkForce [];
};
# Pickle key e2ee générée une fois, hors du store nix.
# mkBefore : exportée AVANT le envsubst du preStart du module qui l'injecte
# dans config.yaml.
systemd.services.mautrix-whatsapp.preStart = lib.mkBefore ''
if [ ! -f ${dataDir}/pickle_key.txt ]; then
${pkgs.openssl}/bin/openssl rand -hex 32 > ${dataDir}/pickle_key.txt
chmod 600 ${dataDir}/pickle_key.txt
fi
export MAUTRIX_WHATSAPP_PICKLE_KEY=$(cat ${dataDir}/pickle_key.txt)
'';
# Le module enregistre tout seul le bridge auprès de synapse
# (registerToSynapse = true quand synapse est activé) et donne à synapse
# le groupe mautrix-whatsapp pour lire whatsapp-registration.yaml.
# Après déploiement, connexion WhatsApp (à faire par marc, QR sur son tel) :
# 1. ouvrir un DM avec @whatsappbot:matrix.marcpartensky.com dans son client Matrix
# 2. envoyer : login
# 3. scanner le QR code avec WhatsApp (appareils connectés)
}
+83
View File
@@ -0,0 +1,83 @@
# services/matrix/default.nix
# Serveur Matrix classique (Synapse), pont WhatsApp : ../matrix-whatsapp
{
pkgs,
lib,
...
}: let
dataDir = "/var/lib/matrix-synapse";
in {
services.matrix-synapse = {
enable = true;
settings = {
# ATTENTION : server_name ne peut plus être changé après le premier démarrage
server_name = "matrix.marcpartensky.com";
public_baseurl = "https://matrix.marcpartensky.com/";
report_stats = false;
max_upload_size = "100M";
# inscription publique fermée : les comptes se créent avec
# sudo matrix-synapse-register_new_matrix_user -u <user> -a
# TCP obligatoire : le service tourne avec PrivateUsers=true, ce qui
# casse l'auth peer par socket Unix (UID mappé). 127.0.0.1 est trust
# (cf. services/postgres) et déclenche la dépendance postgresql.target
# du module synapse.
database.args.host = "127.0.0.1";
};
# registration_shared_secret hors du store nix, généré au 1er démarrage (preStart ci-dessous)
# TODO : migrer vers sops si une clé age devient accessible à hermes
extraConfigFiles = ["${dataDir}/secrets.yaml"];
};
# BDD locale : postgres de tower est en auth trust sur localhost
# (cf. services/postgres), donc pas de mot de passe à gérer.
# Le module synapse attend postgresql.target tout seul (host = 127.0.0.1).
services.postgresql = {
ensureUsers = [
{
name = "matrix-synapse";
# pas d'ensureDBOwnership ici : la DB est créée par le oneshot
# ci-dessous avec le OWNER (ensureDatabases ne gère pas la collation)
}
];
};
# Synapse EXIGE une DB en collation C (sinon IncorrectDatabaseSetup au boot)
# et ensureDatabases ne permet pas de la spécifier : création idempotente ici.
# Connexion par socket en peer (user postgres local, pas de PrivateUsers ici).
systemd.services.matrix-synapse-db = {
description = "Création de la base matrix-synapse (collation C)";
after = ["postgresql.service" "postgresql-setup.service"];
requires = ["postgresql.service"];
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "postgres";
};
script = ''
${pkgs.postgresql}/bin/psql -tAc "SELECT 1 FROM pg_database WHERE datname='matrix-synapse'" \
| grep -q 1 \
|| ${pkgs.postgresql}/bin/psql -c "CREATE DATABASE \"matrix-synapse\" OWNER \"matrix-synapse\" LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0"
'';
};
systemd.services.matrix-synapse = {
# Le bridge WhatsApp génère son fichier de registration dans son propre
# preStart : synapse doit démarrer APRÈS lui sinon le fichier
# app_service_config_files n'existe pas encore et synapse refuse de démarrer.
after = ["mautrix-whatsapp.service" "matrix-synapse-db.service"];
wants = ["mautrix-whatsapp.service"];
requires = ["matrix-synapse-db.service"];
# mkBefore : tourne avant le --generate-keys du module
preStart = lib.mkBefore ''
if [ ! -f ${dataDir}/secrets.yaml ]; then
echo "registration_shared_secret: $(${pkgs.openssl}/bin/openssl rand -hex 32)" > ${dataDir}/secrets.yaml
chmod 600 ${dataDir}/secrets.yaml
fi
'';
};
# Listener par défaut du module : 127.0.0.1:8008 (client + federation, x_forwarded).
# Pas de port firewall à ouvrir : newt/Pangolin tourne sur tower et tape en local.
# Exposition publique à faire côté Pangolin : matrix.marcpartensky.com -> http://127.0.0.1:8008
}