Self-host your own digital island
1{ config, pkgs, lib, ... }:
2
3let cfg = config.eilean; in
4{
5 options.eilean.matrix = {
6 enable = lib.mkEnableOption "matrix";
7 turn = lib.mkOption {
8 type = lib.types.bool;
9 default = true;
10 };
11 };
12
13 config = lib.mkIf cfg.matrix.enable {
14 services.postgresql.enable = true;
15 services.postgresql.package = pkgs.postgresql_13;
16 services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
17 CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
18 CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
19 TEMPLATE template0
20 LC_COLLATE = "C"
21 LC_CTYPE = "C";
22 '';
23
24 services.nginx = {
25 enable = true;
26 # only recommendedProxySettings and recommendedGzipSettings are strictly required,
27 # but the rest make sense as well
28 recommendedTlsSettings = true;
29 recommendedOptimisation = true;
30 recommendedGzipSettings = true;
31 recommendedProxySettings = true;
32
33 virtualHosts = {
34 # This host section can be placed on a different host than the rest,
35 # i.e. to delegate from the host being accessible as ${config.networking.domain}
36 # to another host actually running the Matrix homeserver.
37 "${config.networking.domain}" = {
38 enableACME = true;
39 forceSSL = true;
40
41 locations."= /.well-known/matrix/server".extraConfig =
42 let
43 # use 443 instead of the default 8448 port to unite
44 # the client-server and server-server port for simplicity
45 server = { "m.server" = "matrix.${config.networking.domain}:443"; };
46 in ''
47 default_type application/json;
48 return 200 '${builtins.toJSON server}';
49 '';
50 locations."= /.well-known/matrix/client".extraConfig =
51 let
52 client = {
53 "m.homeserver" = { "base_url" = "https://matrix.${config.networking.domain}"; };
54 "m.identity_server" = { "base_url" = "https://vector.im"; };
55 };
56 # ACAO required to allow element-web on any URL to request this json file
57 # set other headers due to https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md
58 in ''
59 default_type application/json;
60 add_header Access-Control-Allow-Origin *;
61 add_header Strict-Transport-Security max-age=31536000 always;
62 add_header X-Frame-Options SAMEORIGIN always;
63 add_header X-Content-Type-Options nosniff always;
64 add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always;
65 add_header Referrer-Policy 'same-origin';
66 return 200 '${builtins.toJSON client}';
67 '';
68 };
69
70 # Reverse proxy for Matrix client-server and server-server communication
71 "matrix.${config.networking.domain}" = {
72 enableACME = true;
73 forceSSL = true;
74
75 # Or do a redirect instead of the 404, or whatever is appropriate for you.
76 # But do not put a Matrix Web client here! See the Element web section below.
77 locations."/".extraConfig = ''
78 return 404;
79 '';
80
81 # forward all Matrix API calls to the synapse Matrix homeserver
82 locations."/_matrix" = {
83 proxyPass = "http://127.0.0.1:8008"; # without a trailing /
84 #proxyPassReverse = "http://127.0.0.1:8008"; # without a trailing /
85 };
86 };
87 };
88 };
89
90 services.matrix-synapse = {
91 enable = true;
92 settings = lib.mkMerge [
93 {
94 server_name = config.networking.domain;
95 enable_registration = true;
96 registration_requires_token = true;
97 registration_shared_secret_path = "${config.eilean.secretsDir}/matrix-shared-secret";
98 listeners = [
99 {
100 port = 8008;
101 bind_addresses = [ "::1" "127.0.0.1" ];
102 type = "http";
103 tls = false;
104 x_forwarded = true;
105 resources = [
106 {
107 names = [ "client" "federation" ];
108 compress = false;
109 }
110 ];
111 }
112 ];
113 max_upload_size = "100M";
114 }
115 (lib.mkIf cfg.matrix.turn {
116 turn_uris = with config.services.coturn; [
117 "turn:${realm}:3478?transport=udp"
118 "turn:${realm}:3478?transport=tcp"
119 "turns:${realm}:5349?transport=udp"
120 "turns:${realm}:5349?transport=tcp"
121 ];
122 turn_user_lifetime = "1h";
123 })
124 ];
125 extraConfigFiles = [ "${config.eilean.secretsDir}/matrix-turn-shared-secret" ];
126 };
127
128 eilean.turn.enable = lib.mkIf cfg.matrix.turn true;
129
130 eilean.dns.enable = true;
131 eilean.services.dns.zones.${config.networking.domain}.records = [
132 {
133 name = "matrix";
134 type = "CNAME";
135 data = "vps";
136 }
137 ];
138 };
139}