Skip to content

Commit

Permalink
Support Implicit TLS for sending emails
Browse files Browse the repository at this point in the history
Previously, TLS could only be used with STARTTLS.
Add a new option `implicit_tls`, where TLS is used from the start.
Implicit TLS is recommended over STARTLS,
see https://datatracker.ietf.org/doc/html/rfc8314

Fixes matrix-org#8046.

Signed-off-by: Jan Schär <jan@jschaer.ch>
  • Loading branch information
jscissr committed Jul 18, 2022
1 parent c6a0506 commit f2b0002
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog.d/13317.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support Implicit TLS for sending emails, enabled by the new option `implicit_tls`. Contributed by Jan Schär.
7 changes: 6 additions & 1 deletion docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3150,9 +3150,13 @@ Server admins can configure custom templates for email content. See

This setting has the following sub-options:
* `smtp_host`: The hostname of the outgoing SMTP server to use. Defaults to 'localhost'.
* `smtp_port`: The port on the mail server for outgoing SMTP. Defaults to 25.
* `smtp_port`: The port on the mail server for outgoing SMTP. Defaults to 465 if `implicit_tls` is true, else 25.
* `smtp_user` and `smtp_pass`: Username/password for authentication to the SMTP server. By default, no
authentication is attempted.
* `implicit_tls`: By default, Synapse connects over plain text and then optionally upgrades
to TLS via STARTTLS. If this option is set to true, TLS is used from the start,
and the options `require_transport_security` and `enable_tls` are ignored.
It is recommended to enable this if supported by your mail server.
* `require_transport_security`: Set to true to require TLS transport security for SMTP.
By default, Synapse will connect over plain text, and will then switch to
TLS via STARTTLS *if the SMTP server supports it*. If this option is set,
Expand Down Expand Up @@ -3217,6 +3221,7 @@ email:
smtp_port: 587
smtp_user: "exampleusername"
smtp_pass: "examplepassword"
implicit_tls: true
require_transport_security: true
enable_tls: false
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
Expand Down
5 changes: 4 additions & 1 deletion synapse/config/emailconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
if email_config is None:
email_config = {}

self.implicit_tls = email_config.get("implicit_tls", False)
self.email_smtp_host = email_config.get("smtp_host", "localhost")
self.email_smtp_port = email_config.get("smtp_port", 25)
self.email_smtp_port = email_config.get(
"smtp_port", 465 if self.implicit_tls else 25
)
self.email_smtp_user = email_config.get("smtp_user", None)
self.email_smtp_pass = email_config.get("smtp_pass", None)
self.require_transport_security = email_config.get(
Expand Down
36 changes: 26 additions & 10 deletions synapse/handlers/send_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@

import twisted
from twisted.internet.defer import Deferred
from twisted.internet.interfaces import IOpenSSLContextFactory, IReactorTCP
from twisted.internet.interfaces import IOpenSSLContextFactory
from twisted.internet.ssl import optionsForClientTLS
from twisted.mail.smtp import ESMTPSender, ESMTPSenderFactory

from synapse.logging.context import make_deferred_yieldable
from synapse.types import ISynapseReactor

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand All @@ -48,7 +50,7 @@ def _getContextFactory(self) -> Optional[IOpenSSLContextFactory]:


async def _sendmail(
reactor: IReactorTCP,
reactor: ISynapseReactor,
smtphost: str,
smtpport: int,
from_addr: str,
Expand All @@ -59,6 +61,7 @@ async def _sendmail(
require_auth: bool = False,
require_tls: bool = False,
enable_tls: bool = True,
implicit_tls: bool = False,
) -> None:
"""A simple wrapper around ESMTPSenderFactory, to allow substitution in tests
Expand All @@ -73,8 +76,9 @@ async def _sendmail(
password: password to give when authenticating
require_auth: if auth is not offered, fail the request
require_tls: if TLS is not offered, fail the reqest
enable_tls: True to enable TLS. If this is False and require_tls is True,
enable_tls: True to enable STARTTLS. If this is False and require_tls is True,
the request will fail.
implicit_tls: True to enable Implicit TLS.
"""
msg = BytesIO(msg_bytes)
d: "Deferred[object]" = Deferred()
Expand Down Expand Up @@ -105,13 +109,23 @@ def build_sender_factory(**kwargs: Any) -> ESMTPSenderFactory:
# set to enable TLS.
factory = build_sender_factory(hostname=smtphost if enable_tls else None)

reactor.connectTCP(
smtphost,
smtpport,
factory,
timeout=30,
bindAddress=None,
)
if implicit_tls:
reactor.connectSSL(
smtphost,
smtpport,
factory,
optionsForClientTLS(smtphost),
timeout=30,
bindAddress=None,
)
else:
reactor.connectTCP(
smtphost,
smtpport,
factory,
timeout=30,
bindAddress=None,
)

await make_deferred_yieldable(d)

Expand All @@ -132,6 +146,7 @@ def __init__(self, hs: "HomeServer"):
self._smtp_pass = passwd.encode("utf-8") if passwd is not None else None
self._require_transport_security = hs.config.email.require_transport_security
self._enable_tls = hs.config.email.enable_smtp_tls
self._implicit_tls = hs.config.email.implicit_tls

self._sendmail = _sendmail

Expand Down Expand Up @@ -189,4 +204,5 @@ async def send_email(
require_auth=self._smtp_user is not None,
require_tls=self._require_transport_security,
enable_tls=self._enable_tls,
implicit_tls=self._implicit_tls,
)

0 comments on commit f2b0002

Please sign in to comment.