I have a code in Python that accepts (E)SMTP requests via aiosmtp but since I pushed this code on Debian 10, I'm having a few errors that wasn't present before (and my code didn't changed):
[SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
SSL handshake failed protocol: transport: <_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()
and:
[SSL: KRB5_S_INIT] application data after close notify (_ssl.c:2609)
SSL error in data received protocol: transport: <_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()
I think these two issues are related.
Unfortunately the two stacktrace don't show anything related to my code, which makes it harder for me to better see where this is happening, and the exception isn't related to another exception (Python3).
Here's the versions of my package:
uname -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 --version: Python 3.7.3
pip freeze
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
I believe that if something was wrong with my code, I would have had this error on Debian 9 and earlier which I never had.
I searched on SO and Google about this error but didn't find anything. I suspect some issue on a specific version of a specific project (aiosmtpd, async or python) but don't have any clue.
I'm hoping you'll be able to help me :)
Update:
I've added tracking of ciphers in the communication. The shared ciphers are:
[[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]]
And the Cipher for the socket is: [ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256] which is in the shared ciphers.
Update 2
I can reproduce the error, but only under specific conditions.
On the new server, here's the code I run:
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()
It's a basic script that helps me reproduce the issue.
On the Old server, I run this code:
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', '[email protected]')
com(client, 'rcpt', '[email protected]')
com(client, 'quit')
print('All good !')
Which I call with :
sendmail.py {ip.of.new.server} 2125
On the old server (the one running the script), I get this error:
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()
What's super odd, is that if I copy the sendmail script on my local machine, and run it pointing to the new server, I don't have the error anymore!
(So the issue must be related to the old server? But why the new server shows the exception?!)
If I switch the scripts (testing sending an email from the new server to the old), it works...
opensslshould be able to connect to anything which tries to speak SSL. Or you could write a simple Python client which connects to the unencrypted connection and then takes you through theSTARTTLSnegotiation.