From 1895d14e12b901ed4928950e6cc3b1e2e6fd89cd Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 15 Feb 2019 12:05:08 +0000 Subject: [PATCH 1/8] Support .well-known delegation when issuing certificates through ACME --- changelog.d/4652.feature | 1 + synapse/handlers/acme.py | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 changelog.d/4652.feature diff --git a/changelog.d/4652.feature b/changelog.d/4652.feature new file mode 100644 index 000000000..48d9bb08a --- /dev/null +++ b/changelog.d/4652.feature @@ -0,0 +1 @@ +Support .well-known delegation when issuing certificates through ACME diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index dd0b21796..9d1b1a1c2 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -25,8 +25,11 @@ from twisted.python.filepath import FilePath from twisted.python.url import URL from twisted.web import server, static from twisted.web.resource import Resource +from twisted.web.client import URI from synapse.app import check_bind_error +from synapse.crypto.context_factory import ClientTLSOptionsFactory +from synapse.http.federation.matrix_federation_agent import MatrixFederationAgent logger = logging.getLogger(__name__) @@ -123,15 +126,31 @@ class AcmeHandler(object): @defer.inlineCallbacks def provision_certificate(self): - logger.warning("Reprovisioning %s", self.hs.hostname) + # Retrieve .well-known if it's in use. We do so through the federation + # agent, because that's where the .well-known logic lives. + agent = MatrixFederationAgent( + tls_client_options_factory=ClientTLSOptionsFactory(None), + reactor=self.reactor, + ) + delegated = yield agent._get_well_known(bytes(self.hs.hostname,"ascii")) + + # If .well-known is in use, use the delegated hostname instead of the + # homeserver's server_name. + if delegated: + cert_name = delegated.decode("ascii") + logger.info(".well-known is in use, provisionning %s instead of %s", cert_name, self.hs.hostname) + else: + cert_name = self.hs.hostname + + logger.warning("Reprovisioning %s", cert_name) try: - yield self._issuer.issue_cert(self.hs.hostname) + yield self._issuer.issue_cert(cert_name) except Exception: logger.exception("Fail!") raise - logger.warning("Reprovisioned %s, saving.", self.hs.hostname) - cert_chain = self._store.certs[self.hs.hostname] + logger.warning("Reprovisioned %s, saving.", cert_name) + cert_chain = self._store.certs[cert_name] try: with open(self.hs.config.tls_private_key_file, "wb") as private_key_file: From af8a2f679b38d0e3594e172b3b4f7a7c4468193e Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 15 Feb 2019 12:27:43 +0000 Subject: [PATCH 2/8] Remove unused import --- synapse/handlers/acme.py | 1 - 1 file changed, 1 deletion(-) diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index 9d1b1a1c2..93a6a36e6 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -25,7 +25,6 @@ from twisted.python.filepath import FilePath from twisted.python.url import URL from twisted.web import server, static from twisted.web.resource import Resource -from twisted.web.client import URI from synapse.app import check_bind_error from synapse.crypto.context_factory import ClientTLSOptionsFactory From f86b695cbd6a39492946fcddbfcc241ff836e767 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 15 Feb 2019 12:29:34 +0000 Subject: [PATCH 3/8] Various cosmetics to make TravisCI happy --- synapse/handlers/acme.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index 93a6a36e6..a56a9cd28 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -131,13 +131,16 @@ class AcmeHandler(object): tls_client_options_factory=ClientTLSOptionsFactory(None), reactor=self.reactor, ) - delegated = yield agent._get_well_known(bytes(self.hs.hostname,"ascii")) + delegated = yield agent._get_well_known(bytes(self.hs.hostname, "ascii")) # If .well-known is in use, use the delegated hostname instead of the # homeserver's server_name. if delegated: cert_name = delegated.decode("ascii") - logger.info(".well-known is in use, provisionning %s instead of %s", cert_name, self.hs.hostname) + logger.info( + ".well-known is in use, provisionning %s instead of %s", + cert_name, self.hs.hostname, + ) else: cert_name = self.hs.hostname From 6d02a13d81f7d99cb92081631b188398eea0c4d7 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Mon, 18 Feb 2019 11:36:34 +0000 Subject: [PATCH 4/8] Typo in info log Co-Authored-By: babolivier --- synapse/handlers/acme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index a56a9cd28..ca5b7257d 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -138,7 +138,7 @@ class AcmeHandler(object): if delegated: cert_name = delegated.decode("ascii") logger.info( - ".well-known is in use, provisionning %s instead of %s", + ".well-known is in use, provisioning %s instead of %s", cert_name, self.hs.hostname, ) else: From 5b68e12fd842272833d065dab952ec66a6f3f9e6 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Mon, 18 Feb 2019 11:36:44 +0000 Subject: [PATCH 5/8] Typo in changelog Co-Authored-By: babolivier --- changelog.d/4652.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/4652.feature b/changelog.d/4652.feature index 48d9bb08a..ebe6880b2 100644 --- a/changelog.d/4652.feature +++ b/changelog.d/4652.feature @@ -1 +1 @@ -Support .well-known delegation when issuing certificates through ACME +Support .well-known delegation when issuing certificates through ACME. From 45bb55c6de8b50fdd00893a6ef86623d2f34b864 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Mon, 18 Feb 2019 15:46:23 +0000 Subject: [PATCH 6/8] Use a configuration parameter to give the domain to generate a certificate for --- synapse/config/tls.py | 7 +++++++ synapse/handlers/acme.py | 29 ++++------------------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/synapse/config/tls.py b/synapse/config/tls.py index 5fb3486db..a3a5ece68 100644 --- a/synapse/config/tls.py +++ b/synapse/config/tls.py @@ -42,6 +42,7 @@ class TlsConfig(Config): self.acme_port = acme_config.get("port", 80) self.acme_bind_addresses = acme_config.get("bind_addresses", ['::', '0.0.0.0']) self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30) + self.acme_domain = acme_config.get("domain", config.get("server_name")) self.tls_certificate_file = self.abspath(config.get("tls_certificate_path")) self.tls_private_key_file = self.abspath(config.get("tls_private_key_path")) @@ -229,6 +230,12 @@ class TlsConfig(Config): # # reprovision_threshold: 30 + # What domain the certificate should be for. Only useful if + # delegation via a /.well-known/matrix/server file is being used. + # Defaults to the server_name configuration parameter. + # + # domain: matrix.example.com + # 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 ca5b7257d..f8a786a4d 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -27,8 +27,6 @@ from twisted.web import server, static from twisted.web.resource import Resource from synapse.app import check_bind_error -from synapse.crypto.context_factory import ClientTLSOptionsFactory -from synapse.http.federation.matrix_federation_agent import MatrixFederationAgent logger = logging.getLogger(__name__) @@ -125,34 +123,15 @@ class AcmeHandler(object): @defer.inlineCallbacks def provision_certificate(self): - # Retrieve .well-known if it's in use. We do so through the federation - # agent, because that's where the .well-known logic lives. - agent = MatrixFederationAgent( - tls_client_options_factory=ClientTLSOptionsFactory(None), - reactor=self.reactor, - ) - delegated = yield agent._get_well_known(bytes(self.hs.hostname, "ascii")) - - # If .well-known is in use, use the delegated hostname instead of the - # homeserver's server_name. - if delegated: - cert_name = delegated.decode("ascii") - logger.info( - ".well-known is in use, provisioning %s instead of %s", - cert_name, self.hs.hostname, - ) - else: - cert_name = self.hs.hostname - - logger.warning("Reprovisioning %s", cert_name) + logger.warning("Reprovisioning %s", self.hs.config.acme_domain) try: - yield self._issuer.issue_cert(cert_name) + yield self._issuer.issue_cert(self.hs.config.acme_domain) except Exception: logger.exception("Fail!") raise - logger.warning("Reprovisioned %s, saving.", cert_name) - cert_chain = self._store.certs[cert_name] + logger.warning("Reprovisioned %s, saving.", self.hs.config.acme_domain) + cert_chain = self._store.certs[self.hs.config.acme_domain] try: with open(self.hs.config.tls_private_key_file, "wb") as private_key_file: From a8626901cd384f263c8ae578466f95f0c3cceb95 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Tue, 19 Feb 2019 10:54:33 +0000 Subject: [PATCH 7/8] Fetch ACME domain into an instance member --- synapse/handlers/acme.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/synapse/handlers/acme.py b/synapse/handlers/acme.py index f8a786a4d..813777bf1 100644 --- a/synapse/handlers/acme.py +++ b/synapse/handlers/acme.py @@ -56,6 +56,7 @@ class AcmeHandler(object): def __init__(self, hs): self.hs = hs self.reactor = hs.get_reactor() + self._acme_domain = hs.config.acme_domain @defer.inlineCallbacks def start_listening(self): @@ -123,15 +124,15 @@ class AcmeHandler(object): @defer.inlineCallbacks def provision_certificate(self): - logger.warning("Reprovisioning %s", self.hs.config.acme_domain) + logger.warning("Reprovisioning %s", self._acme_domain) try: - yield self._issuer.issue_cert(self.hs.config.acme_domain) + yield self._issuer.issue_cert(self._acme_domain) except Exception: logger.exception("Fail!") raise - logger.warning("Reprovisioned %s, saving.", self.hs.config.acme_domain) - cert_chain = self._store.certs[self.hs.config.acme_domain] + logger.warning("Reprovisioned %s, saving.", self._acme_domain) + cert_chain = self._store.certs[self._acme_domain] try: with open(self.hs.config.tls_private_key_file, "wb") as private_key_file: From 5a707a2f9a82ed67f5339ff2c6898790341ce20f Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Tue, 19 Feb 2019 10:59:26 +0000 Subject: [PATCH 8/8] Improve config documentation --- synapse/config/tls.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/synapse/config/tls.py b/synapse/config/tls.py index a3a5ece68..38425bb05 100644 --- a/synapse/config/tls.py +++ b/synapse/config/tls.py @@ -230,9 +230,17 @@ class TlsConfig(Config): # # reprovision_threshold: 30 - # What domain the certificate should be for. Only useful if - # delegation via a /.well-known/matrix/server file is being used. - # Defaults to the server_name configuration parameter. + # The domain that the certificate should be for. Normally this + # should be the same as your Matrix domain (i.e., 'server_name'), but, + # by putting a file at 'https:///.well-known/matrix/server', + # you can delegate incoming traffic to another server. If you do that, + # you should give the target of the delegation here. + # + # For example: if your 'server_name' is 'example.com', but + # 'https://example.com/.well-known/matrix/server' delegates to + # 'matrix.example.com', you should put 'matrix.example.com' here. + # + # If not set, defaults to your 'server_name'. # # domain: matrix.example.com