From 08c84693227de9571412fa18a7d82818a370c655 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Thu, 17 Jun 2021 19:56:48 +0200 Subject: Remove support for ACME v1 (#10194) Fixes #9778 ACME v1 has been fully decommissioned for existing installs on June 1st 2021(see https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430/27), so we can now safely remove it from Synapse. --- synapse/handlers/acme.py | 117 ---------------------------- synapse/handlers/acme_issuing_service.py | 127 ------------------------------- 2 files changed, 244 deletions(-) delete mode 100644 synapse/handlers/acme.py delete mode 100644 synapse/handlers/acme_issuing_service.py (limited to 'synapse/handlers') diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py deleted file mode 100644 index 16ab93f580..0000000000 --- a/synapse/handlers/acme.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2019 New Vector Ltd -# -# 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. - -import logging -from typing import TYPE_CHECKING - -import twisted -import twisted.internet.error -from twisted.web import server, static -from twisted.web.resource import Resource - -from synapse.app import check_bind_error - -if TYPE_CHECKING: - from synapse.server import HomeServer - -logger = logging.getLogger(__name__) - -ACME_REGISTER_FAIL_ERROR = """ --------------------------------------------------------------------------------- -Failed to register with the ACME provider. This is likely happening because the installation -is new, and ACME v1 has been deprecated by Let's Encrypt and disabled for -new installations since November 2019. -At the moment, Synapse doesn't support ACME v2. For more information and alternative -solutions, please read https://github.com/matrix-org/synapse/blob/master/docs/ACME.md#deprecation-of-acme-v1 ---------------------------------------------------------------------------------""" - - -class AcmeHandler: - def __init__(self, hs: "HomeServer"): - self.hs = hs - self.reactor = hs.get_reactor() - self._acme_domain = hs.config.acme_domain - - async def start_listening(self) -> None: - from synapse.handlers import acme_issuing_service - - # Configure logging for txacme, if you need to debug - # from eliot import add_destinations - # from eliot.twisted import TwistedDestination - # - # add_destinations(TwistedDestination()) - - well_known = Resource() - - self._issuer = acme_issuing_service.create_issuing_service( - self.reactor, - acme_url=self.hs.config.acme_url, - account_key_file=self.hs.config.acme_account_key_file, - well_known_resource=well_known, - ) - - 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 - for host in bind_addresses: - logger.info( - "Listening for ACME requests on %s:%i", host, self.hs.config.acme_port - ) - try: - self.reactor.listenTCP( - self.hs.config.acme_port, srv, backlog=50, interface=host - ) - except twisted.internet.error.CannotListenError as e: - check_bind_error(e, host, bind_addresses) - - # Make sure we are registered to the ACME server. There's no public API - # for this, it is usually triggered by startService, but since we don't - # want it to control where we save the certificates, we have to reach in - # and trigger the registration machinery ourselves. - self._issuer._registered = False - - try: - await self._issuer._ensure_registered() - except Exception: - logger.error(ACME_REGISTER_FAIL_ERROR) - raise - - async def provision_certificate(self) -> None: - - logger.warning("Reprovisioning %s", self._acme_domain) - - try: - await self._issuer.issue_cert(self._acme_domain) - except Exception: - logger.exception("Fail!") - raise - logger.warning("Reprovisioned %s, saving.", 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: - for x in cert_chain: - if x.startswith(b"-----BEGIN RSA PRIVATE KEY-----"): - private_key_file.write(x) - - with open(self.hs.config.tls_certificate_file, "wb") as certificate_file: - for x in cert_chain: - if x.startswith(b"-----BEGIN CERTIFICATE-----"): - certificate_file.write(x) - except Exception: - logger.exception("Failed saving!") - raise diff --git a/synapse/handlers/acme_issuing_service.py b/synapse/handlers/acme_issuing_service.py deleted file mode 100644 index a972d3fa0a..0000000000 --- a/synapse/handlers/acme_issuing_service.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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 logging -from typing import Dict, Iterable, List - -import attr -import pem -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.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.internet.interfaces import IReactorTCP -from twisted.python.filepath import FilePath -from twisted.python.url import URL -from twisted.web.resource import IResource - -logger = logging.getLogger(__name__) - - -def create_issuing_service( - reactor: IReactorTCP, - acme_url: str, - account_key_file: str, - well_known_resource: IResource, -) -> AcmeIssuingService: - """Create an ACME issuing service, and attach it to a web Resource - - Args: - reactor: twisted reactor - acme_url: URL to use to request certificates - account_key_file: where to store the account key - well_known_resource: 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(account_key_file), - alg=RS256, - ) - ), - clock=reactor, - responders=[responder], - ) - - -@attr.s(slots=True) -@implementer(ICertificateStore) -class ErsatzStore: - """ - A store that only stores in memory. - """ - - certs = attr.ib(type=Dict[bytes, List[bytes]], default=attr.Factory(dict)) - - def store( - self, server_name: bytes, pem_objects: Iterable[pem.AbstractPEMObject] - ) -> defer.Deferred: - self.certs[server_name] = [o.as_bytes() for o in pem_objects] - return defer.succeed(None) - - -def load_or_create_client_key(key_file: str) -> JWKRSA: - """Load the ACME account key from a file, creating it if it does not exist. - - Args: - key_file: 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.4.1