# docs: # - # example configs: # - # - # - # - enables STUN and TURN # - only over UDP 3478, not firewall-forwarding any TURN port range # - uses stun_disco module (but with no options) # - # - # - # - 2013: # # compliance tests: # - { config, lib, pkgs, ... }: # XXX: avatar support works in MUCs but not DMs let # TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward # TURN port range (inclusive) turnPortLow = 49152; turnPortHigh = 49167; turnPortRange = lib.range turnPortLow turnPortHigh; in { sane.persist.sys.plaintext = [ { user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; } ]; sane.ports.ports = lib.mkMerge ([ { "3478" = { protocol = [ "tcp" "udp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-stun-turn"; }; "5222" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-client-to-server"; }; "5223" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpps-client-to-server"; # XMPP over TLS }; "5269" = { protocol = [ "tcp" ]; visibleTo.wan = true; description = "colin-xmpp-server-to-server"; }; "5270" = { protocol = [ "tcp" ]; visibleTo.wan = true; description = "colin-xmpps-server-to-server"; # XMPP over TLS }; "5280" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-bosh"; }; "5281" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-bosh-https"; }; "5349" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-stun-turn-over-tls"; }; "5443" = { protocol = [ "tcp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-web-services"; # file uploads, websockets, admin }; } ] ++ (builtins.map (port: { "${builtins.toString port}" = let count = port - turnPortLow + 1; numPorts = turnPortHigh - turnPortLow + 1; in { protocol = [ "tcp" "udp" ]; visibleTo.lan = true; visibleTo.wan = true; description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}"; }; }) turnPortRange )); # provide access to certs # TODO: this should just be `acme`. then we also add nginx to the `acme` group. # why is /var/lib/acme/* owned by `nginx` group?? users.users.ejabberd.extraGroups = [ "nginx" ]; security.acme.certs."uninsane.org".extraDomainNames = [ "xmpp.uninsane.org" "muc.xmpp.uninsane.org" "pubsub.xmpp.uninsane.org" "upload.xmpp.uninsane.org" "vjid.xmpp.uninsane.org" ]; # exists so the XMPP server's cert can obtain altNames for all its resources services.nginx.virtualHosts."xmpp.uninsane.org" = { useACMEHost = "uninsane.org"; }; services.nginx.virtualHosts."muc.xmpp.uninsane.org" = { useACMEHost = "uninsane.org"; }; services.nginx.virtualHosts."pubsub.xmpp.uninsane.org" = { useACMEHost = "uninsane.org"; }; services.nginx.virtualHosts."upload.xmpp.uninsane.org" = { useACMEHost = "uninsane.org"; }; services.nginx.virtualHosts."vjid.xmpp.uninsane.org" = { useACMEHost = "uninsane.org"; }; sane.dns.zones."uninsane.org".inet = { # XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs A."xmpp" = "%ANATIVE%"; CNAME."muc.xmpp" = "xmpp"; CNAME."pubsub.xmpp" = "xmpp"; CNAME."upload.xmpp" = "xmpp"; CNAME."vjid.xmpp" = "xmpp"; # _Service._Proto.Name TTL Class SRV Priority Weight Port Target # - # something's requesting the SRV records for muc.xmpp, so let's include it # nothing seems to request XMPP SRVs for the other records (except @) # lower numerical priority field tells clients to prefer this method SRV."_xmpps-client._tcp.muc.xmpp" = "3 50 5223 xmpp"; SRV."_xmpps-server._tcp.muc.xmpp" = "3 50 5270 xmpp"; SRV."_xmpp-client._tcp.muc.xmpp" = "5 50 5222 xmpp"; SRV."_xmpp-server._tcp.muc.xmpp" = "5 50 5269 xmpp"; SRV."_xmpps-client._tcp" = "3 50 5223 xmpp"; SRV."_xmpps-server._tcp" = "3 50 5270 xmpp"; SRV."_xmpp-client._tcp" = "5 50 5222 xmpp"; SRV."_xmpp-server._tcp" = "5 50 5269 xmpp"; SRV."_stun._udp" = "5 50 3478 xmpp"; SRV."_stun._tcp" = "5 50 3478 xmpp"; SRV."_stuns._tcp" = "5 50 5349 xmpp"; SRV."_turn._udp" = "5 50 3478 xmpp"; SRV."_turn._tcp" = "5 50 3478 xmpp"; SRV."_turns._tcp" = "5 50 5349 xmpp"; }; # TODO: allocate UIDs/GIDs ? services.ejabberd.enable = true; services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml"; systemd.services.ejabberd.preStart = let config-in = pkgs.writeTextFile { name = "ejabberd.yaml.in"; text = '' hosts: - uninsane.org # none | emergency | alert | critical | error | warning | notice | info | debug loglevel: debug # loglevel: info # loglevel: notice acme: auto: false certfiles: - /var/lib/acme/uninsane.org/full.pem # ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/ # ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt pam_userinfotype: jid acl: admin: user: - "colin@uninsane.org" local: user_regexp: "" loopback: ip: - 127.0.0.0/8 - ::1/128 access_rules: local: allow: local c2s_access: allow: all announce: allow: admin configure: allow: admin muc_create: allow: local pubsub_createnode_access: allow: all trusted_network: allow: loopback # docs: shaper_rules: # setting this to above 1 may break outgoing messages # - maybe some servers rate limit? or just don't understand simultaneous connections? max_s2s_connections: 1 max_user_sessions: 10 max_user_offline_messages: 5000 c2s_shaper: fast: all s2s_shaper: med: all # docs: # this limits the bytes/sec. # for example, burst: 3_000_000 and rate: 100_000 means: # - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB shaper: fast: 1000000 med: 500000 # fast: # - rate: 1000000 # - burst_size: 10000000 # med: # - rate: 500000 # - burst_size: 5000000 # see: # s2s_use_starttls: true s2s_use_starttls: optional # lessens 504: remote-server-timeout errors # see: negotiation_timeout: 60 listen: - port: 5222 module: ejabberd_c2s shaper: c2s_shaper starttls: true access: c2s_access - port: 5223 module: ejabberd_c2s shaper: c2s_shaper tls: true access: c2s_access - port: 5269 module: ejabberd_s2s_in shaper: s2s_shaper - port: 5270 module: ejabberd_s2s_in shaper: s2s_shaper tls: true - port: 5443 module: ejabberd_http tls: true request_handlers: /admin: ejabberd_web_admin # TODO: ensure this actually works /api: mod_http_api # ejabberd API endpoint (to control server) /bosh: mod_bosh /upload: mod_http_upload /ws: ejabberd_http_ws # /.well-known/host-meta: mod_host_meta # /.well-known/host-meta.json: mod_host_meta - # STUN+TURN TCP # note that the full port range should be forwarded ("not NAT'd") # `use_turn=true` enables both TURN *and* STUN port: 3478 module: ejabberd_stun transport: tcp use_turn: true turn_min_port: ${builtins.toString turnPortLow} turn_max_port: ${builtins.toString turnPortHigh} turn_ipv4_address: %ANATIVE% - # STUN+TURN UDP port: 3478 module: ejabberd_stun transport: udp use_turn: true turn_min_port: ${builtins.toString turnPortLow} turn_max_port: ${builtins.toString turnPortHigh} turn_ipv4_address: %ANATIVE% - # STUN+TURN TLS over TCP port: 5349 module: ejabberd_stun transport: tcp tls: true certfile: /var/lib/acme/uninsane.org/full.pem use_turn: true turn_min_port: ${builtins.toString turnPortLow} turn_max_port: ${builtins.toString turnPortHigh} turn_ipv4_address: %ANATIVE% # TODO: enable mod_fail2ban # TODO(low): look into mod_http_fileserver for serving macros? modules: # mod_adhoc: {} # mod_announce: # access: admin # allows users to set avatars in vCard # - mod_avatar: {} mod_caps: {} # for mod_pubsub mod_carboncopy: {} # allows multiple clients to receive a user's message # queues messages when recipient is offline, including PEP and presence messages. # compliance test suggests this be enabled mod_client_state: {} # mod_conversejs: TODO: enable once on 21.12 # allows clients like Dino to discover where to upload files mod_disco: server_info: - modules: all name: abuse-addresses urls: - "mailto:admin.xmpp@uninsane.org" - "xmpp:colin@uninsane.org" - modules: all name: admin-addresses urls: - "mailto:admin.xmpp@uninsane.org" - "xmpp:colin@uninsane.org" mod_http_upload: host: upload.xmpp.uninsane.org hosts: - upload.xmpp.uninsane.org put_url: "https://@HOST@:5443/upload" dir_mode: "0750" file_mode: "0750" rm_on_unregister: false # allow discoverability of BOSH and websocket endpoints # TODO: enable once on ejabberd 22.05 (presently 21.04) # mod_host_meta: {} mod_jidprep: {} # probably not needed: lets clients normalize jids mod_last: {} # allow other users to know when i was last online mod_mam: # Mnesia is limited to 2GB, better to use an SQL backend # For small servers SQLite is a good fit and is very easy # to configure. Uncomment this when you have SQL configured: # db_type: sql assume_mam_usage: true default: always mod_muc: access: - allow access_admin: - allow: admin access_create: muc_create access_persistent: muc_create access_mam: - allow history_size: 100 # messages to show new participants host: muc.xmpp.uninsane.org hosts: - muc.xmpp.uninsane.org default_room_options: anonymous: false lang: en persistent: true mam: true mod_muc_admin: {} mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?) access_max_user_messages: max_user_offline_messages store_groupchat: true mod_ping: {} mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis` mod_private: {} # allow local clients to persist arbitrary data on my server # push notifications to services integrated with e.g. Apple/Android. # default is for a maximum amount of PII to be withheld, since these push notifs # generally traverse 3rd party services. can opt to include message body, etc, though. mod_push: {} # i don't fully understand what this does, but it seems aimed at making push notifs more reliable. mod_push_keepalive: {} mod_roster: versioning: true # docs: # s2s dialback to verify inbound messages # unclear to what degree the XMPP network requires this mod_s2s_dialback: {} mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered? mod_stream_mgmt: resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline # fallback for when DNS-based STUN discovery is unsupported. # - see: # docs: # people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?) mod_stun_disco: {} # docs: mod_vcard: allow_return_all: true # all users are discoverable (?) host: vjid.xmpp.uninsane.org hosts: - vjid.xmpp.uninsane.org search: true mod_vcard_xupdate: {} # needed for avatars # docs: mod_pubsub: # needed for avatars access_createnode: pubsub_createnode_access host: pubsub.xmpp.uninsane.org hosts: - pubsub.xmpp.uninsane.org ignore_pep_from_offline: false last_item_cache: true plugins: - pep - flat force_node_config: # ensure client bookmarks are private storage:bookmarks: access_model: whitelist urn:xmpp:avatar:data: access_model: open urn:xmpp:avatar:metadata: access_model: open mod_version: {} ''; }; sed = "${pkgs.gnused}/bin/sed"; in '' ip=$(cat '${config.sane.services.dyn-dns.ipPath}') # config is 444 (not 644), so we want to write out-of-place and then atomically move # TODO: factor this out into `sane-woop` helper? rm -f /var/lib/ejabberd/ejabberd.yaml.new ${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new mv /var/lib/ejabberd/ejabberd.yaml{.new,} ''; sane.services.dyn-dns.restartOnChange = [ "ejabberd.service" ]; }