From 0c4f3acb281be6290c55a6e96bc29fab5b5c7a11 Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 11 Sep 2023 18:24:28 +0200 Subject: stockholm -> hrm --- modules/ejabberd/default.nix | 274 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 modules/ejabberd/default.nix (limited to 'modules/ejabberd') diff --git a/modules/ejabberd/default.nix b/modules/ejabberd/default.nix new file mode 100644 index 0000000..02c060d --- /dev/null +++ b/modules/ejabberd/default.nix @@ -0,0 +1,274 @@ +{ config, lib, mylib, pkgs, ... }: let + cfg = config.tv.ejabberd; + + gen-dhparam = pkgs.writeDash "gen-dhparam" '' + set -efu + path=$1 + bits=2048 + # TODO regenerate dhfile after some time? + if ! test -e "$path"; then + ${pkgs.openssl}/bin/openssl dhparam "$bits" > "$path" + fi + ''; + + settingsFormat = pkgs.formats.json {}; + +in { + options.tv.ejabberd = { + enable = lib.mkEnableOption "tv.ejabberd"; + certfiles = lib.mkOption { + type = lib.types.listOf mylib.types.absolute-pathname; + default = [ + (toString + "/ejabberd.pem") + ]; + }; + configFile = lib.mkOption { + type = lib.types.either lib.types.package mylib.types.absolute-pathname; + default = settingsFormat.generate "ejabberd.yaml" cfg.settings; + }; + ciphers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "ECDHE-ECDSA-AES256-GCM-SHA384" + "ECDHE-RSA-AES256-GCM-SHA384" + "ECDHE-ECDSA-CHACHA20-POLY1305" + "ECDHE-RSA-CHACHA20-POLY1305" + "ECDHE-ECDSA-AES128-GCM-SHA256" + "ECDHE-RSA-AES128-GCM-SHA256" + "ECDHE-ECDSA-AES256-SHA384" + "ECDHE-RSA-AES256-SHA384" + "ECDHE-ECDSA-AES128-SHA256" + "ECDHE-RSA-AES128-SHA256" + ]; + }; + credentials.certfiles = lib.mkOption { + internal = true; + readOnly = true; + default = + lib.imap + (i: _: "/tmp/credentials/certfile${builtins.toJSON i}") + cfg.certfiles; + }; + hosts = lib.mkOption { + type = lib.types.listOf lib.types.str; + }; + pkgs.ejabberd = lib.mkOption { + type = mylib.types.package; + default = pkgs.symlinkJoin { + name = "ejabberd-wrapper"; + paths = [ + (pkgs.writeDashBin "ejabberdctl" '' + exec ${pkgs.ejabberd}/bin/ejabberdctl \ + --config /etc/ejabberd/ejabberd.yaml \ + --ctl-config /etc/ejabberd/ejabberdctl.cfg \ + --logs ${cfg.stateDir} \ + --spool ${cfg.stateDir} \ + "$@" + '') + pkgs.ejabberd + ]; + }; + }; + protocol_options = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + "no_sslv2" + "no_sslv3" + "no_tlsv1" + "no_tlsv1_10" + ]; + }; + registration_watchers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ + config.krebs.users.tv.mail + ]; + }; + settings = lib.mkOption { + type = settingsFormat.type; + default = {}; + }; + stateDir = lib.mkOption { + type = + lib.types.addCheck + mylib.types.absolute-pathname + (path: + lib.hasPrefix "/var/lib/" path && + mylib.types.filename.check (lib.removePrefix "/var/lib/" path) + ); + default = "/var/lib/ejabberd"; + }; + }; + config = lib.mkIf cfg.enable { + + environment.etc."ejabberd/ejabberd.yaml".source = cfg.configFile; + environment.etc."ejabberd/ejabberdctl.cfg".source = + builtins.toFile "ejabberdctl.cfg" /* sh */ '' + ERL_OPTIONS='-setcookie ${cfg.stateDir}/.erlang.cookie' + ''; + + environment.systemPackages = [ + (pkgs.symlinkJoin { + name = "ejabberd-sudo-wrapper"; + paths = [ + (pkgs.writeDashBin "ejabberdctl" '' + exec ${pkgs.systemd}/bin/systemd-run \ + --unit=ejabberdctl \ + --property=StateDirectory=ejabberd \ + --property=User=ejabberd \ + --collect \ + --pipe \ + --quiet \ + ${cfg.pkgs.ejabberd}/bin/ejabberdctl "$@" + '') + cfg.pkgs.ejabberd + ]; + }) + ]; + + krebs.systemd.services.ejabberd.restartIfCredentialsChange = true; + + systemd.services.ejabberd = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + reloadTriggers = [ + config.environment.etc."ejabberd/ejabberd.yaml".source + config.environment.etc."ejabberd/ejabberdctl.cfg".source + ]; + serviceConfig = { + ExecStartPre = [ + "${pkgs.coreutils}/bin/ln -s \${CREDENTIALS_DIRECTORY} /tmp/credentials" + "${gen-dhparam} ${cfg.stateDir}/dhfile" + ]; + ExecStart = "${cfg.pkgs.ejabberd}/bin/ejabberdctl foreground"; + ExecStop = [ + "${cfg.pkgs.ejabberd}/bin/ejabberdctl stop" + "${cfg.pkgs.ejabberd}/bin/ejabberdctl stopped" + ]; + ExecReload = "${cfg.pkgs.ejabberd}/bin/ejabberdctl reload_config"; + LoadCredential = + lib.zipListsWith + (dst: src: "${baseNameOf dst}:${src}") + cfg.credentials.certfiles + cfg.certfiles; + LimitNOFILE = 65536; + PrivateDevices = true; + PrivateTmp = true; + SyslogIdentifier = "ejabberd"; + StateDirectory = "ejabberd"; + User = "ejabberd"; + DynamicUser = true; + TimeoutSec = 60; + RestartSec = 5; + Restart = "on-failure"; + Type = "notify"; + NotifyAccess = "all"; + WatchdogSec = 30; + }; + }; + + # preset config values + tv.ejabberd.settings = { + access_rules = { + announce = lib.mkDefault [{ allow = "admin"; }]; + local = lib.mkDefault [{ allow = "local"; }]; + configure = lib.mkDefault [{ allow = "admin"; }]; + register = lib.mkDefault ["allow"]; + s2s = lib.mkDefault ["allow"]; + trusted_network = lib.mkDefault [{ allow = "loopback"; }]; + }; + + acl = { + local.user_regexp = lib.mkDefault ""; + loopback.ip = lib.mkDefault [ + "127.0.0.0/8" + "::1/128" + "::FFFF:127.0.0.1/128" + ]; + }; + + certfiles = lib.mkDefault cfg.credentials.certfiles; + + hosts = lib.mkDefault cfg.hosts; + + language = lib.mkDefault "en"; + + listen = lib.mkDefault [ + { + port = 5222; + ip = "::"; + module = "ejabberd_c2s"; + shaper = "c2s_shaper"; + ciphers = lib.concatStringsSep ":" cfg.ciphers; + protocol_options = cfg.protocol_options; + starttls = true; + starttls_required = true; + tls = false; + tls_compression = false; + max_stanza_size = 65536; + } + { + port = 5269; + ip = "::"; + module = "ejabberd_s2s_in"; + shaper = "s2s_shaper"; + dhfile = "${cfg.stateDir}/dhfile"; + max_stanza_size = 131072; + } + ]; + + loglevel = lib.mkDefault "4"; + + modules = { + mod_adhoc = lib.mkDefault {}; + mod_admin_extra = lib.mkDefault {}; + mod_announce.access = lib.mkDefault "announce"; + mod_caps = lib.mkDefault {}; + mod_carboncopy = lib.mkDefault {}; + mod_client_state = lib.mkDefault {}; + mod_configure = lib.mkDefault {}; + mod_disco = lib.mkDefault {}; + mod_echo = lib.mkDefault {}; + mod_bosh = lib.mkDefault {}; + mod_last = lib.mkDefault {}; + mod_offline.access_max_user_messages = lib.mkDefault "max_user_offline_messages"; + mod_ping = lib.mkDefault {}; + mod_privacy = lib.mkDefault {}; + mod_private = lib.mkDefault {}; + mod_register = { + access_from = lib.mkDefault "deny"; + access = lib.mkDefault "register"; + ip_access = lib.mkDefault "trusted_network"; + registration_watchers = lib.mkDefault cfg.registration_watchers; + }; + mod_roster = lib.mkDefault {}; + mod_shared_roster = lib.mkDefault {}; + mod_stats = lib.mkDefault {}; + mod_time = lib.mkDefault {}; + mod_vcard.search = lib.mkDefault false; + mod_version = lib.mkDefault {}; + mod_http_api = lib.mkDefault {}; + }; + + s2s_access = lib.mkDefault "s2s"; + s2s_ciphers = lib.concatStringsSep ":" cfg.ciphers; + s2s_dhfile = lib.mkDefault "${cfg.stateDir}/dhfile"; + s2s_protocol_options = lib.mkDefault cfg.protocol_options; + s2s_tls_compression = lib.mkDefault false; + s2s_use_starttls = lib.mkDefault "required"; + + shaper_rules = { + max_user_offline_messages = lib.mkDefault [ + { "5000" = "admin"; } + 100 + ]; + max_user_sessions = lib.mkDefault 10; + c2s_shaper = lib.mkDefault [ + { "none" = "admin"; } + "normal" + ]; + s2s_shaper = lib.mkDefault "fast"; + }; + }; + }; +} -- cgit v1.2.3