tls.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. # Copyright 2014-2016 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import logging
  15. import os
  16. from typing import List, Optional, Pattern
  17. from OpenSSL import SSL, crypto
  18. from twisted.internet._sslverify import Certificate, trustRootFromCertificates
  19. from synapse.config._base import Config, ConfigError
  20. from synapse.util import glob_to_regex
  21. logger = logging.getLogger(__name__)
  22. class TlsConfig(Config):
  23. section = "tls"
  24. def read_config(self, config: dict, config_dir_path: str, **kwargs):
  25. self.tls_certificate_file = self.abspath(config.get("tls_certificate_path"))
  26. self.tls_private_key_file = self.abspath(config.get("tls_private_key_path"))
  27. if self.root.server.has_tls_listener():
  28. if not self.tls_certificate_file:
  29. raise ConfigError(
  30. "tls_certificate_path must be specified if TLS-enabled listeners are "
  31. "configured."
  32. )
  33. if not self.tls_private_key_file:
  34. raise ConfigError(
  35. "tls_private_key_path must be specified if TLS-enabled listeners are "
  36. "configured."
  37. )
  38. # Whether to verify certificates on outbound federation traffic
  39. self.federation_verify_certificates = config.get(
  40. "federation_verify_certificates", True
  41. )
  42. # Minimum TLS version to use for outbound federation traffic
  43. self.federation_client_minimum_tls_version = str(
  44. config.get("federation_client_minimum_tls_version", 1)
  45. )
  46. if self.federation_client_minimum_tls_version not in ["1", "1.1", "1.2", "1.3"]:
  47. raise ConfigError(
  48. "federation_client_minimum_tls_version must be one of: 1, 1.1, 1.2, 1.3"
  49. )
  50. # Prevent people shooting themselves in the foot here by setting it to
  51. # the biggest number blindly
  52. if self.federation_client_minimum_tls_version == "1.3":
  53. if getattr(SSL, "OP_NO_TLSv1_3", None) is None:
  54. raise ConfigError(
  55. "federation_client_minimum_tls_version cannot be 1.3, "
  56. "your OpenSSL does not support it"
  57. )
  58. # Whitelist of domains to not verify certificates for
  59. fed_whitelist_entries = config.get(
  60. "federation_certificate_verification_whitelist", []
  61. )
  62. if fed_whitelist_entries is None:
  63. fed_whitelist_entries = []
  64. # Support globs (*) in whitelist values
  65. self.federation_certificate_verification_whitelist: List[Pattern] = []
  66. for entry in fed_whitelist_entries:
  67. try:
  68. entry_regex = glob_to_regex(entry.encode("ascii").decode("ascii"))
  69. except UnicodeEncodeError:
  70. raise ConfigError(
  71. "IDNA domain names are not allowed in the "
  72. "federation_certificate_verification_whitelist: %s" % (entry,)
  73. )
  74. # Convert globs to regex
  75. self.federation_certificate_verification_whitelist.append(entry_regex)
  76. # List of custom certificate authorities for federation traffic validation
  77. custom_ca_list = config.get("federation_custom_ca_list", None)
  78. # Read in and parse custom CA certificates
  79. self.federation_ca_trust_root = None
  80. if custom_ca_list is not None:
  81. if len(custom_ca_list) == 0:
  82. # A trustroot cannot be generated without any CA certificates.
  83. # Raise an error if this option has been specified without any
  84. # corresponding certificates.
  85. raise ConfigError(
  86. "federation_custom_ca_list specified without "
  87. "any certificate files"
  88. )
  89. certs = []
  90. for ca_file in custom_ca_list:
  91. logger.debug("Reading custom CA certificate file: %s", ca_file)
  92. content = self.read_file(ca_file, "federation_custom_ca_list")
  93. # Parse the CA certificates
  94. try:
  95. cert_base = Certificate.loadPEM(content)
  96. certs.append(cert_base)
  97. except Exception as e:
  98. raise ConfigError(
  99. "Error parsing custom CA certificate file %s: %s" % (ca_file, e)
  100. )
  101. self.federation_ca_trust_root = trustRootFromCertificates(certs)
  102. # This config option applies to non-federation HTTP clients
  103. # (e.g. for talking to recaptcha, identity servers, and such)
  104. # It should never be used in production, and is intended for
  105. # use only when running tests.
  106. self.use_insecure_ssl_client_just_for_testing_do_not_use = config.get(
  107. "use_insecure_ssl_client_just_for_testing_do_not_use"
  108. )
  109. self.tls_certificate: Optional[crypto.X509] = None
  110. self.tls_private_key: Optional[crypto.PKey] = None
  111. def read_certificate_from_disk(self) -> None:
  112. """
  113. Read the certificates and private key from disk.
  114. """
  115. self.tls_private_key = self.read_tls_private_key()
  116. self.tls_certificate = self.read_tls_certificate()
  117. def generate_config_section(
  118. self,
  119. config_dir_path,
  120. server_name,
  121. data_dir_path,
  122. tls_certificate_path,
  123. tls_private_key_path,
  124. **kwargs,
  125. ):
  126. """If the TLS paths are not specified the default will be certs in the
  127. config directory"""
  128. base_key_name = os.path.join(config_dir_path, server_name)
  129. if bool(tls_certificate_path) != bool(tls_private_key_path):
  130. raise ConfigError(
  131. "Please specify both a cert path and a key path or neither."
  132. )
  133. tls_enabled = "" if tls_certificate_path and tls_private_key_path else "#"
  134. if not tls_certificate_path:
  135. tls_certificate_path = base_key_name + ".tls.crt"
  136. if not tls_private_key_path:
  137. tls_private_key_path = base_key_name + ".tls.key"
  138. # flake8 doesn't recognise that variables are used in the below string
  139. _ = tls_enabled
  140. return (
  141. """\
  142. ## TLS ##
  143. # PEM-encoded X509 certificate for TLS.
  144. # This certificate, as of Synapse 1.0, will need to be a valid and verifiable
  145. # certificate, signed by a recognised Certificate Authority.
  146. #
  147. # Be sure to use a `.pem` file that includes the full certificate chain including
  148. # any intermediate certificates (for instance, if using certbot, use
  149. # `fullchain.pem` as your certificate, not `cert.pem`).
  150. #
  151. %(tls_enabled)stls_certificate_path: "%(tls_certificate_path)s"
  152. # PEM-encoded private key for TLS
  153. #
  154. %(tls_enabled)stls_private_key_path: "%(tls_private_key_path)s"
  155. # Whether to verify TLS server certificates for outbound federation requests.
  156. #
  157. # Defaults to `true`. To disable certificate verification, uncomment the
  158. # following line.
  159. #
  160. #federation_verify_certificates: false
  161. # The minimum TLS version that will be used for outbound federation requests.
  162. #
  163. # Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note
  164. # that setting this value higher than `1.2` will prevent federation to most
  165. # of the public Matrix network: only configure it to `1.3` if you have an
  166. # entirely private federation setup and you can ensure TLS 1.3 support.
  167. #
  168. #federation_client_minimum_tls_version: 1.2
  169. # Skip federation certificate verification on the following whitelist
  170. # of domains.
  171. #
  172. # This setting should only be used in very specific cases, such as
  173. # federation over Tor hidden services and similar. For private networks
  174. # of homeservers, you likely want to use a private CA instead.
  175. #
  176. # Only effective if federation_verify_certicates is `true`.
  177. #
  178. #federation_certificate_verification_whitelist:
  179. # - lon.example.com
  180. # - "*.domain.com"
  181. # - "*.onion"
  182. # List of custom certificate authorities for federation traffic.
  183. #
  184. # This setting should only normally be used within a private network of
  185. # homeservers.
  186. #
  187. # Note that this list will replace those that are provided by your
  188. # operating environment. Certificates must be in PEM format.
  189. #
  190. #federation_custom_ca_list:
  191. # - myCA1.pem
  192. # - myCA2.pem
  193. # - myCA3.pem
  194. """
  195. # Lowercase the string representation of boolean values
  196. % {
  197. x[0]: str(x[1]).lower() if isinstance(x[1], bool) else x[1]
  198. for x in locals().items()
  199. }
  200. )
  201. def read_tls_certificate(self) -> crypto.X509:
  202. """Reads the TLS certificate from the configured file, and returns it
  203. Returns:
  204. The certificate
  205. """
  206. cert_path = self.tls_certificate_file
  207. logger.info("Loading TLS certificate from %s", cert_path)
  208. cert_pem = self.read_file(cert_path, "tls_certificate_path")
  209. cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem.encode())
  210. return cert
  211. def read_tls_private_key(self) -> crypto.PKey:
  212. """Reads the TLS private key from the configured file, and returns it
  213. Returns:
  214. The private key
  215. """
  216. private_key_path = self.tls_private_key_file
  217. logger.info("Loading TLS key from %s", private_key_path)
  218. private_key_pem = self.read_file(private_key_path, "tls_private_key_path")
  219. return crypto.load_privatekey(crypto.FILETYPE_PEM, private_key_pem)