1import ../make-test-python.nix ({ pkgs, ... } : let
2
3
4 runWithOpenSSL = file: cmd: pkgs.runCommand file {
5 buildInputs = [ pkgs.openssl ];
6 } cmd;
7
8
9 ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
10 ca_pem = runWithOpenSSL "ca.pem" ''
11 openssl req \
12 -x509 -new -nodes -key ${ca_key} \
13 -days 10000 -out $out -subj "/CN=snakeoil-ca"
14 '';
15 key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048";
16 csr = runWithOpenSSL "matrix.csr" ''
17 openssl req \
18 -new -key ${key} \
19 -out $out -subj "/CN=localhost" \
20 '';
21 cert = runWithOpenSSL "matrix_cert.pem" ''
22 openssl x509 \
23 -req -in ${csr} \
24 -CA ${ca_pem} -CAkey ${ca_key} \
25 -CAcreateserial -out $out \
26 -days 365
27 '';
28
29
30 mailerCerts = import ../common/acme/server/snakeoil-certs.nix;
31 mailerDomain = mailerCerts.domain;
32 registrationSharedSecret = "unsecure123";
33 testUser = "alice";
34 testPassword = "alicealice";
35 testEmail = "alice@example.com";
36
37 listeners = [ {
38 port = 8448;
39 bind_addresses = [
40 "127.0.0.1"
41 "::1"
42 ];
43 type = "http";
44 tls = true;
45 x_forwarded = false;
46 resources = [ {
47 names = [
48 "client"
49 ];
50 compress = true;
51 } {
52 names = [
53 "federation"
54 ];
55 compress = false;
56 } ];
57 } ];
58
59in {
60
61 name = "matrix-synapse";
62 meta = with pkgs.lib; {
63 maintainers = teams.matrix.members;
64 };
65
66 nodes = {
67 # Since 0.33.0, matrix-synapse doesn't allow underscores in server names
68 serverpostgres = { pkgs, nodes, config, ... }: let
69 mailserverIP = nodes.mailserver.config.networking.primaryIPAddress;
70 in
71 {
72 services.matrix-synapse = {
73 enable = true;
74 settings = {
75 inherit listeners;
76 database = {
77 name = "psycopg2";
78 args.password = "synapse";
79 };
80 redis = {
81 enabled = true;
82 host = "localhost";
83 port = config.services.redis.servers.matrix-synapse.port;
84 };
85 tls_certificate_path = "${cert}";
86 tls_private_key_path = "${key}";
87 registration_shared_secret = registrationSharedSecret;
88 public_baseurl = "https://example.com";
89 email = {
90 smtp_host = mailerDomain;
91 smtp_port = 25;
92 require_transport_security = true;
93 notif_from = "matrix <matrix@${mailerDomain}>";
94 app_name = "Matrix";
95 };
96 };
97 };
98 services.postgresql = {
99 enable = true;
100
101 # The database name and user are configured by the following options:
102 # - services.matrix-synapse.database_name
103 # - services.matrix-synapse.database_user
104 #
105 # The values used here represent the default values of the module.
106 initialScript = pkgs.writeText "synapse-init.sql" ''
107 CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
108 CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
109 TEMPLATE template0
110 LC_COLLATE = "C"
111 LC_CTYPE = "C";
112 '';
113 };
114
115 services.redis.servers.matrix-synapse = {
116 enable = true;
117 port = 6380;
118 };
119
120 networking.extraHosts = ''
121 ${mailserverIP} ${mailerDomain}
122 '';
123
124 security.pki.certificateFiles = [
125 mailerCerts.ca.cert ca_pem
126 ];
127
128 environment.systemPackages = let
129 sendTestMailStarttls = pkgs.writeScriptBin "send-testmail-starttls" ''
130 #!${pkgs.python3.interpreter}
131 import smtplib
132 import ssl
133
134 ctx = ssl.create_default_context()
135
136 with smtplib.SMTP('${mailerDomain}') as smtp:
137 smtp.ehlo()
138 smtp.starttls(context=ctx)
139 smtp.ehlo()
140 smtp.sendmail('matrix@${mailerDomain}', '${testEmail}', 'Subject: Test STARTTLS\n\nTest data.')
141 smtp.quit()
142 '';
143
144 obtainTokenAndRegisterEmail = let
145 # adding the email through the API is quite complicated as it involves more than one step and some
146 # client-side calculation
147 insertEmailForAlice = pkgs.writeText "alice-email.sql" ''
148 INSERT INTO user_threepids (user_id, medium, address, validated_at, added_at) VALUES ('${testUser}@serverpostgres', 'email', '${testEmail}', '1629149927271', '1629149927270');
149 '';
150 in
151 pkgs.writeScriptBin "obtain-token-and-register-email" ''
152 #!${pkgs.runtimeShell}
153 set -o errexit
154 set -o pipefail
155 set -o nounset
156 su postgres -c "psql -d matrix-synapse -f ${insertEmailForAlice}"
157 curl --fail -XPOST 'https://localhost:8448/_matrix/client/r0/account/password/email/requestToken' -d '{"email":"${testEmail}","client_secret":"foobar","send_attempt":1}' -v
158 '';
159 in [ sendTestMailStarttls pkgs.matrix-synapse obtainTokenAndRegisterEmail ];
160 };
161
162 # test mail delivery
163 mailserver = args: let
164 in
165 {
166 security.pki.certificateFiles = [
167 mailerCerts.ca.cert
168 ];
169
170 networking.firewall.enable = false;
171
172 services.postfix = {
173 enable = true;
174 hostname = "${mailerDomain}";
175 # open relay for subnet
176 networksStyle = "subnet";
177 enableSubmission = true;
178 tlsTrustedAuthorities = "${mailerCerts.ca.cert}";
179 sslCert = "${mailerCerts.${mailerDomain}.cert}";
180 sslKey = "${mailerCerts.${mailerDomain}.key}";
181
182 # blackhole transport
183 transport = "example.com discard:silently";
184
185 config = {
186 debug_peer_level = "10";
187 smtpd_relay_restrictions = [
188 "permit_mynetworks" "reject_unauth_destination"
189 ];
190
191 # disable obsolete protocols, something old versions of twisted are still using
192 smtpd_tls_protocols = "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
193 smtp_tls_protocols = "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
194 smtpd_tls_mandatory_protocols = "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
195 smtp_tls_mandatory_protocols = "TLSv1.3, TLSv1.2, !TLSv1.1, !TLSv1, !SSLv2, !SSLv3";
196 };
197 };
198 };
199
200 serversqlite = args: {
201 services.matrix-synapse = {
202 enable = true;
203 settings = {
204 inherit listeners;
205 database.name = "sqlite3";
206 tls_certificate_path = "${cert}";
207 tls_private_key_path = "${key}";
208 };
209 };
210 };
211 };
212
213 testScript = ''
214 start_all()
215 mailserver.wait_for_unit("postfix.service")
216 serverpostgres.succeed("send-testmail-starttls")
217 serverpostgres.wait_for_unit("matrix-synapse.service")
218 serverpostgres.wait_until_succeeds(
219 "curl --fail -L --cacert ${ca_pem} https://localhost:8448/"
220 )
221 serverpostgres.wait_until_succeeds(
222 "journalctl -u matrix-synapse.service | grep -q 'Connected to redis'"
223 )
224 serverpostgres.require_unit_state("postgresql.service")
225 serverpostgres.succeed("register_new_matrix_user -u ${testUser} -p ${testPassword} -a -k ${registrationSharedSecret} https://localhost:8448/")
226 serverpostgres.succeed("obtain-token-and-register-email")
227 serversqlite.wait_for_unit("matrix-synapse.service")
228 serversqlite.wait_until_succeeds(
229 "curl --fail -L --cacert ${ca_pem} https://localhost:8448/"
230 )
231 serversqlite.succeed("[ -e /var/lib/matrix-synapse/homeserver.db ]")
232 '';
233
234})