Repository: matrix-org/synapse

Issue:
ACME should support being given an alternate hostname
For people who don't want to deal with certificate management and also don't have their synapse and webserver in the same place. 

ie: support .well-known delegation from 1708 and ACME.


Discussion:
We deliberately decided not to support this, at least to begin with. I think it's easy enough and sensible enough (I would use it myself), so maybe we ought to scope it. cc @neilisfragile 
as a datapoint: I've ended up having to do my own certificate management on 4 homeservers so far. This might be down to all the environments being similar/done differently from what is expected, however the built-in acme stuff just hasn't been useful in my upgrade spree (so far).
what exactly is this asking for? It sounds like it's asking for synapse to support configuring a SAN cert which covers both the server_name and an alternate hostname (for the client, presumably), but also says:

> support .well-known delegation from 1708 and ACME.

which sounds like something different that I don't really understand?
Specifically a config option under the acme section for which single server name to acquire a certificate for. 
oh, so you want to have:
 * a separate webserver on `example.com` which serves up a .well-known, which delegates to `synapse.example.com`
 * `synapse.example.com` points to the synapse server
 * synapse acquires a certificate for `synapse.example.com` despite having `server_name: example.com`.

Sounds plausible to me, and agreed it sounds useful.
Yup, that. Nothing fancier than that.
@neilisfragile I think we should scope this for 1.0. Thoughts?

Resolving patch:
diff --git a/synapse/config/tls.py b/synapse/config/tls.py
--- a/synapse/config/tls.py
+++ b/synapse/config/tls.py
@@ -42,6 +42,7 @@ def read_config(self, 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,20 @@ def default_config(self, config_dir_path, server_name, **kwargs):
             #
             # reprovision_threshold: 30
 
+            # 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://<server_name>/.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
+
         # 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
--- 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 @@ def start_listening(self):
     @defer.inlineCallbacks
     def provision_certificate(self):
 
-        logger.warning("Reprovisioning %s", self.hs.hostname)
+        logger.warning("Reprovisioning %s", self._acme_domain)
 
         try:
-            yield self._issuer.issue_cert(self.hs.hostname)
+            yield self._issuer.issue_cert(self._acme_domain)
         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.", 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:


Test patch: