From dddf20e8e146bb77be449e791a98ec24018c35d9 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 24 Jun 2019 10:06:51 +0100 Subject: Fix /messages on workers when no from param specified. If no `from` param is specified we calculate and use the "current token" that inlcuded typing, presence, etc. These are unused during pagination and are not available on workers, so we simply don't calculate them. --- synapse/handlers/pagination.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'synapse/handlers') diff --git a/synapse/handlers/pagination.py b/synapse/handlers/pagination.py index 062e026e5f..76ee97ddd3 100644 --- a/synapse/handlers/pagination.py +++ b/synapse/handlers/pagination.py @@ -180,9 +180,7 @@ class PaginationHandler(object): room_token = pagin_config.from_token.room_key else: pagin_config.from_token = ( - yield self.hs.get_event_sources().get_current_token_for_room( - room_id=room_id - ) + yield self.hs.get_event_sources().get_current_token_for_pagination() ) room_token = pagin_config.from_token.room_key -- cgit 1.5.1 From 21bf4318b58be4d56b854825eafa83fc53c448f6 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 24 Jun 2019 11:33:56 +0100 Subject: Factor acme bits out to a separate file (#5521) This makes some of the conditional-import hoop-jumping easier. --- changelog.d/5521.misc | 1 + synapse/handlers/acme.py | 62 ++++------------------- synapse/handlers/acme_issuing_service.py | 84 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 53 deletions(-) create mode 100644 changelog.d/5521.misc create mode 100644 synapse/handlers/acme_issuing_service.py (limited to 'synapse/handlers') diff --git a/changelog.d/5521.misc b/changelog.d/5521.misc new file mode 100644 index 0000000000..e3a14fdeaf --- /dev/null +++ b/changelog.d/5521.misc @@ -0,0 +1 @@ +Factor acme bits out to a separate file. diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index 01e0ef408d..a760372203 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -15,14 +15,9 @@ import logging -import attr -from zope.interface import implementer - import twisted import twisted.internet.error from twisted.internet import defer -from twisted.python.filepath import FilePath -from twisted.python.url import URL from twisted.web import server, static from twisted.web.resource import Resource @@ -30,27 +25,6 @@ from synapse.app import check_bind_error logger = logging.getLogger(__name__) -try: - from txacme.interfaces import ICertificateStore - - @attr.s - @implementer(ICertificateStore) - class ErsatzStore(object): - """ - A store that only stores in memory. - """ - - certs = attr.ib(default=attr.Factory(dict)) - - def store(self, server_name, pem_objects): - self.certs[server_name] = [o.as_bytes() for o in pem_objects] - return defer.succeed(None) - - -except ImportError: - # txacme is missing - pass - class AcmeHandler(object): def __init__(self, hs): @@ -60,6 +34,7 @@ class AcmeHandler(object): @defer.inlineCallbacks def start_listening(self): + from synapse.handlers import acme_issuing_service # Configure logging for txacme, if you need to debug # from eliot import add_destinations @@ -67,37 +42,18 @@ class AcmeHandler(object): # # add_destinations(TwistedDestination()) - from txacme.challenges import HTTP01Responder - from txacme.service import AcmeIssuingService - from txacme.endpoint import load_or_create_client_key - from txacme.client import Client - from josepy.jwa import RS256 - - self._store = ErsatzStore() - responder = HTTP01Responder() - - self._issuer = AcmeIssuingService( - cert_store=self._store, - client_creator=( - lambda: Client.from_url( - reactor=self.reactor, - url=URL.from_text(self.hs.config.acme_url), - key=load_or_create_client_key( - FilePath(self.hs.config.config_dir_path) - ), - alg=RS256, - ) - ), - clock=self.reactor, - responders=[responder], + well_known = Resource() + + self._issuer = acme_issuing_service.create_issuing_service( + self.reactor, + acme_url=self.hs.config.acme_url, + pem_path=self.hs.config.config_dir_path, + well_known_resource=well_known, ) - well_known = Resource() - well_known.putChild(b"acme-challenge", responder.resource) responder_resource = Resource() responder_resource.putChild(b".well-known", well_known) responder_resource.putChild(b"check", static.Data(b"OK", b"text/plain")) - srv = server.Site(responder_resource) bind_addresses = self.hs.config.acme_bind_addresses @@ -128,7 +84,7 @@ class AcmeHandler(object): logger.exception("Fail!") raise logger.warning("Reprovisioned %s, saving.", self._acme_domain) - cert_chain = self._store.certs[self._acme_domain] + cert_chain = self._issuer.cert_store.certs[self._acme_domain] try: with open(self.hs.config.tls_private_key_file, "wb") as private_key_file: diff --git a/synapse/handlers/acme_issuing_service.py b/synapse/handlers/acme_issuing_service.py new file mode 100644 index 0000000000..70e73d2be0 --- /dev/null +++ b/synapse/handlers/acme_issuing_service.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Utility function to create an ACME issuing service. + +This file contains the unconditional imports on the acme and cryptography bits that we +only need (and may only have available) if we are doing ACME, so is designed to be +imported conditionally. +""" + +import attr +from josepy.jwa import RS256 +from txacme.challenges import HTTP01Responder +from txacme.client import Client +from txacme.endpoint import load_or_create_client_key +from txacme.interfaces import ICertificateStore +from txacme.service import AcmeIssuingService +from zope.interface import implementer + +from twisted.internet import defer +from twisted.python.filepath import FilePath +from twisted.python.url import URL + + +def create_issuing_service(reactor, acme_url, pem_path, well_known_resource): + """Create an ACME issuing service, and attach it to a web Resource + + Args: + reactor: twisted reactor + acme_url (str): URL to use to request certificates + pem_path (str): where to store the client key + well_known_resource (twisted.web.IResource): web resource for .well-known. + we will attach a child resource for "acme-challenge". + + Returns: + AcmeIssuingService + """ + responder = HTTP01Responder() + + well_known_resource.putChild(b"acme-challenge", responder.resource) + + store = ErsatzStore() + + return AcmeIssuingService( + cert_store=store, + client_creator=( + lambda: Client.from_url( + reactor=reactor, + url=URL.from_text(acme_url), + key=load_or_create_client_key(FilePath(pem_path)), + alg=RS256, + ) + ), + clock=reactor, + responders=[responder], + ) + + +@attr.s +@implementer(ICertificateStore) +class ErsatzStore(object): + """ + A store that only stores in memory. + """ + + certs = attr.ib(default=attr.Factory(dict)) + + def store(self, server_name, pem_objects): + self.certs[server_name] = [o.as_bytes() for o in pem_objects] + return defer.succeed(None) -- cgit 1.5.1 From edea4bb5bed609ec011dd1f04256912a1a54e03f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 21 Jun 2019 15:27:41 +0100 Subject: Allow configuration of the path used for ACME account keys. Because sticking it in the same place as the config isn't necessarily the right thing to do. --- docs/sample_config.yaml | 7 ++++++ synapse/config/tls.py | 16 +++++++++++-- synapse/handlers/acme.py | 2 +- synapse/handlers/acme_issuing_service.py | 41 ++++++++++++++++++++++++++++---- 4 files changed, 59 insertions(+), 7 deletions(-) (limited to 'synapse/handlers') diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index d5cc3e7abc..bb07b02f4e 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -402,6 +402,13 @@ acme: # #domain: matrix.example.com + # file to use for the account key. This will be generated if it doesn't + # exist. + # + # If unspecified, we will use CONFDIR/client.key. + # + account_key_file: DATADIR/acme_account.key + # List of allowed TLS fingerprints for this server to publish along # with the signing keys for this server. Other matrix servers that # make HTTPS requests to this server will check that the TLS diff --git a/synapse/config/tls.py b/synapse/config/tls.py index 28be4366d6..9a66e8cc4b 100644 --- a/synapse/config/tls.py +++ b/synapse/config/tls.py @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) class TlsConfig(Config): - def read_config(self, config, **kwargs): + def read_config(self, config, config_dir_path, **kwargs): acme_config = config.get("acme", None) if acme_config is None: @@ -50,6 +50,10 @@ class TlsConfig(Config): self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30) self.acme_domain = acme_config.get("domain", config.get("server_name")) + self.acme_account_key_file = self.abspath( + acme_config.get("account_key_file", config_dir_path + "/client.key") + ) + self.tls_certificate_file = self.abspath(config.get("tls_certificate_path")) self.tls_private_key_file = self.abspath(config.get("tls_private_key_path")) @@ -213,11 +217,12 @@ class TlsConfig(Config): if sha256_fingerprint not in sha256_fingerprints: self.tls_fingerprints.append({"sha256": sha256_fingerprint}) - def default_config(self, config_dir_path, server_name, **kwargs): + def default_config(self, config_dir_path, server_name, data_dir_path, **kwargs): base_key_name = os.path.join(config_dir_path, server_name) tls_certificate_path = base_key_name + ".tls.crt" tls_private_key_path = base_key_name + ".tls.key" + default_acme_account_file = os.path.join(data_dir_path, "acme_account.key") # this is to avoid the max line length. Sorrynotsorry proxypassline = ( @@ -343,6 +348,13 @@ class TlsConfig(Config): # #domain: matrix.example.com + # file to use for the account key. This will be generated if it doesn't + # exist. + # + # If unspecified, we will use CONFDIR/client.key. + # + account_key_file: %(default_acme_account_file)s + # List of allowed TLS fingerprints for this server to publish along # with the signing keys for this server. Other matrix servers that # make HTTPS requests to this server will check that the TLS diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index a760372203..fbef2f3d38 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -47,7 +47,7 @@ class AcmeHandler(object): self._issuer = acme_issuing_service.create_issuing_service( self.reactor, acme_url=self.hs.config.acme_url, - pem_path=self.hs.config.config_dir_path, + account_key_file=self.hs.config.acme_account_key_file, well_known_resource=well_known, ) diff --git a/synapse/handlers/acme_issuing_service.py b/synapse/handlers/acme_issuing_service.py index 70e73d2be0..e1d4224e74 100644 --- a/synapse/handlers/acme_issuing_service.py +++ b/synapse/handlers/acme_issuing_service.py @@ -21,28 +21,34 @@ This file contains the unconditional imports on the acme and cryptography bits t only need (and may only have available) if we are doing ACME, so is designed to be imported conditionally. """ +import logging import attr +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from josepy import JWKRSA from josepy.jwa import RS256 from txacme.challenges import HTTP01Responder from txacme.client import Client -from txacme.endpoint import load_or_create_client_key from txacme.interfaces import ICertificateStore from txacme.service import AcmeIssuingService +from txacme.util import generate_private_key from zope.interface import implementer from twisted.internet import defer from twisted.python.filepath import FilePath from twisted.python.url import URL +logger = logging.getLogger(__name__) -def create_issuing_service(reactor, acme_url, pem_path, well_known_resource): + +def create_issuing_service(reactor, acme_url, account_key_file, well_known_resource): """Create an ACME issuing service, and attach it to a web Resource Args: reactor: twisted reactor acme_url (str): URL to use to request certificates - pem_path (str): where to store the client key + account_key_file (str): where to store the account key well_known_resource (twisted.web.IResource): web resource for .well-known. we will attach a child resource for "acme-challenge". @@ -61,7 +67,7 @@ def create_issuing_service(reactor, acme_url, pem_path, well_known_resource): lambda: Client.from_url( reactor=reactor, url=URL.from_text(acme_url), - key=load_or_create_client_key(FilePath(pem_path)), + key=load_or_create_client_key(account_key_file), alg=RS256, ) ), @@ -82,3 +88,30 @@ class ErsatzStore(object): def store(self, server_name, pem_objects): self.certs[server_name] = [o.as_bytes() for o in pem_objects] return defer.succeed(None) + + +def load_or_create_client_key(key_file): + """Load the ACME account key from a file, creating it if it does not exist. + + Args: + key_file (str): name of the file to use as the account key + """ + # this is based on txacme.endpoint.load_or_create_client_key, but doesn't + # hardcode the 'client.key' filename + acme_key_file = FilePath(key_file) + if acme_key_file.exists(): + logger.info("Loading ACME account key from '%s'", acme_key_file) + key = serialization.load_pem_private_key( + acme_key_file.getContent(), password=None, backend=default_backend() + ) + else: + logger.info("Saving new ACME account key to '%s'", acme_key_file) + key = generate_private_key("rsa") + acme_key_file.setContent( + key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + return JWKRSA(key=key) -- cgit 1.5.1