Skip to main content

This is a new website theme. Help me improve it and give your feedback (opens in a new tab).

Matrix Synapse Certificates

Published:

Tags:

Sysadmin Openbsd Acme Matrix Synapse
This blog post is more than two years old. It is preserved here in the hope that it is useful to someone, but please be aware that links may be broken and that opinions expressed here may not reflect my current views. If this is a technical article, it may no longer reflect current best practice.

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.