Closed mariobriggs closed 2 years ago
Have you tried this with any native gunicorn workers? I ask because here it is uvicorn that is in the traceback and causing the error, not gunicorn.
thanks @jamadden there are a couple of things i tried
1 - With uvicorn only, but select only TLSv1.2 ciphers available to python. This worked perfectly end-2-end. So i was curious how i specify TLSv1.3 to gunicorn and then how gunicorn passes that to uvicorn.
2 - Ran uvicorn directly now, and got the same error ssl.SSLError: ('No cipher can be selected.',)
. So will ask the uvicorn git
export CIPHERS1_3=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256
uvicorn xxx.yyy:app --ssl-ciphers=$CIPHERS1_3 --ssl-keyfile=/tmp/PROD_SELF_SIGNED_PRIVATEKEY --ssl-certfile=/tmp/PROD_SELF_SIGNED_CERTIFICATE --ssl-version=2
Traceback (most recent call last):
File "/usr/local/bin/uvicorn", line 8, in <module>
sys.exit(main())
File "/usr/local/lib/python3.8/site-packages/click/core.py", line 829, in __call__
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.8/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/uvicorn/main.py", line 362, in main
run(**kwargs)
File "/usr/local/lib/python3.8/site-packages/uvicorn/main.py", line 386, in run
server.run()
File "/usr/local/lib/python3.8/site-packages/uvicorn/server.py", line 48, in run
loop.run_until_complete(self.serve(sockets=sockets))
File "uvloop/loop.pyx", line 1494, in uvloop.loop.Loop.run_until_complete
File "/usr/local/lib/python3.8/site-packages/uvicorn/server.py", line 55, in serve
config.load()
File "/usr/local/lib/python3.8/site-packages/uvicorn/config.py", line 273, in load
self.ssl = create_ssl_context(
File "/usr/local/lib/python3.8/site-packages/uvicorn/config.py", line 114, in create_ssl_context
ctx.set_ciphers(ciphers)
ssl.SSLError: ('No cipher can be selected.',)
@jamadden I reopened this. As apart from the uvicorn issue and you specifically asked Have you tried this with any native gunicorn workers?
i tried with sync
, it starts up properly
gunicorn -k sync --bind=0.0.0.0:5004 --workers=1 --certfile=/tmp/PROD_SELF_SIGNED_CERTIFICATE --keyfile=/tmp/PROD_SELF_SIGNED_PRIVATEKEY --ssl-version=TLSv1_2 --ciphers=$CIPHERS1_3 monitor.api:app
[2021-05-09 07:36:39 +0000] [54] [INFO] Starting gunicorn 20.0.4
[2021-05-09 07:36:39 +0000] [54] [INFO] Listening at: https://0.0.0.0:5004 (54)
[2021-05-09 07:36:39 +0000] [54] [INFO] Using worker: sync
[2021-05-09 07:36:39 +0000] [56] [INFO] Booting worker with pid: 56
And then testing via curl gives a wrong record version #
curl -k -v https://localhost:5004/dp/monitor/v1/liveness
* Trying ::1...
* TCP_NODELAY set
* connect to ::1 port 5004 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 5004 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* error:1408F10B:SSL routines:ssl3_get_record:wrong version number
curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
Which i believe is the server responding to the client's (TLSv1.3) hello that version ssl3 is not supported
with gthread worker, server starts up fine, but runs into no cipher can be selected
, when serving a request
curl -k -v https://localhost:8080/test
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:8080
* Closing connection 0
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to localhost:8080
class StandaloneApplication(gunicorn.app.base.BaseApplication):
...
if __name__ == '__main__':
options = {
'bind': '%s:%s' % ('127.0.0.1', '8080'),
'certfile': './server.crt',
'keyfile': '/.server.key',
'workers': 2,
'ssl-version': 'TLSv1_2',
'ciphers': 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256',
'worker_class': 'gthread',
}
StandaloneApplication(handler_app, options).run()
[2021-05-10 09:54:24 +0530] [26234] [INFO] Starting gunicorn 20.0.4
[2021-05-10 09:54:24 +0530] [26234] [INFO] Listening at: https://127.0.0.1:8080 (26234)
[2021-05-10 09:54:24 +0530] [26234] [INFO] Using worker: gthread
[2021-05-10 09:54:24 +0530] [26237] [INFO] Booting worker with pid: 26237
[2021-05-10 09:54:24 +0530] [26238] [INFO] Booting worker with pid: 26238
[2021-05-10 09:54:47 +0530] [26237] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
worker.init_process()
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 92, in init_process
super().init_process()
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/base.py", line 140, in init_process
self.run()
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 203, in run
callback(key.fileobj)
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 124, in accept
self.enqueue_req(conn)
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 112, in enqueue_req
conn.init()
File "/Users/mariobriggs/work/NLQ/git/venv-37/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 53, in init
**self.cfg.ssl_options)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 1234, in wrap_socket
context.set_ciphers(ciphers)
ssl.SSLError: ('No cipher can be selected.',)
[2021-05-10 09:54:47 +0530] [26237] [INFO] Worker exiting (pid: 26237)
Firstly, there are some limitations on exactly what can be achieved currently in terms of allowed protocols and ciphersuites, and those limitations are a mixture of Python limitations and Gunicorn limitations.
3.10
) doesn't appear to allow restriction, or negotiation-order of the cipher suites offered under TLS 1.3. This is documented and discussed at https://bugs.python.org/issue36484 "Can't reorder TLS 1.3 ciphersuites", but IIUC it is more than ordering - you can't filter the suites either.
I don't have plans to implement cipher suite selection for TLS 1.3 any time soon, maybe not at all.
20.1.0
is still using the deprecated wrap_socket
to make the SSL socket - this is scheduled to be replaced by access to the SSLContext
, hopefully via #2649 . Until that is complete, it probably isn't possible to offer TLS 1.3 only - TLS 1.2 would remain as a lower-priority protocol that a client could negotiate. Strict TLS 1.3 might be possible once we have access to the SSLContext
and can set ssl.OP_NO_TLSv1_2
, but this has not been tested.What is possible with current Python 3.9
and Gunicorn 20.1.0
is:
--ciphers
So for example:
gunicorn --certfile cert.pem --key key.pem --ssl-version TLS_SERVER --ciphers AES256-GCM-SHA384:AES128-GCM-SHA256 -b 0.0.0.0:8000 --workers 1 flaskt:app
Would result in:
SSLv2 not offered (OK)
SSLv3 not offered (OK)
TLS 1 not offered
TLS 1.1 not offered
TLS 1.2 offered (OK)
TLS 1.3 offered (OK): final
NPN/SPDY not offered
ALPN/HTTP2 not offered
TLSv1.2 (server order)
x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384
x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256
TLSv1.3 (server order)
x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384
x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256
x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256
Notice that the TLS 1.2 cipher suites are filtered, but the TLS 1.3 ones are not.
I propose closing this now and picking-up additional functionality via or after #2649.
no longer needed. Figured out issues was difference between standard names vs openssl names for the ciphers - https://www.mail-archive.com/openssl-users@openssl.org/msg89457.html
@javabrett I have been messing my head around on how to add support for TLSv1.3 with gunicorn 20.1.0 and python 3.9 but seems like this is a blocker and I can't get this working. The example you mentioned above which promises to offer TLSv1.3 shows not offered for me. I have created a sample flask app and ran gunicorn with your config:
gunicorn --certfile cert.pem --key key.pem --ssl-version TLS_SERVER --ciphers AES256-GCM-SHA384:AES128-GCM-SHA256 -b 0.0.0.0:8000 --workers 1 flaskt:app
Result:
Looking for some inputs here. Thanks.
There are a set if tlsv1_3 ciphers available in my python env that i would like to use with gunicorn.
But the
gunicorn --ssl-version=
does not me to specify tlsv1_3repro : Retrieve tlsv1_3 ciphers available to python ssl module
output
Try to use the 1_3 cipherlist with gunicorn
I believe this is because i have set
--ssl-version=TLSv1_2
. I am not finding in the docs, how to set TLSV1_3The above code works fine, when i extract the TLSv1_2 from the python ssl module