summary refs log tree commit diff
path: root/nixos/modules/services/matrix/matrix-synapse.xml
blob: cf33957d58ece7b75aacafbb9de059744423498c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<chapter xmlns="http://docbook.org/ns/docbook"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xi="http://www.w3.org/2001/XInclude"
         version="5.0"
         xml:id="module-services-matrix">
 <title>Matrix</title>
 <para>
  <link xlink:href="https://matrix.org/">Matrix</link> is an open standard for
  interoperable, decentralised, real-time communication over IP. It can be used
  to power Instant Messaging, VoIP/WebRTC signalling, Internet of Things
  communication - or anywhere you need a standard HTTP API for publishing and
  subscribing to data whilst tracking the conversation history.
 </para>
 <para>
  This chapter will show you how to set up your own, self-hosted Matrix
  homeserver using the Synapse reference homeserver, and how to serve your own
  copy of the Element web client. See the
  <link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try
  Matrix Now!</link> overview page for links to Element Apps for Android and iOS,
  desktop clients, as well as bridges to other networks and other projects
  around Matrix.
 </para>
 <section xml:id="module-services-matrix-synapse">
  <title>Synapse Homeserver</title>

  <para>
   <link xlink:href="https://github.com/matrix-org/synapse">Synapse</link> is
   the reference homeserver implementation of Matrix from the core development
   team at matrix.org. The following configuration example will set up a
   synapse server for the <literal>example.org</literal> domain, served from
   the host <literal>myhostname.example.org</literal>. For more information,
   please refer to the
   <link xlink:href="https://github.com/matrix-org/synapse#synapse-installation">
   installation instructions of Synapse </link>.
<programlisting>
{ pkgs, lib, ... }:
let
  fqdn =
    let
      join = hostName: domain: hostName + lib.optionalString (domain != null) ".${domain}";
    in join config.networking.hostName config.networking.domain;
in {
  networking = {
    <link linkend="opt-networking.hostName">hostName</link> = "myhostname";
    <link linkend="opt-networking.domain">domain</link> = "example.org";
  };
  <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];

  <link linkend="opt-services.postgresql.enable">services.postgresql.enable</link> = true;
  <link linkend="opt-services.postgresql.initialScript">services.postgresql.initialScript</link> = pkgs.writeText "synapse-init.sql" ''
    CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
    CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
      TEMPLATE template0
      LC_COLLATE = "C"
      LC_CTYPE = "C";
  '';

  services.nginx = {
    <link linkend="opt-services.nginx.enable">enable</link> = true;
    # only recommendedProxySettings and recommendedGzipSettings are strictly required,
    # but the rest make sense as well
    <link linkend="opt-services.nginx.recommendedTlsSettings">recommendedTlsSettings</link> = true;
    <link linkend="opt-services.nginx.recommendedOptimisation">recommendedOptimisation</link> = true;
    <link linkend="opt-services.nginx.recommendedGzipSettings">recommendedGzipSettings</link> = true;
    <link linkend="opt-services.nginx.recommendedProxySettings">recommendedProxySettings</link> = true;

    <link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
      # This host section can be placed on a different host than the rest,
      # i.e. to delegate from the host being accessible as ${config.networking.domain}
      # to another host actually running the Matrix homeserver.
      "${config.networking.domain}" = {
        <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
        <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;

        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/server".extraConfig</link> =
          let
            # use 443 instead of the default 8448 port to unite
            # the client-server and server-server port for simplicity
            server = { "m.server" = "${fqdn}:443"; };
          in ''
            add_header Content-Type application/json;
            return 200 '${builtins.toJSON server}';
          '';
        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/client".extraConfig</link> =
          let
            client = {
              "m.homeserver" =  { "base_url" = "https://${fqdn}"; };
              "m.identity_server" =  { "base_url" = "https://vector.im"; };
            };
          # ACAO required to allow element-web on any URL to request this json file
          in ''
            add_header Content-Type application/json;
            add_header Access-Control-Allow-Origin *;
            return 200 '${builtins.toJSON client}';
          '';
      };

      # Reverse proxy for Matrix client-server and server-server communication
      ${fqdn} = {
        <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
        <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;

        # Or do a redirect instead of the 404, or whatever is appropriate for you.
        # But do not put a Matrix Web client here! See the Element web section below.
        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."/".extraConfig</link> = ''
          return 404;
        '';

        # forward all Matrix API calls to the synapse Matrix homeserver
        locations."/_matrix" = {
          <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">proxyPass</link> = "http://[::1]:8008"; # without a trailing /
        };
      };
    };
  };
  services.matrix-synapse = {
    <link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
    <link linkend="opt-services.matrix-synapse.settings.server_name">server_name</link> = config.networking.domain;
    <link linkend="opt-services.matrix-synapse.settings.listeners">listeners</link> = [
      {
        <link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
        <link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_addresses</link> = [ "::1" ];
        <link linkend="opt-services.matrix-synapse.settings.listeners._.type">type</link> = "http";
        <link linkend="opt-services.matrix-synapse.settings.listeners._.tls">tls</link> = false;
        <link linkend="opt-services.matrix-synapse.settings.listeners._.x_forwarded">x_forwarded</link> = true;
        <link linkend="opt-services.matrix-synapse.settings.listeners._.resources">resources</link> = [ {
          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "client" ];
          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = true;
        } {
          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "federation" ];
          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = false;
        } ];
      }
    ];
  };
}
</programlisting>
  </para>

  <para>
   If the <code>A</code> and <code>AAAA</code> DNS records on
   <literal>example.org</literal> do not point on the same host as the records
   for <code>myhostname.example.org</code>, you can easily move the
   <code>/.well-known</code> virtualHost section of the code to the host that
   is serving <literal>example.org</literal>, while the rest stays on
   <literal>myhostname.example.org</literal> with no other changes required.
   This pattern also allows to seamlessly move the homeserver from
   <literal>myhostname.example.org</literal> to
   <literal>myotherhost.example.org</literal> by only changing the
   <code>/.well-known</code> redirection target.
  </para>

  <para>
   If you want to run a server with public registration by anybody, you can
   then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.settings.enable_registration</link> =
   true;</literal>. Otherwise, or you can generate a registration secret with
   <command>pwgen -s 64 1</command> and set it with
   <option><link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">services.matrix-synapse.settings.registration_shared_secret</link></option>.
   To create a new user or admin, run the following after you have set the secret
   and have rebuilt NixOS:
