Matrix Synapse Certificates
Published:
I’ve had a need to set up and run a Matrix homeserver and I wanted to try to set it up for general use as well as just for this project.
The one thing I wanted to do that is a little non-standard is to use the “plain” domain for my user ID while hosting the homeserver on a subdomain. In this case I wanted to use “irl.xyz” as the user ID domain, and “syn.irl.xyz” to host the Synapse homeserver.
If you’ve run an XMPP server before then you’ll know about this sort of arrangement allowing you to seperate identity from the actual infrastructure providing the services. In XMPP this is achieved with DNS SRV records, for example:
_xmpp-client._tcp.example.net. SRV 5 0 5222 xmpp.example.net.
_xmpp-server._tcp.example.net. SRV 5 0 5269 xmpp.example.net.
There are two records here, one for clients and one for servers. Both of these endpoints should use a TLS certificate with the plain domain “example.net” in the name as this indirection doesn’t change the identity of the service you’re connecting to.
Matrix is considerably more complex unfortunately. There are still two types of discovery required: one for clients and one for servers. The implementation of these however are different. Let’s start with server to server (federation) discovery.
The specification outlines the 5 step process to be used to discover the server that should be connected to. Matrix prefers to use a well known file to be fetched via HTTPS to discover the server to connect to.
In my case, that means connecting to https://irl.xyz/.well-known/matrix/server
and expecting to receive a JSON object that looks something like:
{"m.server": "syn.irl.xyz"}
If this succeeds then it will connect to the Synapse homeserver and expect that
the TLS certificate will have the name syn.irl.xyz
. I’ve not really got a
web server set up there yet though which means that this will fail and move on
to the next step.
The next step looks up a DNS SRV record, similar to that used by XMPP, that looks like:
_matrix._tcp.irl.xyz SRV 0 0 8448 syn.irl.xyz.
This allows other Matrix homeservers to now discover my homeserver and
federate, but the important thing to watch out for here is that now the name
expected on the certificate is the plain domain name, irl.xyz
.
For clients, there is no DNS SRV option available, only the use of a JSON file
at a well known URL. Clients always expect the name on the domain to be the
name of the host running Synapse. As I’m the only user on this server I’m quite
happy for now to just type syn.irl.xyz
into clients that I use rather than
automatically discovering the server.
This means there’s a need for both the plain domain name and the server domain name to be present in certificates for federation and for client use. I suspect that the use of the server name is to better support operation from a browser. A browser can lookup the well known URL to get the JSON file, and then create a new HTTPS request to the homeserver. Without the server’s name on the certificate that would fail in the browser. For federation it’s possible to use the plain domain (and so have some better authenticity checks when you consider that the user IDs have the plain domain) because it’s the homeserver that is checking the certificates.
With XMPP, you just have the plain domain because both servers and clients are all XMPP aware and you’re not having to compromise on security checks based on what a browser can do for you.
So now I need to get two certificates into the Matrix homeserver: irl.xyz
and
syn.irl.xyz
. I’m using OpenBSD which has
acme-client(1) available, but this only
implements the http-01
challenge. This means I won’t be able to use acme for
the plain domain.
To set up acme-client for the server hostname, I used the hambsd-acme role to configure the client and set up periodic renewals. For the plain domain name, I paid for a certificate to be issued which gives it a longer expiry meaning I’m not having to constantly update it manually.
In the relayd.conf
, I added:
tls keypair irl.xyz
tls keypair syn.irl.xyz
This means that when a request comes in, the correct keypair will be chosen based on the SNI TLS option.
In the future, I’ll be aiming to switch from DNS SRV discovery to the well-known URL which will then allow me to use only the certificate for the server hostname and drop the certificate for the plain domain name.