EOF 发生在 python:3.8-slim-buster 上违反协议 (_ssl.c:1125)

EOF occurred in violation of protocol (_ssl.c:1125) on python:3.8-slim-buster

我最近将 django api 从 2.2 更新到 3.1。我更新了 dockerfile 和相关的 bash 文件,就像 django-cookiecutter 所做的 https://github.com/pydanny/cookiecutter-django/commit/b22045bcd4ebf563ccdcf226fb389a6bb71e2654#diff-1872e6a6f0bbcb27f2eda185ac89eed05eb7a402b298e58bcbef29bf039c2c13

除了现在在生产环境中我们无法发送电子邮件外,升级大部分进行得很顺利。我在生产中有一个最小的管理命令 运行 来测试电子邮件设置

from django.conf import settings
from django.core.mail import send_mail
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    """
    Sends an email to the provided addresses.
    """

    help = "Sends an email to the provided addresses."

    def add_arguments(self, parser):
        parser.add_argument("emails", nargs="+")

    def handle(self, *args, **options):
        self.stdout.write(f"send_email from: {settings.EMAIL_FROM_FIELD}")
        for email in options["emails"]:
            try:
                send_mail(
                    "expert-system test email",
                    "a simple email",
                    settings.EMAIL_FROM_FIELD,
                    [email],
                )
            except BaseException as e:
                self.stdout.write(f"Problem sending email: {e}")

这个returns

$ python manage.py send_email harry@test.com
send_email from: test@test.com
Problem sending email: EOF occurred in violation of protocol (_ssl.c:1125)

另一个 Whosebug 建议测试是否支持 tls 1.1。

$ python -c "from urllib.request import urlopen ; 
print(urlopen('https://www.howsmyssl.com/a/check').read())"
b'{"given_cipher_suites":["TLS_AES_256_GCM_SHA384","TLS_CHACHA20_POLY1305_SHA256","TLS_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256","TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256","TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256","TLS_DHE_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_DHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_DHE_RSA_WITH_AES_128_CBC_SHA","TLS_RSA_WITH_AES_256_GCM_SHA384","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA256","TLS_RSA_WITH_AES_128_CBC_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_128_CBC_SHA","TLS_EMPTY_RENEGOTIATION_INFO_SCSV"],"ephemeral_keys_supported":true,"session_ticket_supported":true,"tls_compression_supported":false,"unknown_cipher_suite_supported":false,"beast_vuln":false,"able_to_detect_n_minus_one_splitting":false,"insecure_cipher_suites":{},"tls_version":"TLS 1.3","rating":"Probably Okay"}'

如何在制作时获取要发送的电子邮件?

简而言之,很可能您的邮件服务器仅支持 TLS 1.1 甚至仅支持 TLS 1.0,而 slim-buster 映像不再支持这些协议。

回到 3.7-alpine(这是已知的工作组合)或仍然支持这些协议的旧 ubuntu/debian 版本将允许您再次发送邮件。

那么你应该升级你的邮件服务器,因为 TLS 1.0 和 TLS 1.1 早就应该死了。


编辑:

另一种测试邮件服务器的方法是使用 openssl 的 s_client 命令:

openssl s_client -no_tls1 -no_tls1_1 -no_tls1_2 -connect your.mail.host:port

这很可能会失败。然后删除一个 -no_tls 标志直到它开始工作并且你知道它支持的最高协议。

Note: -no_tls1_2 is only supported on openssl versions that support TLSv1.3

解决方法是使用最初工作的 os python:3.7-alpine,它似乎能够发送电子邮件,但是 smtp 服务器需要它(仍然不确定它强制使用什么 tls 版本,但我假设它tls 1.2).

还想补充一下我们尝试使用 python:3.8-slim-buster 的原因是因为 cookiecutter 使用它,https://pythonspeed.com/articles/base-image-python-docker-images/ and the latest versions of the cryptography module (3.4+ https://cryptography.io/en/latest/changelog.html#v3-4 推荐)推荐使用现代版本的 pip(否则你必须安装 rust 来编译密码学)。 django-allauth 需要加密模块(通过 pyjwt)。

为了使用现代版本的 django-allauth(我认为这是 django 3.1 所要求的),我将加密模块固定到最新的 pre-rust

django-allauth==0.44.0  # https://github.com/pennersr/django-allauth
cryptography==3.3.2 # https://github.com/pyca/cryptography