commit 781a51e2a04014483871ea2d351a43a30d0b3df7
parent ee7787851cf666a5083c7258afa0765e1503bdc8
Author: Katja (ctucx) <git@ctu.cx>
Date: Tue, 18 Mar 2025 12:58:05 +0100
parent ee7787851cf666a5083c7258afa0765e1503bdc8
Author: Katja (ctucx) <git@ctu.cx>
Date: Tue, 18 Mar 2025 12:58:05 +0100
modules/nixos: add `mautrix-signal`, `mautrix-whatsapp`
3 files changed, 248 insertions(+), 0 deletions(-)
A
|
122
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
122
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix @@ -6,6 +6,8 @@ disabledModules = [ "services/misc/gitolite.nix" "services/web-apps/gotosocial.nix" + "services/matrix/mautrix-whatsapp.nix" + "services/matrix/mautrix-signal.nix" ]; imports = [ @@ -16,6 +18,8 @@ ./dns.nix ./gotosocial.nix ./gnome.nix + ./mautrix-whatsapp.nix + ./mautrix-signal.nix ]; }
diff --git a/modules/nixos/mautrix-signal.nix b/modules/nixos/mautrix-signal.nix @@ -0,0 +1,122 @@ +{ lib, config, pkgs, ... }: + +let + cfg = config.services.mautrix-signal; + dataDir = "/var/lib/mautrix-signal"; + registrationFile = "${dataDir}/signal-registration.yaml"; + settingsFile = "${dataDir}/config.yaml"; + settingsFileUnsubstituted = settingsFormat.generate "mautrix-signal-config-unsubstituted.json" cfg.settings; + settingsFormat = pkgs.formats.json { }; + +in { + + options.services.mautrix-signal = { + enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge"; + + settings = lib.mkOption { + type = settingsFormat.type; + default = {}; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + }; + + serviceDependencies = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + }; + + config = lib.mkIf cfg.enable { + users.groups.mautrix-signal = { }; + users.users.mautrix-signal = { + isSystemUser = true; + group = "mautrix-signal"; + home = dataDir; + description = "Mautrix-Signal bridge user"; + }; + + systemd.services.mautrix-signal = { + description = "mautrix-signal, a Matrix-Signal puppeting bridge."; + restartTriggers = [ settingsFileUnsubstituted ]; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + # ffmpeg is required for conversion of voice messages + path = [ pkgs.ffmpeg-headless ]; + + preStart = '' + # substitute the settings file by environment variables + # in this case read from EnvironmentFile + test -f '${settingsFile}' && rm -f '${settingsFile}' + old_umask=$(umask) + umask 0177 + ${pkgs.envsubst}/bin/envsubst \ + -o '${settingsFile}' \ + -i '${settingsFileUnsubstituted}' + umask $old_umask + + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mautrix-signal}/bin/mautrix-signal \ + --generate-registration \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + fi + chmod 640 ${registrationFile} + + umask 0177 + # 1. Overwrite registration tokens in config + # 2. If environment variable MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET + # is set, set it as the login shared secret value for the configured + # homeserver domain. + ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token + | .[0].appservice.hs_token = .[1].hs_token + | .[0] + | if env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET then .double_puppet.secrets.[.homeserver.domain] = env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET else . end' \ + '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp' + mv '${settingsFile}.tmp' '${settingsFile}' + umask $old_umask + ''; + + serviceConfig = { + User = "mautrix-signal"; + Group = "mautrix-signal"; + EnvironmentFile = cfg.environmentFile; + StateDirectory = baseNameOf dataDir; + WorkingDirectory = dataDir; + ExecStart = '' + ${pkgs.mautrix-signal}/bin/mautrix-signal \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + ''; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + Restart = "on-failure"; + RestartSec = "30s"; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + Type = "simple"; + UMask = 27; + }; + }; + }; + +}
diff --git a/modules/nixos/mautrix-whatsapp.nix b/modules/nixos/mautrix-whatsapp.nix @@ -0,0 +1,122 @@ +{ lib, config, pkgs, ... }: + +let + cfg = config.services.mautrix-whatsapp; + dataDir = "/var/lib/mautrix-whatsapp"; + registrationFile = "${dataDir}/whatsapp-registration.yaml"; + settingsFile = "${dataDir}/config.json"; + settingsFileUnsubstituted = settingsFormat.generate "mautrix-whatsapp-config-unsubstituted.json" cfg.settings; + settingsFormat = pkgs.formats.json {}; + +in { + + options.services.mautrix-whatsapp = { + enable = lib.mkEnableOption "mautrix-whatsapp, a puppeting/relaybot bridge between Matrix and WhatsApp"; + + settings = lib.mkOption { + type = settingsFormat.type; + default = {}; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + }; + + serviceDependencies = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + }; + + config = lib.mkIf cfg.enable { + users.groups.mautrix-whatsapp = {}; + users.users.mautrix-whatsapp = { + isSystemUser = true; + group = "mautrix-whatsapp"; + home = dataDir; + description = "Mautrix-WhatsApp bridge user"; + }; + + systemd.services.mautrix-whatsapp = { + description = "Mautrix-WhatsApp Service - A WhatsApp bridge for Matrix"; + restartTriggers = [ settingsFileUnsubstituted ]; + + wantedBy = ["multi-user.target"]; + wants = ["network-online.target"] ++ cfg.serviceDependencies; + after = ["network-online.target"] ++ cfg.serviceDependencies; + + + preStart = '' + # substitute the settings file by environment variables + # in this case read from EnvironmentFile + test -f '${settingsFile}' && rm -f '${settingsFile}' + old_umask=$(umask) + umask 0177 + ${pkgs.envsubst}/bin/envsubst \ + -o '${settingsFile}' \ + -i '${settingsFileUnsubstituted}' + umask $old_umask + + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \ + --generate-registration \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + fi + chmod 640 ${registrationFile} + + umask 0177 + # 1. Overwrite registration tokens in config + # 2. If environment variable MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET + # is set, set it as the login shared secret value for the configured + # homeserver domain. + ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token + | .[0].appservice.hs_token = .[1].hs_token + | .[0] + | if env.MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET then .bridge.login_shared_secret_map.[.homeserver.domain] = env.MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET else . end' \ + '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp' + mv '${settingsFile}.tmp' '${settingsFile}' + umask $old_umask + ''; + + serviceConfig = { + User = "mautrix-whatsapp"; + Group = "mautrix-whatsapp"; + EnvironmentFile = cfg.environmentFile; + StateDirectory = baseNameOf dataDir; + WorkingDirectory = dataDir; + ExecStart = '' + ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + ''; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + Restart = "on-failure"; + RestartSec = "30s"; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = ["@system-service"]; + Type = "simple"; + UMask = 0027; + }; + }; + }; + +}