healthchecks / healthchecks

Open-source cron job and background task monitoring service, written in Python & Django

Home Page:https://healthchecks.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to send email notification with protonmail bridge

nmonserrato opened this issue · comments

Hey there!
I'm running healthchecks on a TrueNas Scale instance (docker image / k3s), and trying to configure it so that it uses my local protonmail bridge to send the email notifications.

I'm quite sure that the bridge works fine because I also use it for Truenas alerts, and they work ok.

The bridge unfortunately uses a self-signed certificate, and from the pod logs it seems clear that this is the issue:

2024-03-03 20:58:57.737783+01:00Exception in thread Thread-1:
2024-03-03 20:58:57.737833+01:00Traceback (most recent call last):
2024-03-03 20:58:57.737842+01:00File "/usr/lib/python3.11/threading.py", line 1045, in _bootstrap_inner
2024-03-03 20:58:57.738401+01:00self.run()
2024-03-03 20:58:57.738427+01:00File "/app/healthchecks/hc/lib/emails.py", line 26, in run
2024-03-03 20:58:57.738526+01:00self.message.send()
2024-03-03 20:58:57.738536+01:00File "/lsiopy/lib/python3.11/site-packages/django/core/mail/message.py", line 300, in send
2024-03-03 20:58:57.738684+01:00return self.get_connection(fail_silently).send_messages([self])
2024-03-03 20:58:57.738728+01:00^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-03-03 20:58:57.738737+01:00File "/lsiopy/lib/python3.11/site-packages/django/core/mail/backends/smtp.py", line 128, in send_messages
2024-03-03 20:58:57.738834+01:00new_conn_created = self.open()
2024-03-03 20:58:57.738867+01:00^^^^^^^^^^^
2024-03-03 20:58:57.738884+01:00File "/lsiopy/lib/python3.11/site-packages/django/core/mail/backends/smtp.py", line 93, in open
2024-03-03 20:58:57.738954+01:00self.connection.starttls(context=self.ssl_context)
2024-03-03 20:58:57.739000+01:00File "/usr/lib/python3.11/smtplib.py", line 790, in starttls
2024-03-03 20:58:57.739257+01:00self.sock = context.wrap_socket(self.sock,
2024-03-03 20:58:57.739280+01:00^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-03-03 20:58:57.739291+01:00File "/usr/lib/python3.11/ssl.py", line 517, in wrap_socket
2024-03-03 20:58:57.739506+01:00return self.sslsocket_class._create(
2024-03-03 20:58:57.739515+01:00^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-03-03 20:58:57.739527+01:00File "/usr/lib/python3.11/ssl.py", line 1104, in _create
2024-03-03 20:58:57.739859+01:00self.do_handshake()
2024-03-03 20:58:57.739896+01:00File "/usr/lib/python3.11/ssl.py", line 1382, in do_handshake
2024-03-03 20:58:57.740281+01:00self._sslobj.do_handshake()
2024-03-03 20:58:57.740319+01:00ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1006)
2024-03-03 20:58:57.781532+01:00[pid: 218|app: 0|req: 2/2] 10.20.114.4 () {44 vars in 1309 bytes} [Sun Mar  3 19:58:57 2024] POST /integrations/45697a7d-2b98-47a8-89e1-e4c7d047706b/test/ => generated 0 bytes in 205 msecs (HTTP/1.1 302) 9 headers in 473 bytes (1 switches on core 0)
2024-03-03 20:58:57.945799+01:00[pid: 218|app: 0|req: 3/3] 10.20.114.4 () {38 vars in 1340 bytes} [Sun Mar  3 19:58:57 2024] GET /projects/b7bce1e7-843a-4508-b52c-cc0af8d46bca/integrations/ => generated 22977 bytes in 20 msecs (HTTP/1.1 200) 9 headers in 457 bytes (1 switches on core 0)

Is there a way to add this certificate as trusted, or skip the validation?

Thanks in advance for the help and for the great tool!

From a quick look at Django email-related configuration options, it doesn't look like there's a simple way to turn the validation off.

A couple ideas:

  1. In Protonmail's docs it shows a Security field:

image

Perhaps it's possible to configure the bridge to require no TLS/SSL encryption? i.e., Security: None?

  1. Django's email backends are pluggable. One could subclass the default backend (backends.smtp.EmailBackend) and turn off certificate validation in the subclass. There's this bit in the code:
    @cached_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)
            return ssl_context
        else:
            return ssl.create_default_context()

I have not tested this, but perhaps something like this could work:

    @cached_property
    def ssl_context(self):
        ssl_context = ssl.create_default_context()
        ssl_context.verify_mode = ssl.CERT_NONE
        return ssl_context

If it does work, the easiest way to get this change in production would be to fork the Healthchecks repository, and put the customized EmailBackend in the fork. Then build custom Docker images off it.

You could also suggest a new configuration option to the Django project, for controlling the certificate validation mode. There's understandable reluctance to add new settings parameters, but may be worth a shot.

thanks a lot for the answer! Unfortunately those fields in Protonmail are read-only, nothing configurable.

Not a python developer here, but I'll try to do what you suggest and test locally, then raise a PR if it works.

Thanks again!