summaryrefslogtreecommitdiff
path: root/ejabberd_kai.nix
diff options
context:
space:
mode:
authorJohn Bargman2025-05-23 20:13:08 +0000
committerJohn Bargman2025-05-23 20:13:08 +0000
commitf244077b3e73494a4017cfb92bf8f393be7ed555 (patch)
treea270f5e2a16288c9c23fd7cc80af9da38c927d24 /ejabberd_kai.nix
parent7db280d50c2913df493b26b39568ff8ae676400e (diff)
downloadcrash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar.gz
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar.bz2
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar.lz
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar.xz
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.tar.zst
crash-web-f244077b3e73494a4017cfb92bf8f393be7ed555.zip
siloed rooms
Diffstat (limited to 'ejabberd_kai.nix')
-rw-r--r--ejabberd_kai.nix607
1 files changed, 607 insertions, 0 deletions
diff --git a/ejabberd_kai.nix b/ejabberd_kai.nix
new file mode 100644
index 0000000..aed1acc
--- /dev/null
+++ b/ejabberd_kai.nix
@@ -0,0 +1,607 @@
+{ config, pkgs, lib, ... }:
+
+let
+ cfg = config.services.ejabberd;
+ inherit (config.security.acme) certs;
+
+ runtimeDir = "/run/ejabberd";
+
+ # wouldn’t it be cool if we could attach extra data?
+ biboumiCfg = rec {
+ user = "biboumi";
+ group = "biboumi";
+ database = {
+ user = user;
+ name = "biboumi";
+ };
+ } // config.services.biboumi;
+
+ fqdn = "toastal.in.th";
+
+ database = {
+ name = "ejabberd";
+ user = cfg.user;
+ };
+
+ ports = {
+ mqtt = 1883;
+ c2s = 5222;
+ c2ss = 5223;
+ s2s = 5269;
+ s2ss = 5270;
+ http = 5280;
+ https = 5443;
+ irc = 6667;
+ ircs = 6697;
+ proxy65 = 7777;
+ matrix = 8448;
+ };
+
+ pow = lib.fix (
+ self: base: power:
+ if power != 0
+ then base * (self base (power - 1))
+ else 1
+ );
+
+ ejabberd_config = {
+ # Use systemd EnvironmentFile + EJABBERD_MACRO_* to define
+ define_macro = {
+ BIBOUMI_SECRET = null;
+ MATRIX_SECRET = null;
+ TURN_SECRET = null;
+ };
+ loglevel = "notice";
+ log_rotate_size = 1 * (pow 2 30);
+ log_rotate_count = 1;
+ hide_sensitive_log_data = true;
+ hosts = [ fqdn ];
+ language = "en";
+ default_db = "mnesia";
+ acme.auto = false;
+ ca_file = "${config.environment.etc."ssl/certs/ca-certificates.crt".source}";
+ certfiles = [
+ "${certs.${fqdn}.directory}/*.pem"
+ ];
+ c2s_tls_compression = true;
+ s2s_access = "s2s";
+ s2s_tls_compression = true;
+ s2s_use_starttls = true;
+ new_sql_schema = true;
+ captcha_cmd = "${cfg.package.out}/lib/ejabberd-${cfg.package.version}/priv/bin/captcha.sh";
+ captcha_url = "https://xmpp.@HOST@/captcha";
+ acl = {
+ admin = [
+ { user = "admin@${fqdn}"; }
+ { user = "toastal@${fqdn}"; }
+ ];
+ local.user_regexp = "";
+ loopback.ip = [
+ "127.0.0.1/8"
+ "::1/128"
+ ];
+ };
+ access_rules = {
+ c2s = {
+ deny = "blocked";
+ allow = "all";
+ };
+ s2s = {
+ allow = "all";
+ };
+ local.allow = "local";
+ announce.allow = "admin";
+ configure.allow = "admin";
+ muc_create.allow = "local";
+ pubsub_createnode.allow = "local";
+ trusted_network.allow = "loopback";
+ };
+ api_permissions = {
+ "console commands" = {
+ from = [ "ejabberd_ctl" ];
+ who = "all";
+ what = "*";
+ };
+ "admin access" = {
+ who = {
+ access.allow = [
+ { acl = "local"; }
+ { acl = "admin"; }
+ ];
+ oauth = {
+ scope = "ejabberd:admin";
+ access.allow = [
+ { acl = "local"; }
+ { acl = "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;
+ };
+ modules = {
+ mod_adhoc = { };
+ mod_admin_extra = { };
+ mod_announce = {
+ access = "announce";
+ };
+ mod_avatar = { };
+ mod_blocking = { };
+ mod_bosh = { };
+ mod_caps = { };
+ mod_carboncopy = { };
+ mod_client_state = { };
+ mod_configure = { };
+ mod_disco = {
+ server_info = [
+ {
+ modules = "all";
+ name = "abuse-addresses";
+ urls = [ "mailto:toastal+abuse@posteo.net" ];
+ }
+ ];
+ };
+ mod_host_meta = {
+ bosh_service_url = "https://xmpp.@HOST@/bosh";
+ websocket_url = "wss://xmpp.@HOST@/ws";
+ };
+ mod_http_api = { };
+ #mod_http_fileserver.docroot = "${cfg.spoolDir}/http";
+ mod_http_upload = {
+ docroot = "${cfg.spoolDir}/uploads";
+ dir_mode = "0755";
+ file_mode = "0644";
+ get_url = "https://xmpp.@HOST@/upload";
+ put_url = "https://xmpp.@HOST@/upload";
+ max_size = 4 * (pow 2 30);
+ custom_headers = {
+ Access-Control-Allow-Origin = "https://@HOST@,https://xmpp.@HOST@,https://social.@HOST@";
+ Access-Control-Allow-Methods = "GET,HEAD,PUT,OPTIONS";
+ Access-Control-Allow-Headers = "Content-Type";
+ };
+ };
+ mod_http_upload_quota = {
+ max_days = 2 * 365;
+ };
+ mod_last = { };
+ mod_matrix_gw = {
+ host = "matrix.@HOST@";
+ key_name = "use_xmpp";
+ key = "MATRIX_SECRET";
+ };
+ mod_mam = {
+ assume_mam_usage = true;
+ default = "always";
+ db_type = "sql";
+ compress_xml = true;
+ };
+ mod_mqtt = { };
+ mod_muc = {
+ hosts = [ "chat.@HOST@" ];
+ access = [ "allow" ];
+ access_admin = [
+ { allow = "admin"; }
+ ];
+ access_create = "muc_create";
+ access_persistent = "muc_create";
+ access_mam = [ "allow" ];
+ default_room_options = {
+ allow_change_subj = true;
+ allow_private_messages_from_visitors = "moderators";
+ allow_subscription = true;
+ allow_user_invites = true;
+ allowpm = "participants";
+ lang = "en";
+ mam = true;
+ max_users = 512;
+ moderated = true;
+ };
+ };
+ mod_muc_admin = { };
+ mod_offline = {
+ access_max_user_messages = "max_user_offline_messages";
+ use_mam_for_storage = true;
+ };
+ mod_ping = { };
+ mod_private = {
+ db_type = "sql";
+ };
+ mod_privacy = {
+ db_type = "sql";
+ };
+ mod_proxy65 = {
+ hosts = [
+ "proxy.@HOST@"
+ ];
+ port = ports.proxy65;
+ access = "local";
+ max_connections = 8;
+ };
+ mod_pubsub = {
+ hosts = [
+ "tidings.@HOST@"
+ ];
+ access_createnode = "pubsub_createnode";
+ ignore_pep_from_offline = false;
+ last_item_cache = false;
+ max_items_node = 2048;
+ default_node_config = {
+ max_items = 2048;
+ };
+ plugins = [ "flat" "pep" ];
+ force_node_config = {
+ "storage:bookmarks".access_model = "whitelist";
+ "eu.siacs.conversations.axolotl.*".access_model = "open";
+ "urn:xmpp:bookmarks:0" = {
+ access_model = "whitelist";
+ send_last_published_item = "never";
+ max_items = "infinity";
+ persist_items = true;
+ };
+ "urn:xmpp:bookmarks:1" = {
+ access_model = "whitelist";
+ send_last_published_item = "never";
+ max_items = "infinity";
+ persist_items = true;
+ };
+ "urn:xmpp:pubsub:movim-public-subscription" = {
+ access_model = "whitelist";
+ max_items = "infinity";
+ persist_items = true;
+ };
+ "urn:xmpp:microblog:0" = {
+ notify_retract = true;
+ max_items = "infinity";
+ persist_items = true;
+ };
+ "urn:xmpp:microblog:0:comments*" = {
+ access_model = "open";
+ notify_retract = true;
+ max_items = "infinity";
+ persist_items = true;
+ };
+ };
+ };
+ mod_push = { };
+ mod_push_keepalive = { };
+ mod_register = {
+ ip_access = "trusted_network";
+ };
+ mod_roster = {
+ versioning = true;
+ };
+ mod_s2s_dialback = { };
+ mod_shared_roster = { };
+ mod_stream_mgmt = { };
+ mod_stun_disco = {
+ services = map (type: { inherit type; host = "turn.${fqdn}"; port = 3478; }) [ "turn" "turns" ];
+ secret = "TURN_SECRET";
+ };
+ mod_time = { };
+ mod_vcard = {
+ db_type = "sql";
+ };
+ mod_vcard_xupdate = { };
+ };
+ listen = [
+ {
+ module = "ejabberd_c2s";
+ port = ports.c2s;
+ max_stanza_size = 262144;
+ #shaper = "c2s_shaper";
+ access = "c2s";
+ starttls_required = true;
+ }
+ {
+ module = "ejabberd_c2s";
+ port = ports.c2ss;
+ max_stanza_size = 262144;
+ #shaper = "c2s_shaper";
+ access = "c2s";
+ tls = true;
+ starttls_required = true;
+ }
+ {
+ module = "ejabberd_s2s_in";
+ port = ports.s2s;
+ max_stanza_size = 524288;
+ shaper = "fast";
+ }
+ {
+ module = "ejabberd_s2s_in";
+ port = ports.s2ss;
+ tls = true;
+ max_stanza_size = 524288;
+ shaper = "fast";
+ }
+ {
+ module = "ejabberd_http";
+ port = ports.http;
+ tls = false;
+ request_handlers = { };
+ }
+ {
+ module = "ejabberd_http";
+ port = ports.https;
+ tls = true;
+ request_handlers = {
+ "/admin" = "ejabberd_web_admin";
+ "/api" = "mod_http_api";
+ "/bosh" = "mod_bosh";
+ "/captcha" = "ejabberd_captcha";
+ "/upload" = "mod_http_upload";
+ "/ws" = "ejabberd_http_ws";
+ "/.well-known/host-meta" = "mod_host_meta";
+ "/.well-known/host-meta.json" = "mod_host_meta";
+ };
+ }
+ {
+ module = "mod_mqtt";
+ port = ports.mqtt;
+ backlog = 1024;
+ }
+ {
+ module = "ejabberd_service";
+ port = biboumiCfg.settings.port;
+ hosts = {
+ "${biboumiCfg.settings.hostname}" = {
+ password = "BIBOUMI_SECRET";
+ };
+ };
+ }
+ {
+ module = "ejabberd_http";
+ port = ports.matrix;
+ tls = true;
+ request_handlers = {
+ "/_matrix" = "mod_matrix_gw";
+ };
+ }
+ ];
+ host_config = {
+ "${fqdn}" = {
+ auth_method = "sql";
+ auth_password_format = "scram";
+ sql_type = "pgsql";
+ sql_server = "localhost";
+ sql_port = config.services.postgresql.settings.port;
+ sql_database = database.name;
+ sql_username = database.user;
+ };
+ };
+ };
+
+ ejabberd_config_file =
+ let
+ settingsFormat = pkgs.formats.yaml { };
+ in
+ settingsFormat.generate "ejabberd.yml" ejabberd_config;
+in
+{
+ users = {
+ groups = {
+ ${biboumiCfg.group} = { };
+ xmpp = { };
+ };
+ users = {
+ ${cfg.user} = {
+ extraGroups = [
+ "xmpp"
+ certs.${fqdn}.group
+ ];
+ };
+ ${biboumiCfg.user} = {
+ isSystemUser = true;
+ group = biboumiCfg.group;
+ extraGroups = [ "xmpp" ];
+ };
+ };
+ };
+
+ security.acme.certs.${fqdn} = {
+ postRun = lib.mkAfter "systemctl restart ejabberd.service";
+ };
+
+ services.ejabberd = {
+ enable = true;
+ package = pkgs.ejabberd.override (old: {
+ withImagemagick = true;
+ withPgsql = true;
+ withTools = true;
+ withZlib = true;
+ });
+ configFile = ejabberd_config_file;
+ ctlConfig = ''
+ CONFIG_DIR=${runtimeDir}
+ ERL_CRASH_DUMP=${cfg.logsDir}/erl_crash.dump
+ '';
+ imagemagick = true;
+ };
+
+ systemd.services.ejabberd-data-setup = {
+ description = "Ejabberd Setup: creates EnvironmentFile & so on";
+ wantedBy = [ "multi-user.target" ];
+ before = [ "ejabberd.service" ];
+ requiredBy = [ "ejabberd.service" ];
+ serviceConfig = {
+ Type = "oneshot";
+ User = cfg.user;
+ Group = cfg.group;
+ UMask = "077";
+ RuntimeDirectory = lib.removePrefix "/run/" runtimeDir;
+ RuntimeDirectoryMode = "700";
+ RemainAfterExit = true;
+ ProtectHome = true;
+ PrivateTmp = true;
+ };
+ script = /* sh */ ''
+ mkdir -p "${runtimeDir}"
+ touch "${runtimeDir}/.env" "${runtimeDir}/inetrc"
+ chmod 600 "${runtimeDir}/.env"
+ old_umask=$(umask)
+ umask 0177
+ cat << EOF > "${runtimeDir}/.env"
+ EJABBERD_MACRO_BIBOUMI_SECRET="$(${lib.getExe pkgs.gawk} -F '=' '{a[$1]=$2} END {print(a["password"])}' "${biboumiCfg.credentialsFile}")"
+ EJABBERD_MACRO_MATRIX_SECRET="$(cat "/var/secrets/ejabberd/matrix.key")"
+ EJABBERD_MACRO_TURN_SECRET="$(cat "/var/secrets/turn-server/static-auth-secret.txt")"
+ EOF
+ '';
+ };
+
+ systemd.services.ejabberd = {
+ requires = [ "ejabberd-data-setup.service" "postgresql.service" ];
+ wantedBy = [ "biboumi.service" ];
+ after = [ "ejabberd-data-setup.service" "postgresql.service" ];
+ serviceConfig = {
+ StartupMemoryMax = "12G";
+ MemoryMax = "8G";
+ RuntimeDirectory = lib.removePrefix "/run/" runtimeDir;
+ RuntimeDirectoryMode = "700";
+ RuntimeDirectoryPreserve = "yes";
+ EnvironmentFile = "${runtimeDir}/.env";
+ ProtectHome = true;
+ PrivateTmp = true;
+ };
+ };
+
+ services.biboumi = {
+ enable = true;
+ package = pkgs.biboumi.override {
+ withPostgreSQL = true;
+ withSQLite = false;
+ };
+ settings = {
+ admin = map (u: u.user) ejabberd_config.acl.admin;
+ hostname = "irc.${fqdn}";
+ db_name = "postgresql://${biboumiCfg.database.user}@localhost:${builtins.toString config.services.postgresql.settings.port}/";
+ password = null;
+ ca_file = "${config.environment.etc."ssl/certs/ca-certificates.crt".source}";
+ };
+ credentialsFile = "/var/secrets/biboumi/biboumi.cfg";
+ openFirewall = true;
+ };
+
+ systemd.services.biboumi = {
+ partOf = [ "ip-change@enp2s0.target" ];
+ before = [ "ip-change@enp2s0.target" ];
+ requires = [
+ "ejabberd.service"
+ "postgresql.service"
+ ];
+ after = [ "ejabberd.service" ];
+ serviceConfig = {
+ MemoryMax = "256M";
+ };
+ };
+
+ services.postgresql = {
+ enable = true;
+ ensureDatabases = [
+ database.name
+ biboumiCfg.database.name
+ ];
+ ensureUsers = [
+ {
+ name = database.user;
+ ensureDBOwnership = true;
+ }
+ {
+ name = biboumiCfg.database.user;
+ ensureDBOwnership = true;
+ }
+ ];
+ authentication = ''
+ host ${database.name} ${database.user} localhost trust
+ host ${biboumiCfg.database.name} ${biboumiCfg.database.user} localhost trust
+ '';
+ };
+
+ services.h2o = {
+ enable = true;
+ hosts = {
+ "matrix.${fqdn}" = {
+ tls.policy = "only";
+ acme.useHost = fqdn;
+ settings = {
+ paths."/" = {
+ "proxy.reverse.url" = "http://matrix.${fqdn}:${builtins.toString ports.matrix}";
+ "proxy.ssl.verify-peer" = "OFF";
+ "proxy.tunnel" = "ON";
+ };
+ };
+ };
+ "proxy.${fqdn}" = {
+ tls.policy = "only";
+ acme.useHost = fqdn;
+ settings = {
+ paths."/" = {
+ "proxy.reverse.url" = "http://proxy.${fqdn}:${builtins.toString ports.proxy65}";
+ "proxy.ssl.verify-peer" = "OFF";
+ "proxy.tunnel" = "ON";
+ };
+ };
+ };
+ "http.xmpp.${fqdn}" = {
+ serverName = "xmpp.${fqdn}";
+ settings = {
+ paths."/" = {
+ "proxy.reverse.url" = "https://xmpp.${fqdn}:${builtins.toString ports.http}";
+ "proxy.tunnel" = "ON";
+ };
+ };
+ };
+ "tls.xmpp.${fqdn}" = {
+ serverName = "xmpp.${fqdn}";
+ tls.policy = "only";
+ acme.useHost = fqdn;
+ settings = {
+ paths."/" = {
+ "proxy.reverse.url" = "https://xmpp.${fqdn}:${builtins.toString ports.https}";
+ "proxy.ssl.verify-peer" = "OFF";
+ "proxy.tunnel" = "ON";
+ };
+ };
+ };
+ };
+ };
+
+ systemd.services.h2o.wants = [ "ejabberd.service" ];
+
+ systemd.tmpfiles.settings."10-ejabberd" = {
+ "${runtimeDir}".d = { inherit (cfg) user group; mode = "0700"; };
+ "${runtimeDir}/.env".f = { inherit (cfg) user group; mode = "0600"; };
+ "${runtimeDir}/inetrc".f = { inherit (cfg) user group; mode = "0600"; };
+ "${runtimeDir}/vm.args".f = { inherit (cfg) user group; mode = "0600"; };
+ "/run/biboumi".d = { inherit (biboumiCfg) user group; mode = "0740"; };
+ };
+
+ networking = {
+ firewall.allowedTCPPorts = with ports; [
+ c2s
+ c2ss
+ s2s
+ c2ss
+ http
+ https
+ irc
+ ircs
+ matrix
+ mqtt
+ proxy65
+ ];
+ nftables.ruleset = ''
+ add rule inet filter output meta skuid biboumi tcp accept
+ '';
+ };
+}