{ fqdn }: { config, lib, pkgs, ... }: let inherit (builtins) toJSON; inherit (pkgs) writeText; inherit (pkgs.lib.lists) foldl'; inherit (pkgs.lib.attrsets) mapAttrs' nameValuePair; inherit (config.networking) domain; certs = config.security.acme.certs; certDirectory = certs.${fqdn}.directory; in { services.ejabberd = { enable = true; imagemagick = true; configFile = let toPaths = s: mapAttrs' (n: v: nameValuePair "/${n}" v) s; dhfile = config.security.dhparams.params.nginx.path; toACLs = map (x: { acl = x; }); in writeText "ejabberd.yml" (toJSON { hosts = [ fqdn ]; loglevel = 4; s2s_cafile = "/etc/ssl/certs/ca-certificates.crt"; ca_file = "/etc/ssl/certs/ca-certificates.crt"; certfiles = [ "${certDirectory}/*.pem" ]; listen = map (x: x // { ip = "10.25.1.3"; }) [ { inherit dhfile; port = 5222; module = "ejabberd_c2s"; max_stanza_size = 262144; shaper = "c2s_shaper"; access = "c2s"; starttls_required = true; } { inherit dhfile; port = 5223; tls = true; module = "ejabberd_c2s"; max_stanza_size = 262144; shaper = "c2s_shaper"; access = "c2s"; starttls_required = true; } { inherit dhfile; port = 5269; module = "ejabberd_s2s_in"; max_stanza_size = 524288; } { inherit dhfile; port = 5443; module = "ejabberd_http"; tls = true; request_handlers = toPaths { admin = "ejabberd_web_admin"; api = "mod_http_api"; bosh = "mod_bosh"; captcha = "ejabberd_captcha"; upload = "mod_http_upload"; ws = "ejabberd_http_ws"; }; } { inherit dhfile; port = 5280; module = "ejabberd_http"; request_handlers = toPaths { admin = "ejabberd_web_admin"; ".well-known/acme-challenge" = "ejabberd_acme"; }; } { port = 3478; transport = "udp"; module = "ejabberd_stun"; use_turn = true; turn_ipv4_address = "193.16.42.36"; } { port = 1883; module = "mod_mqtt"; backlog = 1000; } ]; s2s_use_starttls = "required"; acl = { local.user_regexp = ""; loopback.ip = [ "127.0.0.1/8" "::1/128" ]; admin.user = [ "crash@${fqdn}" ]; }; access_rules = { c2s = { deny = "blocked"; allow = "all"; }; } // mapAttrs' (n: v: nameValuePair n { allow = v; }) { local = "local"; announce = "admin"; configure = "admin"; muc_create = "local"; pubsub_createnode = "local"; trusted_network = "loopback"; }; api_permissions = { "console commands" = { from = [ "ejabberd_ctl" ]; who = "all"; what = "*"; }; "admin access" = { who = { access.allow = toACLs [ "local" "admin" ]; oauth = { scope = "ejabberd:admin"; access.allow = toACLs [ "loopback" "admin" ]; }; }; what = [ "*" "!stop" "!start" ]; }; "public commands" = { who.ip = "127.0.0.1/8"; what = [ "status" "connected_users_number" ]; }; }; shaper = { normal = { rate = 3000; burst_size = 20000; }; fast = 100000; }; shaper_rules = { max_user_sessions = 10; max_user_offline_messages = { "5000" = "admin"; "100" = "all"; }; c2s_shaper = { none = "admin"; normal = "all"; }; s2s_shaper = "fast"; }; modules = mapAttrs' (n: v: nameValuePair "mod_${n}" v) ({ announce.access = "announce"; http_upload = { put_url = "https://@HOST@:5443/upload"; custom_headers = { Access-Control-Allow-Origin = "https://@HOST@"; Access-Control-Allow-Methods = "GET,HEAD,PUT,OPTIONS"; Access-Control-Allow-Headers = "Content-Type"; }; }; mam = { assume_mam_usage = true; default = "always"; }; muc = { access = [ "allow" ]; access_admin = [ { allow = "admin"; } ]; access_create = "muc_create"; access_persistent = "muc_create"; access_mam = [ "allow" ]; default_room_options.mam = true; }; offline.access_max_user_messages = "max_user_offline_messages"; proxy65 = { access = "local"; max_connections = 5; }; pubsub = { access_createnode = "pubsub_createnode"; plugins = [ "flat" "pep" ]; force_node_config."storage:bookmarks".access_model = "whitelist"; }; register.ip_access = "trusted_network"; roster.versioning = true; stream_mgmt.resend_on_timeout = "if_offline"; version.show_os = false; } // foldl' (a: x: a // { ${x} = {}; }) {} [ "adhoc" "admin_extra" "avatar" "blocking" "bosh" "caps" "carboncopy" "client_state" "configure" "disco" "fail2ban" "http_api" "last" "mqtt" "muc_admin" "ping" "privacy" "private" "push" "push_keepalive" "s2s_dialback" "shared_roster" "stun_disco" "vcard" "vcard_xupdate" ]); }); package = pkgs.ejabberd.override { withZlib = true; withTools = true; }; }; security.acme.certs.${fqdn} = { extraDomainNames = map (x: "${x}.xmpp.${fqdn}") [ "pubsub" "proxy" "upload" "conference" ]; group = "ejabberd-cert"; postRun = "systemctl restart ejabberd.service"; }; users.groups.ejabberd-cert.members = [ "ejabberd" "nginx" ]; security.dhparams = { enable = true; params.nginx = {}; }; networking.firewall.allowedTCPPorts = [ 5222 # xmpp-client 5223 # xmpp-client 5269 # xmpp-server 5280 # xmpp-bosh 5443 # https 3478 # xmpp-stun ]; }