土豆不好吃

为什么我的容器连不上riseup的邮件服务器了?

文章目录[显示]
这篇文章在 2023年03月15日08:03:00 更新了哦~

几天前,我开放了人人影视下载站的用户注册功能,并且新用户在注册后需要验证邮箱才能发表评论。开放注册可能会提高用户的活跃度,但是又总有用户发一些不合适的东西,因此有必要提高一点注册门槛。

后端的接口大概在半年前就写好了,我最近才学了一点点 React做好了邮件验证的功能。

如何用发邮件

我用的是riseup来发,基本如下几步:

  1. 去域名注册商那边添加一个email forward,因为后需要验证
  2. 去riseup添加一个alias
  3. 去DNS那边添加一个SPF 记录v=spf1 include:riseup.net +all,如果同时还想用email forward,那就添加 include就好了,比如说我的是这样的v=spf1 include:_spf.mx.cloudflare.net include:riseup.net +all
  4. 用SMTP协议,可以先用mailhog做测试,确定没问题来之后切到riseup,代码大概这样
import smtplib
from email.header import Header
from email.mime.text import MIMEText
from email.utils import formataddr, parseaddr


def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))


def send_mail(to: str, subject: str, body: str):
    user = "username"
    password = "password"
    host = "mail.riseup.net"
    port = 465
    from_addr = "yyets@dmesg.app"

    msg = MIMEText(body, 'html', 'utf-8')
    msg['From'] = _format_addr('YYeTs <%s>' % from_addr)
    msg['To'] = _format_addr(to)
    msg['Subject'] = Header(subject, 'utf-8').encode()

    if port == "1025":
        server = smtplib.SMTP(host, int(port))
    else:

        server = smtplib.SMTP_SSL(host, int(port))
    server.login(user, password)
    server.sendmail(from_addr, [to], msg.as_string())
    server.quit()


if __name__ == '__main__':
    send_mail("benny.think@gmail.com", "test subject", "test body")

收到的邮件也比较理想,一般来说不会进spam

问题

前几天做测试的时候一切顺利,上线的时候本来以为没什么大问题,结果今天却收到了一个用户的反馈,说收不到邮件,试了各种邮箱,检查了垃圾箱,也没有收到。

排查问题

加了几条log之后发现在容器中 smtplib.SMTP_SSL 会直接卡住,如果按ctrl + c会收到这样的报错

Traceback(most recent call last):
  File "", line 1, in 
  File "/YYeTsBot/yyetsweb/utils.py", line 51, in send_mail
    server = smtplib.SMTP_SSL(host, int(port))
  File "/usr/local/lib/python3.10/smtplib.py", line 1050, in __init__
    SMTP.__init__(self, host, port, local_hostname, timeout,
  File "/usr/local/lib/python3.10/smtplib.py", line 255, in __init__
    (code, msg)=self.connect(host, port)
  File "/usr/local/lib/python3.10/smtplib.py", line 341, in connect
    self.sock=self._get_socket(host, port, self.timeout)
  File "/usr/local/lib/python3.10/smtplib.py", line 1057, in _get_socket
    new_socket=self.context.wrap_socket(new_socket,
  File "/usr/local/lib/python3.10/ssl.py", line 513, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/local/lib/python3.10/ssl.py", line 1071, in _create
    self.do_handshake()
  File "/usr/local/lib/python3.10/ssl.py", line 1342, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLZeroReturnError: TLS/SSL connection has been closed(EOF)(_ssl.c: 997)

奇怪哦,那就排除法做几个测试,首先在容器内nc一下看看

/ # nc -v mail.riseup.net 465
mail.riseup.net (198.252.153.21:465) open

通的耶!那容器内连试试?

[root:~]# docker run --rm -it python:alpine sh
/ # python
Python 3.11.1 (main, Feb 4 2023, 01:59:37) [GCC 12.2.1 20220924] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import smtplib
>>> smtplib.SMTP_SSL("mail.riseup.net",465)

果不其然,卡住了。去宿主机试试看呢?

[root:~]# python
Python 3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import smtplib
>>> smtplib.SMTP_SSL("mail.riseup.net",465)
<smtplib.SMTP_SSL object at 0xffffa1148610>
>>>

没问题哎。看着上面有TLS/SSL connection has been closed(EOF),难道是OpenSSL的问题,试试 python而不是python:alpine 呢?

也不行。

是因为arm64有bug吗?但是我的电脑没问题啊🤔找了一台x86_64的试了一下也没问题。

这么排除下来,问题就是容器的网络了。默认来说docker的默认网络上bridge模式。Bridge是通过iptables去做转发的,之前在《我的ufw怎么又不好用了?使用docker时如何拒绝非cloudflare访问》中提到过。

切换到host模式试试看?

[root:~]# docker run --rm --network host -it python:alpine sh
/ # python
Python 3.11.1 (main, Feb 4 2023, 01:59:37) [GCC 12.2.1 20220924] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import smtplib
>>> smtplib.SMTP_SSL("mail.riseup.net",465)
<smtplib.SMTP_SSL object at 0xffff96122ed0>
>>>

果然,那为什么bridge模式不行host就可以呢?

看了一眼iptables,突然看到这么一条

Chain PREROUTING (policy ACCEPT)
target prot opt source destination

REDIRECT tcp -- anywhere anywhere tcp dpts:telnet:finger redir ports 16698
REDIRECT tcp -- anywhere anywhere tcp dpts:81:442 redir ports 16698
REDIRECT tcp -- anywhere anywhere tcp dpts:snpp:1000 redir ports 16698
DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL

这不是之前为了方便梯子使用,把23-1000中除了80和443的端口都给转发到本地的16698了吗?😓

删掉这条规则就好了。

其他的发邮件的办法

比如说自建,mailgun,sendgrid之类的,我个人觉得sendgrid比较不错,免费用户每天可以发100封。另外一个mailersend一个月可以发12000。同样都可以用SMTP协议手搓邮件。

人人影视分享站运行状况分享

人人影视分享站大概2021年2月初开始上线,6月的时候在 zuiyu的帮助下用React重做了前端,已经跑了整整两年啦。

两年里的收获

未来一段时间想要做的事情

 

欢迎给个star或者点个广告~

https://yyets.dmesg.app/

https://github.com/tgbot-collection/YYeTsBot

 


文章版权归原作者所有丨本站默认采用CC-BY-NC-SA 4.0协议进行授权|
转载必须包含本声明,并以超链接形式注明原作者和本文原始地址:
https://dmesg.app/container-smtp.html
退出移动版