<screen>
<prompt>$ </prompt>nix run nixpkgs.matrix-synapse
<prompt>$ </prompt>register_new_matrix_user -k <replaceable>your-registration-shared-secret</replaceable> http://localhost:8008
<prompt>New user localpart: </prompt><replaceable>your-username</replaceable>
<prompt>Password:</prompt>
<prompt>Confirm password:</prompt>
<prompt>Make admin [no]:</prompt>
Success!
</screen>
   In the example, this would create a user with the Matrix Identifier
   <literal>@your-username:example.org</literal>. Note that the registration
   secret ends up in the nix store and therefore is world-readable by any user
   on your machine, so it makes sense to only temporarily activate the
   <link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">registration_shared_secret</link>
   option until a better solution for NixOS is in place.
  </para>
 </section>
 <section xml:id="module-services-matrix-element-web">
  <title>Element (formerly known as Riot) Web Client</title>

  <para>
   <link xlink:href="https://github.com/vector-im/riot-web/">Element Web</link> is
   the reference web client for Matrix and developed by the core team at
   matrix.org. Element was formerly known as Riot.im, see the
   <link xlink:href="https://element.io/blog/welcome-to-element/">Element introductory blog post</link>
   for more information. The following snippet can be optionally added to the code before
   to complete the synapse installation with a web client served at
   <code>https://element.myhostname.example.org</code> and
   <code>https://element.example.org</code>. Alternatively, you can use the hosted
   copy at <link xlink:href="https://app.element.io/">https://app.element.io/</link>,
   or use other web clients or native client applications. Due to the
   <literal>/.well-known</literal> urls set up done above, many clients should
   fill in the required connection details automatically when you enter your
   Matrix Identifier. See
   <link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try
   Matrix Now!</link> for a list of existing clients and their supported
   featureset.
<programlisting>
{
  services.nginx.virtualHosts."element.${fqdn}" = {
    <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
    <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
    <link linkend="opt-services.nginx.virtualHosts._name_.serverAliases">serverAliases</link> = [
      "element.${config.networking.domain}"
    ];

    <link linkend="opt-services.nginx.virtualHosts._name_.root">root</link> = pkgs.element-web.override {
      conf = {
        default_server_config."m.homeserver" = {
          "base_url" = "https://${fqdn}";
          "server_name" = "${fqdn}";
        };
      };
    };
  };
}
</programlisting>
  </para>

  <para>
   Note that the Element developers do not recommend running Element and your Matrix
   homeserver on the same fully-qualified domain name for security reasons. In
   the example, this means that you should not reuse the
   <literal>myhostname.example.org</literal> virtualHost to also serve Element,
   but instead serve it on a different subdomain, like
   <literal>element.example.org</literal> in the example. See the
   <link xlink:href="https://github.com/vector-im/riot-web#important-security-note">Element
   Important Security Notes</link> for more information on this subject.
  </para>
 </section>
</chapter>