This can be useful for admins running local synapse servers.
in order to prevent users from creating communities, there is an option: put in your /matrix/synapse/config/homeserver.yaml
file:
enable_group_creation: false
in order to prevent users from creating rooms, it is a bit more complex: you have to use Synapse Third party event rules mechanism. They allow Synapse admins to specify an additional set of rules on when to allow or deny an event.
There is few documentation. I found only:
- in the sample of
homeserver.yaml
, see this section https://github.com/matrix-org/synapse/blob/master/docs/sample_config.yaml#L2259 - and also the file from which you need to override the methods: https://github.com/matrix-org/synapse/blob/master/synapse/events/third_party_rules.py
Below is what I use and is (almost) working:
- the logs are a bit hard to configure and are working (the code will run in a deffered twisted context),
- the room creation filter is working, but
- the prohibition of encryption (this is a legal obligation for us) works with some clients and not with others
Extract from /matrix/synapse/config/homeserver.yaml
:
third_party_event_rules:
config:
creators:
- '@admin:domain.com'
- '@other:domain.com'
module: domain_event_rules.DomainRulesModule
Extract from /etc/systemd/system/matrix-synapse.service
(it is a Docker-based server installed by ansible):
ExecStart=/usr/bin/docker run --rm --name matrix-synapse \
[...]
--mount type=bind,\
src=/matrix/synapse/ext/domain_event_rules.py,\
dst=/usr/local/lib/python3.7/site-packages/domain_event_rules.py,\
ro \
[...]
Extract from /matrix/synapse/ext/domain_event_rules.py
:
import logging
import sys
from twisted.internet import defer
from twisted.logger import Logger, eventAsText, FileLogObserver
from synapse.api.errors import SynapseError
class DomainRulesModule(object):
log = Logger()
log.observer.addObserver(FileLogObserver(sys.stdout, lambda e: eventAsText(e) + "\n"))
def __init__(self, config, http_client):
self.log.debug('======== Domain Rules Module init ========')
self.creators = config['creators']
self.log.debug('=== debug creators ===')
self.log.debug('* type: ' + type(self.creators).__name__)
self.log.debug('* values: ' + ', '.join("%s" % i for i in self.creators))
self.log.debug('======== end init of Domain Rules Module ========')
def check_event_allowed(self, event, context):
self.log.debug('======== Domain Rules Module check_event_allowed ========')
if event.type == "m.room.encryption":
self.log.debug('=== m.room.encryption event ===')
valueslist = list(context.values())
ev = valueslist[0]
self.log.debug(" * content: {data!r}.", data=ev['content'])
self.log.debug('*** FORBIDDEN ENCRYPTION REQUEST ***')
return False
else:
return True
def on_create_room(self, requester, config, is_requester_admin):
self.log.debug('======== Domain Rules Module on_create_room ========')
self.log.debug('=== full requester ===')
self.log.debug('* type: ' + type(requester).__name__)
self.log.debug('* name: ' + requester.user.to_string())
self.log.debug('=== debug config ===')
self.log.debug('* type: ' + type(config).__name__)
self.log.debug('* keys and values:')
self.log.debug("* Got event keys: {data!r}.", data=config.keys())
self.log.debug("* Got event values: {data!r}.", data=config.values())
if requester.user.to_string() in self.creators:
self.log.debug('=== is rooms created by authorized person ===')
return True
if 'is_direct' in config.keys():
self.log.debug('=== is direct conversation ===')
return True
if is_requester_admin:
self.log.debug('=== is room created by admin ===')
return True
else:
raise SynapseError(403, "You are not permitted to create rooms")
return False
@staticmethod
def parse_config(config):
if config == None:
raise Exception('Missing config for Domain Rules Module')
if 'creators' not in config:
raise Exception('Missing creators parameter for Domain Rules Module')
return config
- it is possible to add structures to the config in
homeserver.yaml
, parse them and use them (here thecreators
list) - with the Logger it is possible to display quite a lot on the arguments received and see what kind of tests can be implemented (here tests on is_requester_admin or is_direct)
What is not working:
The definition of check_event_allowed
actually blocks any attempt to make a room as encrypted.
But, in some cases it also prevents to create direct conversations..
- in (web UI) riot (v1.5.15) => it works because the client is (was) not asking to setup a recovery key for encrypted information.
- in Riot Desktop => when people setup encryption instead of skipping it, then they can no longer create direct conversations.
A creation not only calls on_create_room but then there is a series of events and one is blocked by the rules above:
- on_create_room => OK ("is direct conversation")
- check_event_allowed(m.room.create) => OK ({'room_version': '5', 'creator': '@g...'})
- check_event_allowed(m.room.member) => OK ({'membership': 'join',...)
- check_event_allowed(m.room.power_levels) => OK ({'users': {'@...)
- check_event_allowed(m.room.join_rules) => OK ({'join_rule': 'invite'})
- check_event_allowed(m.room.history_visibility) => OK ({'history_visibility': 'shared'})
- check_event_allowed(m.room.guest_access) => OK ({'guest_access': 'can_join'})
- check_event_allowed(m.room.encryption) => KO ({'algorithm': 'm.megolm.v1.aes-sha2'})
Only with riot-desktop or similar clients proposing to setup encryption does this type of event arrives. If anybody knows how I could differentiate between an encryption request of just a direct conversation creation with no encryption, I'd be very interested.
for those who wish to use this in future: it works fine, but the PM allowing script is broken.