tls.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. import os
  17. from datetime import datetime
  18. from hashlib import sha256
  19. from unpaddedbase64 import encode_base64
  20. from OpenSSL import crypto
  21. from synapse.config._base import Config
  22. logger = logging.getLogger()
  23. class TlsConfig(Config):
  24. def read_config(self, config):
  25. acme_config = config.get("acme", {})
  26. self.acme_enabled = acme_config.get("enabled", False)
  27. self.acme_url = acme_config.get(
  28. "url", "https://acme-v01.api.letsencrypt.org/directory"
  29. )
  30. self.acme_port = acme_config.get("port", 8449)
  31. self.acme_bind_addresses = acme_config.get("bind_addresses", ["127.0.0.1"])
  32. self.acme_reprovision_threshold = acme_config.get("reprovision_threshold", 30)
  33. self.tls_certificate_file = os.path.abspath(config.get("tls_certificate_path"))
  34. self.tls_private_key_file = os.path.abspath(config.get("tls_private_key_path"))
  35. self._original_tls_fingerprints = config["tls_fingerprints"]
  36. self.tls_fingerprints = list(self._original_tls_fingerprints)
  37. self.no_tls = config.get("no_tls", False)
  38. # This config option applies to non-federation HTTP clients
  39. # (e.g. for talking to recaptcha, identity servers, and such)
  40. # It should never be used in production, and is intended for
  41. # use only when running tests.
  42. self.use_insecure_ssl_client_just_for_testing_do_not_use = config.get(
  43. "use_insecure_ssl_client_just_for_testing_do_not_use"
  44. )
  45. self.tls_certificate = None
  46. self.tls_private_key = None
  47. def is_disk_cert_valid(self):
  48. """
  49. Is the certificate we have on disk valid, and if so, for how long?
  50. Returns:
  51. int: Days remaining of certificate validity.
  52. None: No certificate exists.
  53. """
  54. if not os.path.exists(self.tls_certificate_file):
  55. return None
  56. try:
  57. with open(self.tls_certificate_file, 'rb') as f:
  58. cert_pem = f.read()
  59. except Exception:
  60. logger.exception("Failed to read existing certificate off disk!")
  61. raise
  62. try:
  63. tls_certificate = crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem)
  64. except Exception:
  65. logger.exception("Failed to parse existing certificate off disk!")
  66. raise
  67. # YYYYMMDDhhmmssZ -- in UTC
  68. expires_on = datetime.strptime(
  69. tls_certificate.get_notAfter().decode('ascii'), "%Y%m%d%H%M%SZ"
  70. )
  71. now = datetime.utcnow()
  72. days_remaining = (expires_on - now).days
  73. return days_remaining
  74. def read_certificate_from_disk(self):
  75. """
  76. Read the certificates from disk.
  77. """
  78. self.tls_certificate = self.read_tls_certificate(self.tls_certificate_file)
  79. if not self.no_tls:
  80. self.tls_private_key = self.read_tls_private_key(self.tls_private_key_file)
  81. self.tls_fingerprints = list(self._original_tls_fingerprints)
  82. # Check that our own certificate is included in the list of fingerprints
  83. # and include it if it is not.
  84. x509_certificate_bytes = crypto.dump_certificate(
  85. crypto.FILETYPE_ASN1, self.tls_certificate
  86. )
  87. sha256_fingerprint = encode_base64(sha256(x509_certificate_bytes).digest())
  88. sha256_fingerprints = set(f["sha256"] for f in self.tls_fingerprints)
  89. if sha256_fingerprint not in sha256_fingerprints:
  90. self.tls_fingerprints.append({u"sha256": sha256_fingerprint})
  91. def default_config(self, config_dir_path, server_name, **kwargs):
  92. base_key_name = os.path.join(config_dir_path, server_name)
  93. tls_certificate_path = base_key_name + ".tls.crt"
  94. tls_private_key_path = base_key_name + ".tls.key"
  95. return (
  96. """\
  97. # PEM encoded X509 certificate for TLS.
  98. # You can replace the self-signed certificate that synapse
  99. # autogenerates on launch with your own SSL certificate + key pair
  100. # if you like. Any required intermediary certificates can be
  101. # appended after the primary certificate in hierarchical order.
  102. tls_certificate_path: "%(tls_certificate_path)s"
  103. # PEM encoded private key for TLS
  104. tls_private_key_path: "%(tls_private_key_path)s"
  105. # Don't bind to the https port
  106. no_tls: False
  107. # List of allowed TLS fingerprints for this server to publish along
  108. # with the signing keys for this server. Other matrix servers that
  109. # make HTTPS requests to this server will check that the TLS
  110. # certificates returned by this server match one of the fingerprints.
  111. #
  112. # Synapse automatically adds the fingerprint of its own certificate
  113. # to the list. So if federation traffic is handled directly by synapse
  114. # then no modification to the list is required.
  115. #
  116. # If synapse is run behind a load balancer that handles the TLS then it
  117. # will be necessary to add the fingerprints of the certificates used by
  118. # the loadbalancers to this list if they are different to the one
  119. # synapse is using.
  120. #
  121. # Homeservers are permitted to cache the list of TLS fingerprints
  122. # returned in the key responses up to the "valid_until_ts" returned in
  123. # key. It may be necessary to publish the fingerprints of a new
  124. # certificate and wait until the "valid_until_ts" of the previous key
  125. # responses have passed before deploying it.
  126. #
  127. # You can calculate a fingerprint from a given TLS listener via:
  128. # openssl s_client -connect $host:$port < /dev/null 2> /dev/null |
  129. # openssl x509 -outform DER | openssl sha256 -binary | base64 | tr -d '='
  130. # or by checking matrix.org/federationtester/api/report?server_name=$host
  131. #
  132. tls_fingerprints: []
  133. # tls_fingerprints: [{"sha256": "<base64_encoded_sha256_fingerprint>"}]
  134. ## Support for ACME certificate auto-provisioning.
  135. # acme:
  136. # enabled: false
  137. ## ACME path.
  138. ## If you only want to test, use the staging url:
  139. ## https://acme-staging.api.letsencrypt.org/directory
  140. # url: 'https://acme-v01.api.letsencrypt.org/directory'
  141. ## Port number (to listen for the HTTP-01 challenge).
  142. ## Using port 80 requires utilising something like authbind, or proxying to it.
  143. # port: 8449
  144. ## Hosts to bind to.
  145. # bind_addresses: ['127.0.0.1']
  146. ## How many days remaining on a certificate before it is renewed.
  147. # reprovision_threshold: 30
  148. """
  149. % locals()
  150. )
  151. def read_tls_certificate(self, cert_path):
  152. cert_pem = self.read_file(cert_path, "tls_certificate")
  153. return crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem)
  154. def read_tls_private_key(self, private_key_path):
  155. private_key_pem = self.read_file(private_key_path, "tls_private_key")
  156. return crypto.load_privatekey(crypto.FILETYPE_PEM, private_key_pem)
  157. def generate_files(self, config):
  158. tls_certificate_path = config["tls_certificate_path"]
  159. tls_private_key_path = config["tls_private_key_path"]
  160. if not self.path_exists(tls_private_key_path):
  161. with open(tls_private_key_path, "wb") as private_key_file:
  162. tls_private_key = crypto.PKey()
  163. tls_private_key.generate_key(crypto.TYPE_RSA, 2048)
  164. private_key_pem = crypto.dump_privatekey(
  165. crypto.FILETYPE_PEM, tls_private_key
  166. )
  167. private_key_file.write(private_key_pem)
  168. else:
  169. with open(tls_private_key_path) as private_key_file:
  170. private_key_pem = private_key_file.read()
  171. tls_private_key = crypto.load_privatekey(
  172. crypto.FILETYPE_PEM, private_key_pem
  173. )
  174. if not self.path_exists(tls_certificate_path):
  175. with open(tls_certificate_path, "wb") as certificate_file:
  176. cert = crypto.X509()
  177. subject = cert.get_subject()
  178. subject.CN = config["server_name"]
  179. cert.set_serial_number(1000)
  180. cert.gmtime_adj_notBefore(0)
  181. cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
  182. cert.set_issuer(cert.get_subject())
  183. cert.set_pubkey(tls_private_key)
  184. cert.sign(tls_private_key, 'sha256')
  185. cert_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
  186. certificate_file.write(cert_pem)