mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
matrixsynapse: Add let's encrypt component for certficiates
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
parent
c47a99b25e
commit
9fd1b95244
@ -20,10 +20,6 @@ Configuration helper for Matrix-Synapse server.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import filecmp
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
@ -59,67 +55,6 @@ def parse_arguments():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def _get_certificate_status():
|
||||
"""Return if the current certificate is an up-to-date LE certificate."""
|
||||
configured_domain = get_configured_domain_name()
|
||||
if not configured_domain:
|
||||
return False
|
||||
|
||||
if not os.path.exists(letsencrypt.LIVE_DIRECTORY):
|
||||
return False
|
||||
|
||||
source_dir = os.path.join(letsencrypt.LIVE_DIRECTORY, configured_domain)
|
||||
source_certificate_path = os.path.join(source_dir, 'fullchain.pem')
|
||||
source_private_key_path = os.path.join(source_dir, 'privkey.pem')
|
||||
|
||||
dest_dir = '/etc/matrix-synapse'
|
||||
dest_certificate_path = os.path.join(dest_dir, 'homeserver.tls.crt')
|
||||
dest_private_key_path = os.path.join(dest_dir, 'homeserver.tls.key')
|
||||
|
||||
if filecmp.cmp(source_certificate_path, dest_certificate_path) and \
|
||||
filecmp.cmp(source_private_key_path, dest_private_key_path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _update_tls_certificate():
|
||||
"""Update the TLS certificate and private key used by Matrix Synapse.
|
||||
|
||||
A valid certificate is necessary for federation with other instances
|
||||
starting with version 1.0.
|
||||
|
||||
"""
|
||||
configured_domain = get_configured_domain_name()
|
||||
if os.path.exists(letsencrypt.LIVE_DIRECTORY) and configured_domain:
|
||||
# Copy the latest Let's Encrypt certs into Synapse's directory.
|
||||
src_dir = os.path.join(letsencrypt.LIVE_DIRECTORY, configured_domain)
|
||||
source_certificate_path = os.path.join(src_dir, 'fullchain.pem')
|
||||
source_private_key_path = os.path.join(src_dir, 'privkey.pem')
|
||||
else:
|
||||
# Copy Apache's snake-oil certificate into Synapse's config directory.
|
||||
# The self-signed certificate doesn't really work (other Matrix
|
||||
# Synapse instances do not accept it). It is merely to prevent the
|
||||
# server from failing to startup because the files are missing.
|
||||
source_certificate_path = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
||||
source_private_key_path = '/etc/ssl/private/ssl-cert-snakeoil.key'
|
||||
|
||||
dest_dir = '/etc/matrix-synapse'
|
||||
dest_certificate_path = os.path.join(dest_dir, 'homeserver.tls.crt')
|
||||
dest_private_key_path = os.path.join(dest_dir, 'homeserver.tls.key')
|
||||
|
||||
# Private key is only accessible to the user "matrix-synapse"
|
||||
# Group access is prohibited since it is "nogroup"
|
||||
old_mask = os.umask(0o133)
|
||||
shutil.copyfile(source_certificate_path, dest_certificate_path)
|
||||
os.umask(0o177)
|
||||
shutil.copyfile(source_private_key_path, dest_private_key_path)
|
||||
os.umask(old_mask)
|
||||
|
||||
shutil.chown(dest_certificate_path, user='matrix-synapse', group='nogroup')
|
||||
shutil.chown(dest_private_key_path, user='matrix-synapse', group='nogroup')
|
||||
|
||||
|
||||
def subcommand_post_install(_):
|
||||
"""Perform post installation configuration."""
|
||||
with open(CONFIG_FILE_PATH) as config_file:
|
||||
@ -152,18 +87,12 @@ def subcommand_post_install(_):
|
||||
with open(CONFIG_FILE_PATH, 'w') as config_file:
|
||||
yaml.dump(config, config_file)
|
||||
|
||||
_update_tls_certificate()
|
||||
|
||||
if action_utils.service_is_running('matrix-synapse'):
|
||||
action_utils.service_restart('matrix-synapse')
|
||||
|
||||
|
||||
def subcommand_setup(arguments):
|
||||
"""Configure the domain name for matrix-synapse package."""
|
||||
domain_name = arguments.domain_name
|
||||
action_utils.dpkg_reconfigure('matrix-synapse',
|
||||
{'server-name': domain_name})
|
||||
_update_tls_certificate()
|
||||
|
||||
|
||||
def subcommand_public_registration(argument):
|
||||
@ -190,40 +119,6 @@ def subcommand_public_registration(argument):
|
||||
action_utils.service_restart('matrix-synapse')
|
||||
|
||||
|
||||
def subcommand_letsencrypt(arguments):
|
||||
"""Add/drop usage of Let's Encrypt cert or show status.
|
||||
|
||||
The command 'add' applies only to current domain, will be called by action
|
||||
'letsencrypt run_renew_hooks', when certbot renews the cert (if
|
||||
matrix-synapse is selected for cert use). Drop of a cert must be possible
|
||||
for any domain to respond to domain change.
|
||||
|
||||
"""
|
||||
if arguments.command == 'drop':
|
||||
print('Dropping certificates is not supported for Matrix Synapse.')
|
||||
return
|
||||
|
||||
if arguments.command == 'get-status':
|
||||
print('valid' if _get_certificate_status() else 'invalid')
|
||||
return
|
||||
|
||||
configured_domain = get_configured_domain_name()
|
||||
if arguments.domain is not None and \
|
||||
arguments.domain != configured_domain:
|
||||
print('Aborted: Current domain "{}" is not configured.'.format(
|
||||
arguments.domain))
|
||||
sys.exit(1)
|
||||
|
||||
le_folder = os.path.join(letsencrypt.LIVE_DIRECTORY, configured_domain)
|
||||
if not os.path.exists(le_folder):
|
||||
print('Aborted: No certificate directory at %s.' % le_folder)
|
||||
sys.exit(2)
|
||||
|
||||
_update_tls_certificate()
|
||||
|
||||
action_utils.service_try_restart('matrix-synapse')
|
||||
|
||||
|
||||
def main():
|
||||
arguments = parse_arguments()
|
||||
sub_command = arguments.subcommand.replace('-', '_')
|
||||
|
||||
@ -20,6 +20,7 @@ FreedomBox app to configure matrix-synapse server.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -31,6 +32,7 @@ from plinth import frontpage, menu
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.modules.letsencrypt.components import LetsEncrypt
|
||||
|
||||
from .manifest import backup, clients
|
||||
|
||||
@ -40,6 +42,8 @@ managed_services = ['matrix-synapse']
|
||||
|
||||
managed_packages = ['matrix-synapse', 'matrix-synapse-ldap3']
|
||||
|
||||
managed_paths = [pathlib.Path('/etc/matrix-synapse/')]
|
||||
|
||||
name = _('Matrix Synapse')
|
||||
|
||||
short_description = _('Chat Server')
|
||||
@ -101,6 +105,16 @@ class MatrixSynapseApp(app_module.App):
|
||||
'matrix-synapse-plinth')
|
||||
self.add(webserver)
|
||||
|
||||
letsencrypt = LetsEncrypt(
|
||||
'letsencrypt-matrixsynapse', domains=get_domains, daemons=[
|
||||
managed_services[0]
|
||||
], should_copy_certificates=True,
|
||||
private_key_path='/etc/matrix-synapse/homeserver.tls.key',
|
||||
certificate_path='/etc/matrix-synapse/homeserver.tls.crt',
|
||||
user_owner='matrix-synapse', group_owner='nogroup',
|
||||
managing_app='matrixsynapse')
|
||||
self.add(letsencrypt)
|
||||
|
||||
daemon = Daemon('daemon-matrixsynapse', managed_services[0])
|
||||
self.add(daemon)
|
||||
|
||||
@ -121,6 +135,15 @@ def setup(helper, old_version=None):
|
||||
helper.call('post', actions.superuser_run, 'matrixsynapse',
|
||||
['post-install'])
|
||||
helper.call('post', app.enable)
|
||||
app.get_component('letsencrypt-matrixsynapse').setup_certificates()
|
||||
|
||||
|
||||
def setup_domain(domain_name):
|
||||
"""Configure a domain name for matrixsynapse."""
|
||||
app.get_component('letsencrypt-matrixsynapse').setup_certificates(
|
||||
[domain_name])
|
||||
actions.superuser_run('matrixsynapse',
|
||||
['setup', '--domain-name', domain_name])
|
||||
|
||||
|
||||
def is_setup():
|
||||
@ -141,6 +164,15 @@ def diagnose():
|
||||
return results
|
||||
|
||||
|
||||
def get_domains():
|
||||
"""Return a list of domains this app is interested in."""
|
||||
domain = get_configured_domain_name()
|
||||
if domain:
|
||||
return [domain]
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_configured_domain_name():
|
||||
"""Return the currently configured domain name."""
|
||||
if not is_setup():
|
||||
@ -159,8 +191,10 @@ def get_public_registration_status():
|
||||
return output.strip() == 'enabled'
|
||||
|
||||
|
||||
def has_valid_certificate():
|
||||
"""Return whether the configured domain name has a valid certificate."""
|
||||
status = actions.superuser_run('matrixsynapse',
|
||||
['letsencrypt', 'get-status'])
|
||||
return status.startswith('valid')
|
||||
def get_certificate_status():
|
||||
"""Return the status of certificate for the configured domain."""
|
||||
status = app.get_component('letsencrypt-matrixsynapse').get_status()
|
||||
if not status:
|
||||
return 'no-domains'
|
||||
|
||||
return list(status.values())[0]
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if not has_valid_certificate %}
|
||||
{% if certificate_status != "valid" %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{% url 'letsencrypt:index' as letsencrypt_url %}
|
||||
{% blocktrans %}
|
||||
|
||||
@ -30,7 +30,7 @@ from plinth.modules import matrixsynapse
|
||||
from plinth.utils import get_domain_names
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import get_public_registration_status, has_valid_certificate
|
||||
from . import get_public_registration_status
|
||||
from .forms import MatrixSynapseForm
|
||||
|
||||
|
||||
@ -42,10 +42,7 @@ class SetupView(FormView):
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Handle valid form submission."""
|
||||
domain_name = form.cleaned_data['domain_name']
|
||||
actions.superuser_run('matrixsynapse',
|
||||
['setup', '--domain-name', domain_name])
|
||||
|
||||
matrixsynapse.setup_domain(form.cleaned_data['domain_name'])
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
@ -82,7 +79,7 @@ class MatrixSynapseAppView(AppView):
|
||||
context['domain_name'] = matrixsynapse.get_configured_domain_name()
|
||||
context['clients'] = matrixsynapse.clients
|
||||
context['manual_page'] = matrixsynapse.manual_page
|
||||
context['has_valid_certificate'] = has_valid_certificate()
|
||||
context['certificate_status'] = matrixsynapse.get_certificate_status()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user