Matrix is a decentralised communications protocol that is essentially the modern equivalent of technologies like IRC and XMPP. You might consider using it as an open-source Slack/Microsoft Teams alternative which supports
The protocol itself has a concept of a “homeserver”, which houses
users and chat rooms. Homeservers then form a federated network to
provide for decentralised communications. No homeserver “owns” any given
chat room, and users from one homeserver are able to connect to rooms on
other homeservers (subject to IRC-style access control by the room
owners). For instance, there is a homeserver running on
matrix.org
, and anyone may make an account there, but the
protocol is really designed for each person or group to run its own
server. User identifiers look like @nickhu:matrix.org
with
the part after the :
signifying the homeserver to which the
user belongs, and room identifiers look like
#myroom:matrix.org
. Room identifiers are not unique, in
that there may be multiple identifiers spanning even distinct
homeservers which refer to the same room (you get the view provided by
the homeserver to which you are connected, and over time federation
tries to ensure consistency). The homeserver software that is used in
almost all cases is called synapse, or sometimes matrix-synapse.
With the current health pandemic, it’s become evident that we will be more reliant on digital communications for the foreseeable future, so I thought it was the perfect time to introduce a synapse instance for our CS department. Installing the server itself is fairly straightforward, and the documentation is fairly sufficient. The only point to really pay attention to is choosing a server name, and setting up delegation because it’s likely that you will want synapse to run on a different machine than the one pointed to by the root domain3. Federation makes this hard to change later on.
Getting people to use the service is the next barrier, and one thing that puts people off is having to make yet another messaging account. One way to sidestep this is to utilise the single sign-on (SSO) services implemented in many universities already. Documentation on this front is a lot more lacklustre however.
This article is my attempt to document this process, to help other departments and universities follow suit.
Many universities provide a single sign-on service, typically Shibboleth or Athens. Our university uses Shibboleth, which is effectively an implementation of a protocol called SAML. If SSO is the idea that you should have one identity and password, then SAML is the technical description of how that would work. Matrix has support for SAML-backed logins via pysaml2.
As of current, this is documented (sparsely) here,
and also in comments in the saml2_config
section of the
homeserver.yaml
configuration. My target machine was the
server running synapse, on Ubuntu focal 20.04. To get this working with
Shibboleth, here was my final configuration:
saml2_config:
sp_config:
metadata:
remote:
- url: http://mdq.ukfederation.org.uk/entities/https:%2F%2Fregistry.shibboleth.ox.ac.uk%2Fidp
description: ["Matrix Synapse Server", "en"]
name: ["Department of Computer Science Matrix Server", "en"]
key_file: /etc/shibboleth/sp-key.pem
cert_file: /etc/shibboleth/sp-cert.pem
encryption_keypairs:
- key_file: /etc/shibboleth/sp-key.pem
cert_file: /etc/shibboleth/sp-cert.pem
attribute_map_dir: /etc/matrix-synapse/saml2-attribute-maps
user_mapping_provider:
module: matrix_saml_strip_hostname.mapping_providers.StripHostnameSamlMappingProvider
config:
mxid_source_attribute: email
#attribute_requirements: # could be used to restrict access to e.g. users from a specific department
# - attribute: userGroup
# value: "staff"
# - attribute: department
# value: "sales"
The sp_config
field is basically configuration
for pysaml2 in YAML form.
The first component of this is the metadata. SAML services have two
main interacting components: an identity provider (IdP) and a service
provider (SP). The IdP is the server run by the university that handles
authentication. The SP in this instance is the synapse server that we
are trying to set up. The metadata.remote
YAML attribute
describes to synapse the information on how the university’s IdP can be
accessed (in XML format). In addition to this, synapse will generate its
own metadata XML which needs to be registered with the IdP. By default,
this is exposed at
https://matrix.example.com/_matrix/saml2/metadata.xml. In my case, this
was handled by a member of IT services, and each university will have
its own procedure for SP registration. Universities in the UK
participate in a SAML federation, the UK Access Management
Federation — this is only important for the person who does the SP
registration, but that is why the metadata remote URL of the IdP is
hosted at mdq.ukfederation.org.uk.
The name and description fields are the strings presented to users when they try to login, and ultimately it’s up to the IdP to present this (it doesn’t appear to be used by my university’s IdP, although it did show during the login flow when I was using the samltest.id IdP).
The certificate-key pair from
/etc/shibboleth/sp-{cert,key}.pem
was generated by
sudo shib-keygen -h matrix.example.com
(which is
effectively an openssl
wrapper included with
shibboleth-sp-utils
package). It’s used to both sign and
encrypt requests and responses; the fields key_file
and
cert_file
at the top level handle the signing, and
everything under encryption_keypairs
handles encryption
(without specifying encryption_keypairs
requests and
responses will be unencrypted).
What I have described so far is sufficient to provide a SSO login
flow, and when a user logs in, synapse will receive a dictionary of
attributes. For my university, the attributes are described at here, and
I think most of them should be fairly uniform across other universities
in the UK Federation. The problem is that synapse expects attributes
uid
, email
, and displayName
to
exist, and these might be named differently in the response from the
IdP. Plumbing from attributes returned by the IdP to what the SP
(synapse) expects is done by an attribute
map.
attribute_map_dir
specifies a directory with a single
file map.py
with contents:
# These attributes come from https://help.it.ox.ac.uk/iam/federation/attributes
# e.g. eduPersonPrincipalName, which is identified by 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6', maps onto uid
# NB: Only the SAML2 attributes work
# Here are the released attributes from Shibboleth:
# - eduPersonPrincipalName
# - mail
# - sn
# - eduPersonOrgUnitDN
# - eduPersonScopedAffiliation
# - givenName
# - displayName
# - eduPersonTargetedID
#
# The attributes synapse expects to see are defined here:
# https://github.com/matrix-org/synapse/blob/develop/synapse/handlers/saml_handler.py
# Currently (11/09/2020), it expects the following attributes:
# - uid
# - email
# - displayName
= {
MAP "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
"fro": {
'urn:oid:1.3.6.1.4.1.5923.1.1.1.6': 'uid',
'urn:oid:0.9.2342.19200300.100.1.3': 'email',
'urn:oid:2.16.840.1.113730.3.1.241': 'displayName',
},"to": {
'uid': 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6',
'email': 'urn:oid:0.9.2342.19200300.100.1.3',
'displayName': 'urn:oid:2.16.840.1.113730.3.1.241',
}, }
The response from the IdP will specify what kind name-format to use.
to
and fro
are python dicts that map
attributes returned from the IdP to uid
,
email
, and displayName
(note that they are
inverses of each other — the pysaml2 documentation says that only one
needs to be provided, but synapse refused to start for me unless I
specified both). uid
should be a unique identifier, which
corresponds to eduPersonPrincipalName
(in my university,
this is of the form blah1234@ox.ac.uk
and uniquely
identifies an individual).
The user_mapping_provider
field specifies how these
attributes are mapped onto matrix IDs (mxid, e.g. @user:matrix.example.com).
By default, it takes as input the uid
attribute, but this
can be changed by setting
user_mapping_provider.config.mxid_source_attribute
. This is
passed to the module in user_mapping_provider.module
; the
default one doesn’t do much, but since I used email
as the
mxid_source_attribute
, I used the module matrix-saml-strip-hostname
so that the mxid is only derived from what precedes the @
in a user’s email address. I installed synapse via the Ubuntu package
repositories, and in order for this module to be picked up by the same
python environment as synapse, the command to install is
sudo /opt/venvs/matrix-synapse/bin/pip install matrix-saml-strip-hostname
(you need the matrix-synapse-py3
package installed).
In the case where two people have the same string before the
@
their email addresses, but perhaps they belong to
different departments so refer to different people,
e.g. john.smith@cs.example.com
and
john.smith@maths.example.com
, the first one to register
will get the handle @john.smith:example.com
, and the second
one will get @john.smith1:example.com
(as described by
failures
in the SSO
mapping provider documentation). This is disambiguated by the fact
that they will have different uid
s. As of current, there is
no way to delete matrix users or migrate them to different
identifiers.
If restrictions on who can sign in (e.g. specific department only)
are required, this can be specified in the
attribute_requirements
field.
To finish up the homeserver configuration, one might also wish to set
enable_registration: false
password_config:
enabled: false
In particular, if passwords aren’t disabled some of the UI elements of Element are kind of buggy (e.g. setting up key backup prompts for a password, which never gets set and can’t be changed).
It can be convenient to install a local instance of the Element
client, so that users can simply be given a URL like
https://chat.example.com and the right homeserver information is
pre-filled out. This part is very simple, and can be done on another
server. Take the latest
release and untar it into the webroot
(e.g. /var/www/html
). Copy config.sample.json
to config.json
and make changes to set the homeserver URL
to the homeserver that was set up previously.
It’s also convenient to set
sso:
client_whitelist:
- https://app.element.io/
- https://chat.example.com/
to prevent an extra page asking for confirmation after SSO login is completed.
I hope that this information is useful for someone, and if anyone needs help with any of the above then I can be contacted via matrix.
Back to archive