1{ hostPkgs, lib, ... }:
2
3let
4 movim = {
5 domain = "movim.local";
6 port = 8080;
7 info = "No ToS in tests";
8 description = "NixOS testing server";
9 };
10 ejabberd = {
11 domain = "ejabberd.local";
12 ports = {
13 c2s = 5222;
14 s2s = 5269;
15 http = 5280;
16 };
17 spoolDir = "/var/lib/ejabberd";
18 admin = rec {
19 JID = "${username}@${ejabberd.domain}";
20 username = "romeo";
21 password = "juliet";
22 };
23 };
24
25 # START OF EJABBERD CONFIG ##################################################
26 #
27 # Ejabberd has sparse defaults as it is a generic XMPP server. As such this
28 # config might be longer than expected for a test.
29 #
30 # Movim suggests: https://github.com/movim/movim/wiki/Configure ejabberd
31 #
32 # In the future this may be the default setup
33 # See: https://github.com/NixOS/nixpkgs/pull/312316
34 ejabberd_config_file =
35 let
36 settingsFormat = hostPkgs.formats.yaml { };
37 in
38 settingsFormat.generate "ejabberd.yml" {
39 loglevel = "info";
40 hide_sensitive_log_data = false;
41 hosts = [ ejabberd.domain ];
42 default_db = "mnesia";
43 acme.auto = false;
44 s2s_access = "s2s";
45 s2s_use_starttls = false;
46 new_sql_schema = true;
47 acl = {
48 admin = [
49 { user = ejabberd.admin.JID; }
50 ];
51 local.user_regexp = "";
52 loopback.ip = [
53 "127.0.0.1/8"
54 "::1/128"
55 ];
56 };
57 access_rules = {
58 c2s = {
59 deny = "blocked";
60 allow = "all";
61 };
62 s2s = {
63 allow = "all";
64 };
65 local.allow = "local";
66 announce.allow = "admin";
67 configure.allow = "admin";
68 pubsub_createnode.allow = "local";
69 trusted_network.allow = "loopback";
70 };
71 api_permissions = {
72 "console commands" = {
73 from = [ "ejabberd_ctl" ];
74 who = "all";
75 what = "*";
76 };
77 };
78 shaper = {
79 normal = {
80 rate = 3000;
81 burst_size = 20000;
82 };
83 fast = 100000;
84 };
85 modules = {
86 mod_caps = { };
87 mod_disco = { };
88 mod_mam = { };
89 mod_http_upload = {
90 docroot = "${ejabberd.spoolDir}/uploads";
91 dir_mode = "0755";
92 file_mode = "0644";
93 get_url = "http://@HOST@/upload";
94 put_url = "http://@HOST@/upload";
95 max_size = 65536;
96 custom_headers = {
97 Access-Control-Allow-Origin = "http://@HOST@,http://${movim.domain}";
98 Access-Control-Allow-Methods = "GET,HEAD,PUT,OPTIONS";
99 Access-Control-Allow-Headers = "Content-Type";
100 };
101 };
102 # This PubSub block is required for Movim to work.
103 #
104 # See: https://github.com/movim/movim/wiki/Configure ejabberd#pubsub
105 mod_pubsub = {
106 hosts = [ "pubsub.@HOST@" ];
107 access_createnode = "pubsub_createnode";
108 ignore_pep_from_offline = false;
109 last_item_cache = false;
110 max_items_node = 2048;
111 default_node_config = {
112 max_items = 2048;
113 };
114 plugins = [
115 "flat"
116 "pep"
117 ];
118 force_node_config = {
119 "storage:bookmarks".access_model = "whitelist";
120 "eu.siacs.conversations.axolotl.*".access_model = "open";
121 "urn:xmpp:bookmarks:0" = {
122 access_model = "whitelist";
123 send_last_published_item = "never";
124 max_items = "infinity";
125 persist_items = true;
126 };
127 "urn:xmpp:bookmarks:1" = {
128 access_model = "whitelist";
129 send_last_published_item = "never";
130 max_items = "infinity";
131 persist_items = true;
132 };
133 "urn:xmpp:pubsub:movim-public-subscription" = {
134 access_model = "whitelist";
135 max_items = "infinity";
136 persist_items = true;
137 };
138 "urn:xmpp:microblog:0" = {
139 notify_retract = true;
140 max_items = "infinity";
141 persist_items = true;
142 };
143 "urn:xmpp:microblog:0:comments*" = {
144 access_model = "open";
145 notify_retract = true;
146 max_items = "infinity";
147 persist_items = true;
148 };
149 };
150 };
151 mod_stream_mgmt = { };
152 };
153 listen = [
154 {
155 module = "ejabberd_c2s";
156 port = ejabberd.ports.c2s;
157 max_stanza_size = 262144;
158 access = "c2s";
159 starttls_required = false;
160 }
161 {
162 module = "ejabberd_s2s_in";
163 port = ejabberd.ports.s2s;
164 max_stanza_size = 524288;
165 shaper = "fast";
166 }
167 {
168 module = "ejabberd_http";
169 port = ejabberd.ports.http;
170 request_handlers = {
171 "/upload" = "mod_http_upload";
172 };
173 }
174 ];
175 };
176 # END OF EJABBERD CONFIG ##################################################
177in
178{
179 name = "movim-ejabberd-h2o";
180
181 meta = {
182 maintainers = with lib.maintainers; [ toastal ];
183 };
184
185 nodes = {
186 server =
187 { pkgs, ... }:
188 {
189 environment.systemPackages = [
190 # For testing
191 pkgs.websocat
192 ];
193
194 services.movim = {
195 inherit (movim) domain port;
196 enable = true;
197 verbose = true;
198 podConfig = {
199 inherit (movim) description info;
200 xmppdomain = ejabberd.domain;
201 };
202 database = {
203 type = "postgresql";
204 createLocally = true;
205 };
206 h2o = { };
207 };
208
209 services.ejabberd = {
210 inherit (ejabberd) spoolDir;
211 enable = true;
212 configFile = ejabberd_config_file;
213 imagemagick = false;
214 };
215
216 services.h2o.settings = {
217 compress = "ON";
218 };
219
220 systemd.services.ejabberd = {
221 serviceConfig = {
222 # Certain misconfigurations can cause RAM usage to swell before
223 # crashing; fail sooner with more-than-liberal memory limits
224 StartupMemoryMax = "1G";
225 MemoryMax = "512M";
226 };
227 };
228
229 networking = {
230 firewall.allowedTCPPorts = with ejabberd.ports; [
231 c2s
232 s2s
233 ];
234 extraHosts = ''
235 127.0.0.1 ${movim.domain}
236 127.0.0.1 ${ejabberd.domain}
237 '';
238 };
239 };
240 };
241
242 testScript = # python
243 ''
244 ejabberdctl = "su ejabberd -s $(which ejabberdctl) "
245
246 server.wait_for_unit("phpfpm-movim.service")
247 server.wait_for_unit("h2o.service")
248 server.wait_for_open_port(${builtins.toString movim.port})
249 server.wait_for_open_port(80)
250
251 server.wait_for_unit("ejabberd.service")
252 ejabberd_status = server.succeed(ejabberdctl + "status")
253 assert "status: started" in ejabberd_status
254 server.succeed(ejabberdctl + "register ${ejabberd.admin.username} ${ejabberd.domain} ${ejabberd.admin.password}")
255
256 server.wait_for_unit("movim.service")
257
258 # Test unauthenticated
259 server.fail("curl -L --fail-with-body --max-redirs 0 http://${movim.domain}/chat")
260
261 # Test basic Websocket
262 server.succeed("echo | websocat --origin 'http://${movim.domain}' 'ws://${movim.domain}/ws/?path=login&offset=0'")
263
264 # Test login + create cookiejar
265 login_html = server.succeed("curl --fail-with-body -c /tmp/cookies http://${movim.domain}/login")
266 assert "${movim.description}" in login_html
267 assert "${movim.info}" in login_html
268
269 # Test authentication POST
270 server.succeed("curl --fail-with-body -b /tmp/cookies -X POST --data-urlencode 'username=${ejabberd.admin.JID}' --data-urlencode 'password=${ejabberd.admin.password}' http://${movim.domain}/login")
271
272 server.succeed("curl -L --fail-with-body --max-redirs 1 -b /tmp/cookies http://${movim.domain}/chat")
273 '';
274}