diff --git a/cd.nix b/cd.nix index 47bd4c869..b105fbb8d 100644 --- a/cd.nix +++ b/cd.nix @@ -6,7 +6,7 @@ ./modules/base-cac-CentOS-7-64bit.nix ./modules/exim-cd.nix - #./modules/ejabberd-cd.nix # XXX echtes modul + ./modules/ejabberd-cd.nix # XXX echtes modul ./modules/iptables-cd.nix ./modules/networking-cd.nix ./modules/retiolum.nix @@ -29,10 +29,9 @@ services.cron.enable = false; - #services.ejabberd-cd = { - # enable = true; - # #virtualHosts = ''"jabber.viljetic.de", "viljetic.de"''; - #}; + services.ejabberd-cd = { + enable = true; + }; services.journald.extraConfig = '' SystemMaxUse=1G diff --git a/modules/ejabberd-cd.nix b/modules/ejabberd-cd.nix new file mode 100644 index 000000000..e836d2cdd --- /dev/null +++ b/modules/ejabberd-cd.nix @@ -0,0 +1,856 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) ejabberd writeScript writeScriptBin utillinux; + inherit (lib) makeSearchPath; + + cfg = config.services.ejabberd-cd; + + + +in + +{ + + ####### interface + + options = { + + services.ejabberd-cd = { + + enable = mkOption { + default = false; + description = "Whether to enable ejabberd server"; + }; + + config = mkOption { + type = types.string; + default = ""; + description = '' + TODO + ''; + }; + + user = mkOption { + type = types.string; + default = "ejabberd"; + description = '' + TODO + ''; + }; + + group = mkOption { + type = types.string; + default = "ejabberd"; + description = '' + TODO + ''; + }; + + + # spoolDir = mkOption { + # default = "/var/lib/ejabberd"; + # description = "Location of the spooldir of ejabberd"; + # }; + + # logsDir = mkOption { + # default = "/var/log/ejabberd"; + # description = "Location of the logfile directory of ejabberd"; + # }; + + # confDir = mkOption { + # default = "/var/ejabberd"; + # description = "Location of the config directory of ejabberd"; + # }; + + # virtualHosts = mkOption { + # default = "\"localhost\""; + # description = "Virtualhosts that ejabberd should host. Hostnames are surrounded with doublequotes and separated by commas"; + # }; + + # loadDumps = mkOption { + # default = []; + # description = "Configuration dump that should be loaded on the first startup"; + # example = literalExample "[ ./myejabberd.dump ]"; + # }; + + # config + }; + + }; + + + ####### implementation + + config = + let + my-ejabberdctl = writeScriptBin "ejabberdctl" '' + #! /bin/sh + set -euf + exec env \ + SPOOLDIR=/var/ejabberd \ + EJABBERD_CONFIG_PATH=/etc/ejabberd.cfg \ + ${ejabberd}/bin/ejabberdctl \ + --logs /var/ejabberd \ + "$@" + ''; + in + mkIf cfg.enable { + #environment.systemPackages = [ pkgs.ejabberd ]; + + environment = { + etc."ejabberd.cfg".text = '' + %%% + %%% ejabberd configuration file + %%% + %%%' + + %%% The parameters used in this configuration file are explained in more detail + %%% in the ejabberd Installation and Operation Guide. + %%% Please consult the Guide in case of doubts, it is included with + %%% your copy of ejabberd, and is also available online at + %%% http://www.process-one.net/en/ejabberd/docs/ + + %%% This configuration file contains Erlang terms. + %%% In case you want to understand the syntax, here are the concepts: + %%% + %%% - The character to comment a line is % + %%% + %%% - Each term ends in a dot, for example: + %%% override_global. + %%% + %%% - A tuple has a fixed definition, its elements are + %%% enclosed in {}, and separated with commas: + %%% {loglevel, 4}. + %%% + %%% - A list can have as many elements as you want, + %%% and is enclosed in [], for example: + %%% [http_poll, web_admin, tls] + %%% + %%% - A keyword of ejabberd is a word in lowercase. + %%% Strings are enclosed in "" and can contain spaces, dots, ... + %%% {language, "en"}. + %%% {ldap_rootdn, "dc=example,dc=com"}. + %%% + %%% - This term includes a tuple, a keyword, a list, and two strings: + %%% {hosts, ["jabber.example.net", "im.example.com"]}. + %%% + + + %%%. ======================= + %%%' OVERRIDE STORED OPTIONS + + %% + %% Override the old values stored in the database. + %% + + %% + %% Override global options (shared by all ejabberd nodes in a cluster). + %% + %%override_global. + + %% + %% Override local options (specific for this particular ejabberd node). + %% + %%override_local. + + %% + %% Remove the Access Control Lists before new ones are added. + %% + %%override_acls. + + + %%%. ========= + %%%' DEBUGGING + + %% + %% loglevel: Verbosity of log files generated by ejabberd. + %% 0: No ejabberd log at all (not recommended) + %% 1: Critical + %% 2: Error + %% 3: Warning + %% 4: Info + %% 5: Debug + %% + {loglevel, 3}. + + %% + %% watchdog_admins: Only useful for developers: if an ejabberd process + %% consumes a lot of memory, send live notifications to these XMPP + %% accounts. + %% + %%{watchdog_admins, ["bob@example.com"]}. + + + %%%. ================ + %%%' SERVED HOSTNAMES + + %% + %% hosts: Domains served by ejabberd. + %% You can define one or several, for example: + %% {hosts, ["example.net", "example.com", "example.org"]}. + %% + {hosts, ["jabber.viljetic.de"]}. + + %% + %% route_subdomains: Delegate subdomains to other XMPP servers. + %% For example, if this ejabberd serves example.org and you want + %% to allow communication with an XMPP server called im.example.org. + %% + %%{route_subdomains, s2s}. + + + %%%. =============== + %%%' LISTENING PORTS + + %% + %% listen: The ports ejabberd will listen on, which service each is handled + %% by and what options to start it with. + %% + {listen, + [ + + {5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + starttls, + {certfile, "/etc/nixos/secrets/ejabberd.cd.retiolum.pem"}, + + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + {5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]}, + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{{3478, udp}, ejabberd_stun, []}, + + {5280, ejabberd_http, [ + %%{request_handlers, + %% [ + %% {["pub", "archive"], mod_http_fileserver} + %% ]}, + captcha, + http_bind, + http_poll, + %%register, + web_admin + ]} + + ]}. + + %% + %% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. + %% Allowed values are: false optional required required_trusted + %% You must specify a certificate file. + %% + {s2s_use_starttls, required}. + + %% + %% s2s_certfile: Specify a certificate file. + %% + {s2s_certfile, "/etc/nixos/secrets/ejabberd.cd.retiolum.pem"}. + + %% + %% domain_certfile: Specify a different certificate for each served hostname. + %% + %%{domain_certfile, "example.org", "/path/to/example_org.pem"}. + %%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + + %% + %% S2S whitelist or blacklist + %% + %% Default s2s policy for undefined hosts. + %% + %%{s2s_default_policy, allow}. + + %% + %% Allow or deny communication with specific servers. + %% + %%{{s2s_host, "goodhost.org"}, allow}. + %%{{s2s_host, "badhost.org"}, deny}. + + %% + %% Outgoing S2S options + %% + %% Preferred address families (which to try first) and connect timeout + %% in milliseconds. + %% + %%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + + + %%%. ============== + %%%' AUTHENTICATION + + %% + %% auth_method: Method used to authenticate the users. + %% The default method is the internal. + %% If you want to use a different method, + %% comment this line and enable the correct ones. + %% + {auth_method, internal}. + %% + %% Store the plain passwords or hashed for SCRAM: + %%{auth_password_format, plain}. + %%{auth_password_format, scram}. + %% + %% Define the FQDN if ejabberd doesn't detect it: + %%{fqdn, "server3.example.com"}. + + %% + %% Authentication using external script + %% Make sure the script is executable by ejabberd. + %% + %%{auth_method, external}. + %{extauth_program, "$ {ejabberd-auth}"}. + + %% + %% Authentication using ODBC + %% Remember to setup a database in the next section. + %% + %%{auth_method, odbc}. + + %% + %% Authentication using PAM + %% + %%{auth_method, pam}. + %%{pam_service, "pamservicename"}. + + %% + %% Authentication using LDAP + %% + %%{auth_method, ldap}. + %% + %% List of LDAP servers: + %%{ldap_servers, ["localhost"]}. + %% + %% Encryption of connection to LDAP servers: + %%{ldap_encrypt, none}. + %%{ldap_encrypt, tls}. + %% + %% Port to connect to on LDAP servers: + %%{ldap_port, 389}. + %%{ldap_port, 636}. + %% + %% LDAP manager: + %%{ldap_rootdn, "dc=example,dc=com"}. + %% + %% Password of LDAP manager: + %%{ldap_password, "******"}. + %% + %% Search base of LDAP directory: + %%{ldap_base, "dc=example,dc=com"}. + %% + %% LDAP attribute that holds user ID: + %%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. + %% + %% LDAP filter: + %%{ldap_filter, "(objectClass=shadowAccount)"}. + + %% + %% Anonymous login support: + %% auth_method: anonymous + %% anonymous_protocol: sasl_anon | login_anon | both + %% allow_multiple_connections: true | false + %% + %%{host_config, "public.example.org", [{auth_method, anonymous}, + %% {allow_multiple_connections, false}, + %% {anonymous_protocol, sasl_anon}]}. + %% + %% To use both anonymous and internal authentication: + %% + %%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + + %%%. ============== + %%%' DATABASE SETUP + + %% ejabberd by default uses the internal Mnesia database, + %% so you do not necessarily need this section. + %% This section provides configuration examples in case + %% you want to use other database backends. + %% Please consult the ejabberd Guide for details on database creation. + + %% + %% MySQL server: + %% + %%{odbc_server, {mysql, "server", "database", "username", "password"}}. + %% + %% If you want to specify the port: + %%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + + %% + %% PostgreSQL server: + %% + %%{odbc_server, {pgsql, "server", "database", "username", "password"}}. + %% + %% If you want to specify the port: + %%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. + %% + %% If you use PostgreSQL, have a large database, and need a + %% faster but inexact replacement for "select count(*) from users" + %% + %%{pgsql_users_number_estimate, true}. + + %% + %% ODBC compatible or MSSQL server: + %% + %%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + + %% + %% Number of connections to open to the database for each virtual host + %% + %%{odbc_pool_size, 10}. + + %% + %% Interval to make a dummy SQL request to keep the connections to the + %% database alive. Specify in seconds: for example 28800 means 8 hours + %% + %%{odbc_keepalive_interval, undefined}. + + + %%%. =============== + %%%' TRAFFIC SHAPERS + + %% + %% The "normal" shaper limits traffic speed to 1000 B/s + %% + {shaper, normal, {maxrate, 1000}}. + + %% + %% The "fast" shaper limits traffic speed to 50000 B/s + %% + {shaper, fast, {maxrate, 50000}}. + + %% + %% This option specifies the maximum number of elements in the queue + %% of the FSM. Refer to the documentation for details. + %% + {max_fsm_queue, 1000}. + + + %%%. ==================== + %%%' ACCESS CONTROL LISTS + + %% + %% The 'admin' ACL grants administrative privileges to XMPP accounts. + %% You can put here as many accounts as you want. + %% + %%{acl, admin, {user, "aleksey", "localhost"}}. + %%{acl, admin, {user, "ermine", "example.org"}}. + + %% + %% Blocked users + %% + %%{acl, blocked, {user, "baduser", "example.org"}}. + %%{acl, blocked, {user, "test"}}. + + %% + %% Local users: don't modify this line. + %% + {acl, local, {user_regexp, ""}}. + + %% + %% More examples of ACLs + %% + %%{acl, jabberorg, {server, "jabber.org"}}. + %%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. + %%{acl, test, {user_regexp, "^test"}}. + %%{acl, test, {user_glob, "test*"}}. + + %% + %% Define specific ACLs in a virtual host. + %% + %%{host_config, "localhost", + %% [ + %% {acl, admin, {user, "bob-local", "localhost"}} + %% ] + %%}. + + + %%%. ============ + %%%' ACCESS RULES + + %% Maximum number of simultaneous sessions allowed for a single user: + {access, max_user_sessions, [{10, all}]}. + + %% Maximum number of offline messages that users can have: + {access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + + %% This rule allows access only for local users: + {access, local, [{allow, local}]}. + + %% Only non-blocked users can use c2s connections: + {access, c2s, [{deny, blocked}, + {allow, all}]}. + + %% For C2S connections, all users except admins use the "normal" shaper + {access, c2s_shaper, [{none, admin}, + {normal, all}]}. + + %% All S2S connections use the "fast" shaper + {access, s2s_shaper, [{fast, all}]}. + + %% Only admins can send announcement messages: + {access, announce, [{allow, admin}]}. + + %% Only admins can use the configuration interface: + {access, configure, [{allow, admin}]}. + + %% Admins of this server are also admins of the MUC service: + {access, muc_admin, [{allow, admin}]}. + + %% Only accounts of the local ejabberd server can create rooms: + {access, muc_create, [{allow, local}]}. + + %% All users are allowed to use the MUC service: + {access, muc, [{allow, all}]}. + + %% Only accounts on the local ejabberd server can create Pubsub nodes: + {access, pubsub_createnode, [{allow, local}]}. + + %% In-band registration allows registration of any possible username. + %% To disable in-band registration, replace 'allow' with 'deny'. + {access, register, [{allow, all}]}. + + %% By default the frequency of account registrations from the same IP + %% is limited to 1 account every 10 minutes. To disable, specify: infinity + %%{registration_timeout, 600}. + + %% + %% Define specific Access Rules in a virtual host. + %% + %%{host_config, "localhost", + %% [ + %% {access, c2s, [{allow, admin}, {deny, all}]}, + %% {access, register, [{deny, all}]} + %% ] + %%}. + + + %%%. ================ + %%%' DEFAULT LANGUAGE + + %% + %% language: Default language used for server messages. + %% + {language, "en"}. + + %% + %% Set a different default language in a virtual host. + %% + %%{host_config, "localhost", + %% [{language, "ru"}] + %%}. + + + %%%. ======= + %%%' CAPTCHA + + %% + %% Full path to a script that generates the image. + %% + %%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}. + + %% + %% Host for the URL and port where ejabberd listens for CAPTCHA requests. + %% + %%{captcha_host, "example.org:5280"}. + + %% + %% Limit CAPTCHA calls per minute for JID/IP to avoid DoS. + %% + %%{captcha_limit, 5}. + + %%%. ======= + %%%' MODULES + + %% + %% Modules enabled in all ejabberd virtual hosts. + %% + {modules, + [ + {mod_adhoc, []}, + {mod_announce, [{access, announce}]}, % recommends mod_adhoc + {mod_blocking,[]}, % requires mod_privacy + {mod_caps, []}, + {mod_configure,[]}, % requires mod_adhoc + {mod_disco, []}, + %%{mod_echo, [{host, "echo.localhost"}]}, + {mod_irc, []}, + {mod_http_bind, []}, + %%{mod_http_fileserver, [ + %% {docroot, "/var/www"}, + %% {accesslog, "/var/log/ejabberd/access.log"} + %% ]}, + {mod_last, []}, + {mod_muc, [ + %%{host, "conference.@HOST@"}, + {access, muc}, + {access_create, muc_create}, + {access_persistent, muc_create}, + {access_admin, muc_admin} + ]}, + %%{mod_muc_log,[]}, + {mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, + {mod_ping, []}, + %%{mod_pres_counter,[{count, 5}, {interval, 60}]}, + {mod_privacy, []}, + {mod_private, []}, + %%{mod_proxy65,[]}, + {mod_pubsub, [ + {access_createnode, pubsub_createnode}, + {ignore_pep_from_offline, true}, % reduces resource comsumption, but XEP incompliant + %%{ignore_pep_from_offline, false}, % XEP compliant, but increases resource comsumption + {last_item_cache, false}, + {plugins, ["flat", "hometree", "pep"]} % pep requires mod_caps + ]}, + {mod_register, [ + %% + %% Protect In-Band account registrations with CAPTCHA. + %% + %%{captcha_protected, true}, + + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {"Welcome!", + "Hi.\nWelcome to this XMPP server."}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + %%{mod_register_web, [ + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]} + %% ]}, + {mod_roster, []}, + %%{mod_service_log,[]}, + {mod_shared_roster,[]}, + {mod_stats, []}, + {mod_time, []}, + {mod_vcard, []}, + {mod_version, []} + ]}. + + %% + %% Enable modules with custom options in a specific virtual host + %% + %%{host_config, "localhost", + %% [{{add, modules}, + %% [ + %% {mod_echo, [{host, "mirror.localhost"}]} + %% ] + %% } + %% ]}. + + + %%%. + %%%' + + %%% $Id$ + + %%% Local Variables: + %%% mode: erlang + %%% End: + %%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: + ''; + # TODO properly configured wrapper + systemPackages = [ my-ejabberdctl ]; + }; + #exim_user = ${cfg.user} + #exim_group = ${cfg.group} + #exim_path = /var/setuid-wrappers/exim + #spool_directory = ${cfg.spoolDir} + #${cfg.config} + + users.extraUsers = singleton { + name = "ejabberd"; + description = "TODO"; + uid = 405222; + group = "ejabberd"; + home = "/var/ejabberd"; + createHome = true; + }; + + users.extraGroups = singleton { + name = "ejabberd"; + gid = 405222; + }; + + #security.setuidPrograms = [ "exim" ]; + + systemd.services.ejabberd = { + description = "ejabberd XMPP Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + reloadIfChanged = true; + serviceConfig = { + ExecStart = "${my-ejabberdctl}/bin/ejabberdctl start"; + ExecStop = "${my-ejabberdctl}/bin/ejabberdctl stop"; + ExecReload = "${my-ejabberdctl}/bin/ejabberdctl restart"; + Type = "oneshot"; + RemainAfterExit = "yes"; + RestartSec = 5; + LimitNOFILE = 16000; + User = "ejabberd"; + Group = "ejabberd"; + }; + }; + + systemd.services.ejabberd-prepare = { + description = "ejabberd XMPP Preparetion Service"; + requiredBy = [ "ejabberd.service" ]; + serviceConfig = { + Type = "oneshot"; + RestartSec = 5; + ExecStart = "${writeScript "ejabberd-prepare" + '' + #! /bin/sh + set -euf + chown ejabberd: /etc/nixos/secrets/ejabberd.cd.retiolum.pem + '' + }"; + }; + }; + + + + }; + + #config = mkIf cfg.enable { + # environment.systemPackages = [ pkgs.ejabberd ]; + + # jobs.ejabberd = + # { description = "EJabberd server"; + + # startOn = "started network-interfaces"; + # stopOn = "stopping network-interfaces"; + + # environment = { + # PATH = "$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin"; + # }; + + # preStart = + # '' + # PATH="$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin"; + # + # # Initialise state data + # mkdir -p ${cfg.logsDir} + + # if ! test -d ${cfg.spoolDir} + # then + # initialize=1 + # cp -av ${pkgs.ejabberd}/var/lib/ejabberd /var/lib + # fi + + # #if ! test -d ${cfg.confDir} + # #then + # # mkdir -p ${cfg.confDir} + # # cp ${pkgs.ejabberd}/etc/ejabberd/* ${cfg.confDir} + # # sed -e 's|{hosts, \["localhost"\]}.|{hosts, \[${cfg.virtualHosts}\]}.|' ${pkgs.ejabberd}/etc/ejabberd/ejabberd.cfg > ${cfg.confDir}/ejabberd.cfg + # #fi + # mkdir -p ${cfg.confDir} + + + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start + + # ${if cfg.loadDumps == [] then "" else + # '' + # if [ "$initialize" = "1" ] + # then + # # Wait until the ejabberd server is available for use + # count=0 + # while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status + # do + # if [ $count -eq 30 ] + # then + # echo "Tried 30 times, giving up..." + # exit 1 + # fi + + # echo "Ejabberd daemon not yet started. Waiting for 1 second..." + # count=$((count++)) + # sleep 1 + # done + + # ${concatMapStrings (dump: + # '' + # echo "Importing dump: ${dump}" + + # if [ -f ${dump} ] + # then + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump} + # elif [ -d ${dump} ] + # then + # for i in ${dump}/ejabberd-dump/* + # do + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load $i + # done + # fi + # '') cfg.loadDumps} + # fi + # ''} + # ''; + + # postStop = + # '' + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} stop + # ''; + # }; + + # security.pam.services.ejabberd = {}; + + #}; + +}