From 9fd1b95244efb91ed653bce94bdfe3b509179020 Mon Sep 17 00:00:00 2001
From: Sunil Mohan Adapa
Date: Fri, 21 Jun 2019 11:11:17 -0700
Subject: [PATCH] matrixsynapse: Add let's encrypt component for certficiates
Signed-off-by: Sunil Mohan Adapa
Reviewed-by: Joseph Nuthalapati
---
actions/matrixsynapse | 105 ------------------
plinth/modules/matrixsynapse/__init__.py | 44 +++++++-
.../templates/matrix-synapse.html | 2 +-
plinth/modules/matrixsynapse/views.py | 9 +-
4 files changed, 43 insertions(+), 117 deletions(-)
diff --git a/actions/matrixsynapse b/actions/matrixsynapse
index 7a689bd61..1215c5a7a 100755
--- a/actions/matrixsynapse
+++ b/actions/matrixsynapse
@@ -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('-', '_')
diff --git a/plinth/modules/matrixsynapse/__init__.py b/plinth/modules/matrixsynapse/__init__.py
index 0db44e08a..a13c9f9a0 100644
--- a/plinth/modules/matrixsynapse/__init__.py
+++ b/plinth/modules/matrixsynapse/__init__.py
@@ -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]
diff --git a/plinth/modules/matrixsynapse/templates/matrix-synapse.html b/plinth/modules/matrixsynapse/templates/matrix-synapse.html
index c157394b2..1cc7121b2 100644
--- a/plinth/modules/matrixsynapse/templates/matrix-synapse.html
+++ b/plinth/modules/matrixsynapse/templates/matrix-synapse.html
@@ -39,7 +39,7 @@
{% endblocktrans %}
- {% if not has_valid_certificate %}
+ {% if certificate_status != "valid" %}
{% url 'letsencrypt:index' as letsencrypt_url %}
{% blocktrans %}
diff --git a/plinth/modules/matrixsynapse/views.py b/plinth/modules/matrixsynapse/views.py
index 8d73f20ae..bea8c3b9d 100644
--- a/plinth/modules/matrixsynapse/views.py
+++ b/plinth/modules/matrixsynapse/views.py
@@ -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):