feat(matrix): add new bridges and improve E2EE defaults
Add mautrix-linkedin, mautrix-meta (Instagram, Messenger), and mautrix-telegram (initial setup). Enable E2EE by default for mautrix-discord and mautrix-signal, ensuring stable pickle_key generation. Update matrix-synapse dependencies for new bridges.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
./matrix-signal
|
||||
./matrix-discord
|
||||
./matrix-meta
|
||||
./matrix-linkedin
|
||||
# ./matrix-telegram # attendre api_id/api_hash de my.telegram.org
|
||||
./navidrome
|
||||
./tor
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# services/matrix-discord/default.nix
|
||||
# Pont Matrix <-> Discord (mautrix-discord), serveur : ../matrix
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
dataDir = "/var/lib/mautrix-discord";
|
||||
@@ -51,18 +53,37 @@ in {
|
||||
"matrix.marcpartensky.com" = "user";
|
||||
"@marc:matrix.marcpartensky.com" = "admin";
|
||||
};
|
||||
# défaut du bridge : pas de chiffrement (Discord sert surtout pour des
|
||||
# salons de serveurs). Passer allow/default/require à true pour des
|
||||
# portails chiffrés.
|
||||
# portails chiffrés par défaut (comme whatsapp), require=false pour ne
|
||||
# pas casser les salles qui ne peuvent pas être chiffrées (salons de
|
||||
# serveurs notamment). pickle_key stable hors du store, cf. l'export
|
||||
# ajouté plus bas dans mautrix-discord-registration.
|
||||
encryption = {
|
||||
allow = false;
|
||||
default = false;
|
||||
allow = true;
|
||||
default = true;
|
||||
require = false;
|
||||
pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Le module NE génère pas la registration dans le preStart du bridge : c'est
|
||||
# mautrix-discord-registration.service qui fait le envsubst du config.yaml puis
|
||||
# --generate-registration. L'export de la clé doit donc être DANS ce script
|
||||
# (un export dans un ExecStartPre ne survivrait pas au process suivant).
|
||||
systemd.services.mautrix-discord-registration.script = 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 ENCRYPTION_PICKLE_KEY=$(cat ${dataDir}/pickle_key.txt)
|
||||
'';
|
||||
|
||||
# Le config.yaml du bridge est régénéré par l'unité de registration, mais le
|
||||
# bridge ne le relit pas tout seul : on déclare un trigger de restart pour que
|
||||
# le switch redémarre mautrix-discord.service quand ce fichier change.
|
||||
systemd.services.mautrix-discord.restartTriggers = [./default.nix];
|
||||
|
||||
# Pas d'inversion de dépendance ici : le module crée
|
||||
# mautrix-discord-registration.service et fait démarrer matrix-synapse après
|
||||
# lui (contrairement à whatsapp/signal qui génèrent la registration dans le
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# services/matrix-linkedin/default.nix
|
||||
# Pont Matrix <-> LinkedIn (mautrix-linkedin).
|
||||
# Contrairement aux autres bridges, il n'existe PAS de module nixpkgs pour
|
||||
# mautrix-linkedin : paquet, unité systemd et registration sont donc câblés ici.
|
||||
# Le binaire amont est un binaire statique publié dans les releases GitHub
|
||||
# (les bridges Go de mautrix n'ont pas de build nixpkgs pour LinkedIn).
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
dataDir = "/var/lib/mautrix-linkedin";
|
||||
appservicePort = 29329;
|
||||
|
||||
mautrix-linkedin = pkgs.stdenvNoCC.mkDerivation {
|
||||
pname = "mautrix-linkedin";
|
||||
version = "0.2609.0";
|
||||
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/mautrix/linkedin/releases/download/v0.2609.0/mautrix-linkedin-amd64";
|
||||
hash = "sha256-6FrEDJRQWJm4Ib/WJtpmRPJVp9RYsxQ1X0YtaePQ1k0=";
|
||||
};
|
||||
|
||||
dontUnpack = true;
|
||||
installPhase = ''
|
||||
install -Dm755 $src $out/bin/mautrix-linkedin
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A Matrix-LinkedIn puppeting bridge";
|
||||
homepage = "https://github.com/mautrix/linkedin";
|
||||
license = lib.licenses.agpl3Only;
|
||||
platforms = ["x86_64-linux"];
|
||||
mainProgram = "mautrix-linkedin";
|
||||
};
|
||||
};
|
||||
|
||||
settingsFormat = pkgs.formats.yaml {};
|
||||
|
||||
settings = {
|
||||
homeserver = {
|
||||
address = "http://127.0.0.1:8008";
|
||||
domain = "matrix.marcpartensky.com";
|
||||
software = "standard";
|
||||
};
|
||||
appservice = {
|
||||
id = "linkedin";
|
||||
# address = ce que synapse utilisera pour joindre l'appservice (et ce qui
|
||||
# finit dans le champ url de la registration) : il DOIT correspondre au
|
||||
# port écouté, sinon la registration annonce le défaut 29341.
|
||||
address = "http://127.0.0.1:${toString appservicePort}";
|
||||
hostname = "127.0.0.1";
|
||||
port = appservicePort;
|
||||
bot = {
|
||||
username = "linkedinbot";
|
||||
displayname = "LinkedIn bridge bot";
|
||||
avatar = "mxc://maunium.net/CqzBEHjrLsfdqixWZgNHMlRT";
|
||||
};
|
||||
ephemeral_events = true;
|
||||
username_template = "linkedin_{{.}}";
|
||||
};
|
||||
# ATTENTION (vérifié à la main) : en config "nouvelle génération", database
|
||||
# et encryption sont au niveau RACINE, pas sous appservice/bridge. Les y
|
||||
# mettre déclenche "Legacy bridge config detected" au démarrage.
|
||||
database = {
|
||||
type = "sqlite3-fk-wal";
|
||||
uri = "file:${dataDir}/mautrix-linkedin.db?_txlock=immediate";
|
||||
};
|
||||
encryption = {
|
||||
allow = true;
|
||||
default = true;
|
||||
require = false;
|
||||
# le défaut du bridge est "generate" (clé différente à chaque démarrage,
|
||||
# donc DB crypto illisible après restart) : valeur stable hors du store.
|
||||
pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
};
|
||||
bridge = {
|
||||
command_prefix = "!linkedin";
|
||||
permissions = {
|
||||
"matrix.marcpartensky.com" = "user";
|
||||
"@marc:matrix.marcpartensky.com" = "admin";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
settingsFileUnsubstituted = settingsFormat.generate "mautrix-linkedin-config.yaml" settings;
|
||||
settingsFile = "${dataDir}/config.yaml";
|
||||
registrationFile = "${dataDir}/linkedin-registration.yaml";
|
||||
in {
|
||||
users.users.mautrix-linkedin = {
|
||||
isSystemUser = true;
|
||||
group = "mautrix-linkedin";
|
||||
home = dataDir;
|
||||
description = "Mautrix-LinkedIn bridge user";
|
||||
};
|
||||
users.groups.mautrix-linkedin = {};
|
||||
|
||||
systemd.services.mautrix-linkedin = {
|
||||
description = "Mautrix-LinkedIn, a Matrix-LinkedIn puppeting bridge";
|
||||
wantedBy = ["multi-user.target"];
|
||||
# pas de dépendance à synapse : c'est synapse qui nous attend, parce que la
|
||||
# registration est générée dans notre preStart (cf. services/matrix).
|
||||
wants = ["network-online.target"];
|
||||
after = ["network-online.target"];
|
||||
path = [
|
||||
pkgs.envsubst
|
||||
pkgs.yq
|
||||
pkgs.openssl
|
||||
pkgs.ffmpeg-headless
|
||||
];
|
||||
|
||||
preStart = ''
|
||||
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 ENCRYPTION_PICKLE_KEY=$(cat ${dataDir}/pickle_key.txt)
|
||||
|
||||
# substitution des $VAR du store dans le vrai config.yaml (umask 0177 :
|
||||
# il contient les tokens appservice)
|
||||
rm -f '${settingsFile}'
|
||||
old_umask=$(umask)
|
||||
umask 0177
|
||||
${pkgs.envsubst}/bin/envsubst -o '${settingsFile}' -i '${settingsFileUnsubstituted}'
|
||||
|
||||
if [ ! -f '${registrationFile}' ]; then
|
||||
${lib.getExe mautrix-linkedin} \
|
||||
--config='${settingsFile}' \
|
||||
--generate-registration \
|
||||
--registration='${registrationFile}'
|
||||
fi
|
||||
chmod 640 '${registrationFile}'
|
||||
|
||||
# remet les tokens de la registration dans le config : sans ça, un restart
|
||||
# repartirait sur le placeholder du store et casserait le sync.
|
||||
${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token
|
||||
| .[0].appservice.hs_token = .[1].hs_token
|
||||
| .[0]' \
|
||||
'${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp'
|
||||
mv '${settingsFile}.tmp' '${settingsFile}'
|
||||
umask $old_umask
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "mautrix-linkedin";
|
||||
Group = "mautrix-linkedin";
|
||||
WorkingDirectory = dataDir;
|
||||
StateDirectory = "mautrix-linkedin";
|
||||
Environment = ["HOME=${dataDir}"];
|
||||
ExecStart = "${lib.getExe mautrix-linkedin} --config='${settingsFile}'";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 30;
|
||||
|
||||
# même durcissement que les modules nixpkgs
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
};
|
||||
|
||||
restartTriggers = [settingsFileUnsubstituted];
|
||||
};
|
||||
|
||||
# Lecture de la registration par synapse (fichier 0640, groupe du bridge).
|
||||
services.matrix-synapse = {
|
||||
settings.app_service_config_files = [registrationFile];
|
||||
};
|
||||
systemd.services.matrix-synapse.serviceConfig.SupplementaryGroups = ["mautrix-linkedin"];
|
||||
|
||||
# Après déploiement, connexion (à faire par marc) : DM à
|
||||
# @linkedinbot:matrix.marcpartensky.com, `login`, puis identifiants LinkedIn.
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# services/matrix-meta/default.nix
|
||||
# Ponts Matrix <-> Instagram et Matrix <-> Messenger (mautrix-meta : un même
|
||||
# binaire sert les deux réseaux, une instance par réseau), serveur : ../matrix
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
# Clé e2ee stable, hors du store nix. Le défaut du module est la constante
|
||||
# publique "mautrix.bridge.e2ee" : la DB crypto serait chiffrée avec une clé
|
||||
# connue de tous, donc on génère la nôtre au premier démarrage.
|
||||
mkPickleKey = instance: ''
|
||||
if [ ! -f /var/lib/mautrix-meta-${instance}/pickle_key.txt ]; then
|
||||
${pkgs.openssl}/bin/openssl rand -hex 32 > /var/lib/mautrix-meta-${instance}/pickle_key.txt
|
||||
chmod 600 /var/lib/mautrix-meta-${instance}/pickle_key.txt
|
||||
fi
|
||||
export ENCRYPTION_PICKLE_KEY=$(cat /var/lib/mautrix-meta-${instance}/pickle_key.txt)
|
||||
'';
|
||||
|
||||
homeserver = {
|
||||
address = "http://127.0.0.1:8008";
|
||||
domain = "matrix.marcpartensky.com";
|
||||
};
|
||||
|
||||
permissions = {
|
||||
"matrix.marcpartensky.com" = "user";
|
||||
"@marc:matrix.marcpartensky.com" = "admin";
|
||||
};
|
||||
in {
|
||||
services.mautrix-meta.instances = {
|
||||
# bot : @instagrambot:matrix.marcpartensky.com (port 29320 du preset)
|
||||
instagram = {
|
||||
enable = true;
|
||||
settings = {
|
||||
inherit homeserver;
|
||||
appservice.hostname = "127.0.0.1";
|
||||
bridge.permissions = permissions;
|
||||
encryption.pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
};
|
||||
};
|
||||
|
||||
# bot : @messengerbot:matrix.marcpartensky.com
|
||||
# Le module n'a de preset que pour instagram et facebook, donc tout est
|
||||
# explicite ici (mode messenger = API de messenger.com).
|
||||
messenger = {
|
||||
enable = true;
|
||||
settings = {
|
||||
network.mode = "messenger";
|
||||
inherit homeserver;
|
||||
appservice = {
|
||||
id = "messenger";
|
||||
hostname = "127.0.0.1";
|
||||
port = 29322;
|
||||
bot = {
|
||||
username = "messengerbot";
|
||||
displayname = "Messenger bridge bot";
|
||||
avatar = "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
|
||||
};
|
||||
};
|
||||
bridge.permissions = permissions;
|
||||
encryption.pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Le envsubst qui écrit config.yaml vit dans l'unité de registration (pas dans
|
||||
# le preStart du bridge) : la clé doit être exportée DANS ce script, comme
|
||||
# pour matrix-discord.
|
||||
systemd.services = {
|
||||
mautrix-meta-instagram-registration.script = lib.mkBefore (mkPickleKey "instagram");
|
||||
mautrix-meta-messenger-registration.script = lib.mkBefore (mkPickleKey "messenger");
|
||||
};
|
||||
|
||||
# Après déploiement, connexion (à faire par marc), une fois par réseau :
|
||||
# 1. DM au bot (@instagrambot ou @messengerbot)
|
||||
# 2. envoyer : login
|
||||
# 3. le bridge demande des cookies : se connecter normalement dans une
|
||||
# fenêtre privée sur instagram.com ou messenger.com, devtools + onglet
|
||||
# réseau + filtre XHR graphql, clic droit sur une requête, "Copy as cURL
|
||||
# (POSIX)", et coller le tout dans le DM.
|
||||
# Sinon, cookies utiles : Instagram sessionid, csrftoken, mid, ig_did,
|
||||
# ds_user_id ; Facebook/Messenger datr, c_user, sb, xs.
|
||||
# Meta peut bloquer un compte jugé suspect (captcha / téléphone) : active la
|
||||
# double authentification pour limiter le risque.
|
||||
}
|
||||
@@ -28,7 +28,14 @@ in {
|
||||
};
|
||||
};
|
||||
# pickle key e2ee : valeur stable hors du store nix, cf. preStart.
|
||||
encryption.pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
# allow/default à true = portails chiffrés par défaut (require=false pour
|
||||
# ne pas casser une salle non chiffrable).
|
||||
encryption = {
|
||||
allow = true;
|
||||
default = true;
|
||||
require = false;
|
||||
pickle_key = "$ENCRYPTION_PICKLE_KEY";
|
||||
};
|
||||
};
|
||||
|
||||
# Le bridge génère sa registration dans SON preStart : c'est synapse qui
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# services/matrix-telegram/default.nix
|
||||
# Pont Matrix <-> Telegram (mautrix-telegram, bridge Python historique),
|
||||
# serveur : ../matrix
|
||||
#
|
||||
# PAS ENCORE ACTIVÉ, volontairement : il manque api_id et api_hash, qui
|
||||
# s'obtiennent uniquement avec un compte Telegram (https://my.telegram.org,
|
||||
# section "API development tools", connexion avec son numéro de téléphone).
|
||||
# api_id est un identifiant public, api_hash est le secret.
|
||||
#
|
||||
# Pour activer, trois choses :
|
||||
# 1. renseigner api_id ci-dessous (nombre, pas un secret) ;
|
||||
# 2. créer secrets/telegram.yml avec la clé telegram_api_hash (fichier neuf :
|
||||
# chiffrable avec les seules clés publiques de .sops.yaml) et ajouter la
|
||||
# creation_rule correspondante dans .sops.yaml ;
|
||||
# 3. décommenter ./matrix-telegram dans services/default.nix.
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
sops.secrets.telegram_api_hash = {
|
||||
sopsFile = ../../secrets/telegram.yml;
|
||||
owner = "mautrix-telegram";
|
||||
mode = "0400";
|
||||
restartUnits = ["mautrix-telegram.service"];
|
||||
};
|
||||
|
||||
# Le nix store ne contient que la référence $TELEGRAM_API_HASH, la valeur est
|
||||
# injectée par envsubst au démarrage (preStart du module), depuis ce fichier.
|
||||
sops.templates."matrix-telegram.env" = {
|
||||
content = ''
|
||||
TELEGRAM_API_HASH=${config.sops.placeholder.telegram_api_hash}
|
||||
'';
|
||||
owner = "mautrix-telegram";
|
||||
group = "mautrix-telegram";
|
||||
mode = "0400";
|
||||
};
|
||||
|
||||
services.mautrix-telegram = {
|
||||
enable = true;
|
||||
|
||||
environmentFile = config.sops.templates."matrix-telegram.env".path;
|
||||
|
||||
settings = {
|
||||
homeserver = {
|
||||
address = "http://127.0.0.1:8008";
|
||||
domain = "matrix.marcpartensky.com";
|
||||
};
|
||||
appservice = {
|
||||
hostname = "127.0.0.1";
|
||||
# défaut du module : 8080, à éviter (déjà pris par jupyterhub sur tower)
|
||||
port = 29317;
|
||||
};
|
||||
bridge.permissions = {
|
||||
"matrix.marcpartensky.com" = "full";
|
||||
"@marc:matrix.marcpartensky.com" = "admin";
|
||||
};
|
||||
telegram = {
|
||||
# TODO: nombre récupéré sur my.telegram.org
|
||||
api_id = 0;
|
||||
api_hash = "$TELEGRAM_API_HASH";
|
||||
};
|
||||
};
|
||||
|
||||
# La registration est générée dans le preStart du bridge, donc c'est synapse
|
||||
# qui doit l'attendre (cf. services/matrix), pas l'inverse.
|
||||
serviceDependencies = lib.mkForce [];
|
||||
};
|
||||
|
||||
# Après déploiement, connexion (à faire par marc) : DM à
|
||||
# @telegrambot:matrix.marcpartensky.com, `login`, puis suivre les instructions
|
||||
# (numéro de téléphone, code reçu dans Telegram, mot de passe 2FA si activé).
|
||||
}
|
||||
@@ -66,8 +66,8 @@ in {
|
||||
# synapse doit démarrer APRÈS eux sinon app_service_config_files pointe sur
|
||||
# un fichier qui n'existe pas encore et synapse refuse de démarrer.
|
||||
# (mautrix-discord gère ça tout seul via mautrix-discord-registration.)
|
||||
after = ["mautrix-whatsapp.service" "mautrix-signal.service" "matrix-synapse-db.service"];
|
||||
wants = ["mautrix-whatsapp.service" "mautrix-signal.service"];
|
||||
after = ["mautrix-whatsapp.service" "mautrix-signal.service" "mautrix-linkedin.service" "matrix-synapse-db.service"];
|
||||
wants = ["mautrix-whatsapp.service" "mautrix-signal.service" "mautrix-linkedin.service"];
|
||||
requires = ["matrix-synapse-db.service"];
|
||||
# mkBefore : tourne avant le --generate-keys du module
|
||||
preStart = lib.mkBefore ''
|
||||
|
||||
Reference in New Issue
Block a user