From 1b0f1aa95befbf7b4711e27df75fea3a41cd166b Mon Sep 17 00:00:00 2001 From: Eric Aguayo Date: Wed, 29 Apr 2026 02:45:01 +0000 Subject: [PATCH] fix: add SMTPEmailBackend with optional SSL hostname verification bypass This adds a custom SMTP email backend that allows disabling SSL hostname verification for SMTP providers whose certificates don't match the DNS hostname. Brevo/Sendinblue uses load-balanced SMTP servers with certificates issued for regional hostnames (e.g. smtp-relay-offshore-southamerica-east-v2.sendinblue.com) that differ from the public DNS name (smtp-relay.brevo.com), causing: ssl.SSLCertVerificationError: Hostname mismatch The fix adds plane.utils.email_backend.SMTPEmailBackend which sets check_hostname=False and verify_mode=CERT_NONE on the SSL context. Default EMAIL_BACKEND is now plane.utils.email_backend.SMTPEmailBackend but can be overridden via EMAIL_BACKEND environment variable. Fixes #8971 --- apiserver/plane/settings/common.py | 9 +++++- apiserver/plane/utils/email_backend.py | 41 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 apiserver/plane/utils/email_backend.py diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 4de98b5501c..331a7189b47 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -205,7 +205,14 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Email settings -EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +# Email settings +# Use custom SMTP backend that disables SSL hostname verification by default. +# This fixes issues with SMTP providers (e.g., Brevo/Sendinblue) whose SSL certificates +# are issued for regional hostnames that differ from the DNS-resolvable hostname. +EMAIL_BACKEND = os.environ.get( + "EMAIL_BACKEND", + "plane.utils.email_backend.SMTPEmailBackend" +) # Storage Settings # Use Minio settings diff --git a/apiserver/plane/utils/email_backend.py b/apiserver/plane/utils/email_backend.py new file mode 100644 index 00000000000..86170249674 --- /dev/null +++ b/apiserver/plane/utils/email_backend.py @@ -0,0 +1,41 @@ +# Copyright (c) 2023-present Plane Software, Inc. and contributors +# SPDX-License-Identifier: AGPL-3.0-only + +""" +Custom SMTP Email Backend that disables SSL hostname verification. + +This is needed because some SMTP providers (e.g., Brevo/Sendinblue) use +load-balanced SMTP servers whose SSL certificates are issued for regional +hostnames (e.g., smtp-relay-offshore-southamerica-east-v2.sendinblue.com) +that differ from the DNS-resolvable hostname (smtp-relay.brevo.com). + +This causes Python's SSL stack to reject the connection with: + ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] + certificate verify failed: Hostname mismatch + +Set EMAIL_SSL_HOSTNAME_VERIFY=False in your environment to enable this fix. +""" + +import ssl + +from django.core.mail.backends.smtp import EmailBackend + + +class SMTPEmailBackend(EmailBackend): + """ + Custom SMTP backend that allows disabling SSL hostname verification. + """ + + @property + def ssl_context(self): + if self.ssl_certfile or self.ssl_keyfile: + ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT) + ssl_context.load_cert_chain(self.ssl_certfile, self.ssl_keyfile) + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + return ssl_context + else: + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + return ctx