summaryrefslogtreecommitdiffstats
path: root/modules/ejabberd
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2023-09-11 18:24:28 +0200
committertv <tv@krebsco.de>2023-09-13 18:07:11 +0200
commit0c4f3acb281be6290c55a6e96bc29fab5b5c7a11 (patch)
treedadaec00477a095273475ac345b2066b4748c399 /modules/ejabberd
parentab1d0479e90f11806d4703ec6fffed3d5f782914 (diff)
stockholm -> hrm
Diffstat (limited to 'modules/ejabberd')
-rw-r--r--modules/ejabberd/default.nix274
1 files changed, 274 insertions, 0 deletions
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 <secrets> + "/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";
+ };
+ };
+ };
+}