1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.matrix-synapse;
7 logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig;
8 configFile = pkgs.writeText "homeserver.yaml" ''
9tls_certificate_path: "${cfg.tls_certificate_path}"
10tls_private_key_path: "${cfg.tls_private_key_path}"
11tls_dh_params_path: "${cfg.tls_dh_params_path}"
12no_tls: ${if cfg.no_tls then "true" else "false"}
13bind_port: ${toString cfg.bind_port}
14unsecure_port: ${toString cfg.unsecure_port}
15bind_host: "${cfg.bind_host}"
16server_name: "${cfg.server_name}"
17pid_file: "/var/run/matrix-synapse.pid"
18web_client: ${if cfg.web_client then "true" else "false"}
19database: {
20 name: "${cfg.database_type}",
21 args: {
22 ${concatStringsSep ",\n " (
23 mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args
24 )}
25 }
26}
27log_file: "/var/log/matrix-synapse/homeserver.log"
28log_config: "${logConfigFile}"
29media_store_path: "/var/lib/matrix-synapse/media"
30recaptcha_private_key: "${cfg.recaptcha_private_key}"
31recaptcha_public_key: "${cfg.recaptcha_public_key}"
32enable_registration_captcha: ${if cfg.enable_registration_captcha then "true" else "false"}
33turn_uris: ${if (length cfg.turn_uris) == 0 then "[]" else ("\n" + (concatStringsSep "\n" (map (s: "- " + s) cfg.turn_uris)))}
34turn_shared_secret: "${cfg.turn_shared_secret}"
35enable_registration: ${if cfg.enable_registration then "true" else "false"}
36${optionalString (cfg.registration_shared_secret != "") ''
37registration_shared_secret: "${cfg.registration_shared_secret}"
38''}
39enable_metrics: ${if cfg.enable_metrics then "true" else "false"}
40report_stats: ${if cfg.report_stats then "true" else "false"}
41signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key"
42perspectives:
43 servers: {
44 ${concatStringsSep "},\n" (mapAttrsToList (n: v: ''
45 "${n}": {
46 "verify_keys": {
47 ${concatStringsSep "},\n" (mapAttrsToList (n: v: ''
48 "${n}": {
49 "key": "${v}"
50 }'') v)}
51 }
52 '') cfg.servers)}
53 }
54 }
55${cfg.extraConfig}
56'';
57in {
58 options = {
59 services.matrix-synapse = {
60 enable = mkEnableOption "matrix.org synapse";
61 package = mkOption {
62 type = types.package;
63 default = pkgs.matrix-synapse;
64 defaultText = "pkgs.matrix-synapse";
65 description = ''
66 Overridable attribute of the matrix synapse server package to use.
67 '';
68 };
69 no_tls = mkOption {
70 type = types.bool;
71 default = false;
72 description = ''
73 Don't bind to the https port
74 '';
75 };
76 tls_certificate_path = mkOption {
77 type = types.path;
78 default = "/var/lib/matrix-synapse/homeserver.tls.crt";
79 description = ''
80 PEM encoded X509 certificate for TLS
81 '';
82 };
83 tls_private_key_path = mkOption {
84 type = types.path;
85 default = "/var/lib/matrix-synapse/homeserver.tls.key";
86 description = ''
87 PEM encoded private key for TLS
88 '';
89 };
90 tls_dh_params_path = mkOption {
91 type = types.path;
92 default = "/var/lib/matrix-synapse/homeserver.tls.dh";
93 description = ''
94 PEM dh parameters for ephemeral keys
95 '';
96 };
97 bind_port = mkOption {
98 type = types.int;
99 default = 8448;
100 description = ''
101 The port to listen for HTTPS requests on.
102 For when matrix traffic is sent directly to synapse.
103 '';
104 };
105 unsecure_port = mkOption {
106 type = types.int;
107 default = 8008;
108 description = ''
109 The port to listen for HTTP requests on.
110 For when matrix traffic passes through loadbalancer that unwraps TLS.
111 '';
112 };
113 bind_host = mkOption {
114 type = types.str;
115 default = "";
116 description = ''
117 Local interface to listen on.
118 The empty string will cause synapse to listen on all interfaces.
119 '';
120 };
121 server_name = mkOption {
122 type = types.str;
123 description = ''
124 The domain name of the server, with optional explicit port.
125 This is used by remote servers to connect to this server,
126 e.g. matrix.org, localhost:8080, etc.
127 This is also the last part of your UserID.
128 '';
129 };
130 web_client = mkOption {
131 type = types.bool;
132 default = false;
133 description = ''
134 Whether to serve a web client from the HTTP/HTTPS root resource.
135 '';
136 };
137 database_type = mkOption {
138 type = types.enum [ "sqlite3" "psycopg2" ];
139 default = "sqlite3";
140 description = ''
141 The database engine name. Can be sqlite or psycopg2.
142 '';
143 };
144 database_args = mkOption {
145 type = types.attrs;
146 default = {
147 database = "/var/lib/matrix-synapse/homeserver.db";
148 };
149 description = ''
150 Arguments to pass to the engine.
151 '';
152 };
153 recaptcha_private_key = mkOption {
154 type = types.str;
155 default = "";
156 description = ''
157 This Home Server's ReCAPTCHA private key.
158 '';
159 };
160 recaptcha_public_key = mkOption {
161 type = types.str;
162 default = "";
163 description = ''
164 This Home Server's ReCAPTCHA public key.
165 '';
166 };
167 enable_registration_captcha = mkOption {
168 type = types.bool;
169 default = false;
170 description = ''
171 Enables ReCaptcha checks when registering, preventing signup
172 unless a captcha is answered. Requires a valid ReCaptcha
173 public/private key.
174 '';
175 };
176 turn_uris = mkOption {
177 type = types.listOf types.str;
178 default = [];
179 description = ''
180 The public URIs of the TURN server to give to clients
181 '';
182 };
183 turn_shared_secret = mkOption {
184 type = types.str;
185 default = "";
186 description = ''
187 The shared secret used to compute passwords for the TURN server
188 '';
189 };
190 enable_registration = mkOption {
191 type = types.bool;
192 default = false;
193 description = ''
194 Enable registration for new users.
195 '';
196 };
197 registration_shared_secret = mkOption {
198 type = types.str;
199 default = "";
200 description = ''
201 If set, allows registration by anyone who also has the shared
202 secret, even if registration is otherwise disabled.
203 '';
204 };
205 enable_metrics = mkOption {
206 type = types.bool;
207 default = false;
208 description = ''
209 Enable collection and rendering of performance metrics
210 '';
211 };
212 report_stats = mkOption {
213 type = types.bool;
214 default = false;
215 description = ''
216 '';
217 };
218 servers = mkOption {
219 type = types.attrs;
220 default = {
221 "matrix.org" = {
222 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
223 };
224 };
225 description = ''
226 The trusted servers to download signing keys from.
227 '';
228 };
229 extraConfig = mkOption {
230 type = types.lines;
231 default = "";
232 description = ''
233 Extra config options for matrix-synapse.
234 '';
235 };
236 logConfig = mkOption {
237 type = types.lines;
238 default = readFile ./matrix-synapse-log_config.yaml;
239 description = ''
240 A yaml python logging config file
241 '';
242 };
243 };
244 };
245
246 config = mkIf cfg.enable {
247 users.extraUsers = [
248 { name = "matrix-synapse";
249 group = "matrix-synapse";
250 home = "/var/lib/matrix-synapse";
251 createHome = true;
252 shell = "${pkgs.bash}/bin/bash";
253 uid = config.ids.uids.matrix-synapse;
254 } ];
255
256 users.extraGroups = [
257 { name = "matrix-synapse";
258 gid = config.ids.gids.matrix-synapse;
259 } ];
260
261 systemd.services.matrix-synapse = {
262 after = [ "network.target" ];
263 wantedBy = [ "multi-user.target" ];
264 preStart = ''
265 mkdir -p /var/lib/matrix-synapse
266 chmod 700 /var/lib/matrix-synapse
267 chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
268 ${cfg.package}/bin/homeserver --config-path ${configFile} --generate-keys
269 '';
270 serviceConfig = {
271 Type = "simple";
272 User = "matrix-synapse";
273 Group = "matrix-synapse";
274 WorkingDirectory = "/var/lib/matrix-synapse";
275 PermissionsStartOnly = true;
276 ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile}";
277 };
278 };
279 };
280}