python - Python 上的 SSL 问题
问题描述
我在 Python 中有一个通过 aiosmtp 接受 (E)SMTP 请求的代码,但是由于我在 Debian 10 上推送了此代码,因此我遇到了一些以前不存在的错误(而且我的代码没有改变):
[SSL: NO_SHARED_CIPHER] 没有共享密码 (_ssl.c:1056)
SSL 握手失败协议:传输:<_SelectorSocketTransport fd=11 read=polling write=>
SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
File "asyncio/sslproto.py", line 625, in _on_handshake_complete
raise handshake_exc
File "asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
和:
[SSL: KRB5_S_INIT] 关闭通知后的应用程序数据 (_ssl.c:2609)
数据接收协议中的 SSL 错误:传输:<_SelectorSocketTransport fd=15 read=polling write=>
SSLError: [SSL: KRB5_S_INIT] application data after close notify (_ssl.c:2609)
File "asyncio/sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "asyncio/sslproto.py", line 207, in feed_ssldata
self._sslobj.unwrap()
File "ssl.py", line 767, in unwrap
return self._sslobj.shutdown()
我认为这两个问题是相关的。
不幸的是,这两个堆栈跟踪没有显示与我的代码相关的任何内容,这使我更难更好地了解发生这种情况的位置,并且该异常与另一个异常(Python3)无关。
这是我的包的版本:
unname -a :Linux my-server 4.19.0-5-amd64 #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) x86_64 GNU/Linux
蟒蛇--版本:Python 3.7.3
点冻结
aiomysql==0.0.20
aiosmtpd==1.2
asn1crypto==0.24.0
atpublic==1.0
authres==1.2.0
beanstalkc3==0.4.0
blinker==1.4
certifi==2018.8.24
cffi==1.12.3
chardet==3.0.4
Click==7.0
cloud-init==18.3
configobj==5.0.6
cryptography==2.6.1
distro-info==0.21
dkimpy==0.9.4
dnspython==1.16.0
fail2ban==0.10.2
Flask==1.1.1
idna==2.6
itsdangerous==1.1.0
Jinja2==2.10.1
jsonpatch==1.21
jsonpointer==1.10
jsonschema==2.6.0
MarkupSafe==1.1.0
mysqlclient==1.4.4
oauthlib==2.1.0
psutil==5.6.3
py3dns==3.2.1
pycparser==2.19
PyGObject==3.30.4
pyinotify==0.9.6
PyJWT==1.7.0
PyMySQL==0.9.2
PyNaCl==1.3.0
pyspf==2.0.13
pysrs==1.0.3
python-apt==1.8.4
python-dotenv==0.10.3
PyYAML==3.13
requests==2.21.0
sentry-sdk==0.12.3
six==1.12.0
systemd-python==234
unattended-upgrades==0.1
urllib3==1.24.1
uWSGI==2.0.18
Werkzeug==0.16.0
我相信如果我的代码有问题,我会在 Debian 9 及更早版本上遇到这个错误,这是我从未遇到过的。
我在 SO 和 Google 上搜索了这个错误,但没有找到任何东西。我怀疑特定项目的特定版本(aiosmtpd、async 或 python)存在一些问题,但没有任何线索。
我希望你能帮助我:)
更新:
我在通信中添加了对密码的跟踪。共享密码是:
[[TLS_AES_256_GCM_SHA384, TLSv1.3, 256], [TLS_CHACHA20_POLY1305_SHA256, TLSv1.3, 256], [TLS_AES_128_GCM_SHA256, TLSv1.3, 128], [ECDHE-ECDSA-AES256-GCM-SHA384, TLSv1.2, 256], [ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256], [DHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256], [ECDHE-ECDSA-CHACHA20-POLY1305, TLSv1.2, 256], [ECDHE-RSA-CHACHA20-POLY1305, TLSv1.2, 256], [DHE-RSA-CHACHA20-POLY1305, TLSv1.2, 256], [ECDHE-ECDSA-AES128-GCM-SHA256, TLSv1.2, 128], [ECDHE-RSA-AES128-GCM-SHA256, TLSv1.2, 128]]
并且套接字的密码是:[ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256]
在共享密码中。
更新 2
我可以重现错误,但只能在特定条件下。
在新服务器上,这是我运行的代码:
import asyncio, logging, sys, signal, ssl
from aiosmtpd.controller import Controller
from aiosmtpd.handlers import Debugging
from aiosmtpd.smtp import SMTP
class ControllerTls(Controller):
def factory(self):
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('./certs/certificate.pem', './certs/id_rsa')
context.load_dh_params('./certs/dhparams.pem')
return SMTP(
self.handler,
tls_context=context
)
# Temporary outputing errors from mail.log
streamHandler = logging.StreamHandler(sys.stdout)
streamHandler.setFormatter(logging.Formatter('[%(asctime)-15s] (%(levelname)s) - %(message)s'))
streamHandler.setLevel(logging.INFO)
maillog = logging.getLogger('mail.log')
maillog.setLevel(logging.INFO)
maillog.addHandler(streamHandler)
controller = ControllerTls(Debugging(), hostname='0.0.0.0', port=2125)
controller.start()
print('Controller started!')
sig = signal.sigwait([signal.SIGINT, signal.SIGQUIT])
controller.stop()
这是一个帮助我重现问题的基本脚本。
在旧服务器上,我运行以下代码:
import smtplib, ssl, sys
port = 25
if len(sys.argv) == 3:
port = sys.argv[2]
def com(client, command, *args, **kwargs):
result = getattr(client, command)(*args, **kwargs)
if result[0] > 500:
print('[FATAL] - An error occured!')
print(result)
client.quit()
exit()
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('/var/www/towboat/certs/certificate.pem', '/var/www/towboat/certs/id_rsa')
context.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384')
client = smtplib.SMTP(sys.argv[1], port=port)
com(client, 'ehlo')
com(client, 'starttls', context=context)
com(client, 'ehlo')
com(client, 'mail', 'contact@improvmx.com')
com(client, 'rcpt', 'cyril@improvmx.com')
com(client, 'quit')
print('All good !')
我打电话给:
sendmail.py {ip.of.new.server} 2125
在旧服务器(运行脚本的服务器)上,我收到此错误:
Controller started!
[2019-10-08 15:57:11,878] (INFO) - Peer: ('ip.of.old.server', 45492)
[2019-10-08 15:57:11,878] (INFO) - ('ip.of.old.server', 45492) handling connection
[2019-10-08 15:57:11,880] (INFO) - ('ip.of.old.server', 45492) Data: b'ehlo {name old server}'
[2019-10-08 15:57:11,883] (INFO) - ('ip.of.old.server', 45492) Data: b'STARTTLS'
[2019-10-08 15:57:11,883] (INFO) - ('ip.of.old.server', 45492) STARTTLS
SSL handshake failed
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f04d33d7d30>
transport: <_SelectorSocketTransport fd=7 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/usr/lib/python3.7/asyncio/sslproto.py", line 625, in _on_handshake_complete
raise handshake_exc
File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f04d33d7d30>
transport: <_SelectorSocketTransport closing fd=7 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/usr/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
[2019-10-08 15:58:33,909] (INFO) - Connection lost during _handle_client()
超级奇怪的是,如果我在本地机器上复制 sendmail 脚本,然后将其指向新服务器运行,我就不再有错误了!
(所以问题肯定和老服务器有关?但是为什么新服务器显示异常?!)
如果我切换脚本(测试从新服务器向旧服务器发送电子邮件),它可以工作......
解决方案
我认为这是原因:
新服务器上的 v1.1.1d,旧服务器上的 1.1.0d
1.1.1行介绍了TLSv3和许多其他非常重要的更改 -请参阅更改日志。
正如我看到您在aiosmtpd github 上打开票证一样,您已经正确猜到您收到错误的原因是 aiosmtpd。原因是它支持您至少需要 Python 3.5,它不支持openssl 1.1.1。目前只有python 3.7(甚至还没有完全向后移植到python 3.6)支持openssl 1.1.1。
由于 aiosmtpd 的最新版本是1.2 (2018-09-01),因此可以假设(没有看到任何PR)他们尚未实施新的 openssl 1.1.1 [2018 年 9 月 11 日]是引入了重大变化。
除了为aiosmtpd提供 PR之外,您唯一的选择是将您的 openssl降级到1.1.0 行的最新版本,该行目前是1.1.0i。
推荐阅读
- docker - POD 的回应线索
- python - 翻转二进制字符串python3
- python - 从 .txt 将代理添加到 python 中的字典
- sql - Postgres键值表,选择值作为列
- python - 如何重定向到 Django 中的相同/当前页面?
- python - 如何在 concurrent.futures 的迭代器中确保每个 Future 的超时?
- python - 将球体参数方程乘以 NumPy,Python 中的矩阵
- python - 如何在 csv 文件中使用 python 中的 if 语句比较时间
- python - 如何在 Pygame 中测试碰撞?
- javascript - 如何随机制作 *ngFor 显示?