Gunicorn: Add support for named constants in the --ssl-version flag

Created on 15 Sep 2015  ·  37Comments  ·  Source: benoitc/gunicorn

I'm using gunicorn with ssl cert and key. It provides TLS connection 1.0. Is it possible to configure gunicorn to use TLS 1.2?

Improvement Documentation help wanted FeaturSSL - Mailing List -

All 37 comments

Hi @rrajaravi, did you try setting http://docs.gunicorn.org/en/latest/settings.html#ciphers ?

bump

How to determine it work or not?

can we close this issue?

TL;DR
Gunicorn only appears to support:
TLSv1
SSLv2

And does not support:
SSLv3
SSLv23
TLSv1_1
TLSv1_2

@berkerpeksag The docs at http://docs.gunicorn.org/en/latest/settings.html#ciphers do not appear to be correct.
I found the protocols in ssl.py. None of them except, TLSv1 and SSLv2, work when provided at a command line option on Ubuntu 14.04 LTS, Python 3.5.1, OpenSSL 1.0.1f 6 Jan 2014

The rest fail with error:
[2016-03-22 08:51:49 +0000] [2924] [ERROR] Exception in worker process:
Traceback (most recent call last):
File "/home/me/Envs/myproject/lib/python3.5/site-packages/gunicorn/workers/sync.py", line 126, in handle
self.cfg.ssl_options)
File "/usr/local/lib/python3.5/ssl.py", line 1064, in wrap_socket
ciphers=ciphers)
File "/usr/local/lib/python3.5/ssl.py", line 690, in **init

self._context.set_ciphers(ciphers)
ssl.SSLError: ('No cipher can be selected.',)


/usr/local/lib/python3.5/ssl.py
The following constants identify various SSL protocol variants:

PROTOCOL_SSLv2
PROTOCOL_SSLv3
PROTOCOL_SSLv23
PROTOCOL_TLSv1
PROTOCOL_TLSv1_1
PROTOCOL_TLSv1_2

Thanks for triaging this, @edwardotis. I'm reopening this to clarify documentation.

@berkerpeksag also are we using the good default?

TLSv1 looks fine for older Python versions, but perhaps we can change the default value to SSLv23 to use best available protocol (both for clients and servers).

Also, I think we should raise an exception if SSLv2 or SSLv3 is used in Gunicorn 20 (it would be great to deprecate them in the next 19.x release).

@berkerpeksag :+1:

Has anyone implemented these changes? If not, i'd be happy to give it a shot & open a PR.. Being able to set the cipher to something > TLSv1.0 is important to us and from what we can see any version of TLS greater than 1 can't be set.. it just defaults back to 1.0 (We tried the ones listed in docs & the constants listed here to no avail) -- just outputs No cipher can be selected

@AndrewJHart that would be great! :) Thanks!

It's possible to use other versions. You just need to specify a constant to --ssl-version on the command line. These constants come from the ssl stdlib module.

$ python -c "import ssl; print(ssl.PROTOCOL_SSLv23)"     
  2
$ python -c "import ssl; print(ssl.PROTOCOL_TLSv1_2)"
  5

Just specify --ssl-version 2 or --ssl-version 5 and you should have TLS 1.2 cipher support.

@AndrewJHart take a look at #1440 if you would like to contribute some improvements to SSL. We should switch to using SSLContext for better control over the context, such as setting server cipher order preference. I'll post a diff over on that issue momentarily with an example of starting to implement this for the sync worker. Any help with improving SSL support would be greatly appreciated.

Also, a simple pull request to change the default value in gunicorn/config.py to use SSLv23 constant instead of TLSv1 would be great.

Sorry, I meant to say #1140

I don't understand why this issue was re-opened. I have tested Gunicorn with --ssl-version 2 and found that TLS 1.2 ciphers work fine.

I will close, but please re-open and explain if you think I have done so in error.

I opened #1249 to track changing the default.

I reopened it because:

a) Users should be able to pass 'SSLv23' or 'TLSv1_2' instead of 2, or 5.
b) We should document available options instead of redirecting them to Python docs. https://docs.python.org/3.5/library/ssl.html is one of the longest documents in the stdlib and users shouldn't spend too much time just to see what options are available.

Re-opened, adding the documentation labels.

let me know what's needed here, if we can close it before a 19.5 release (ie before wed) would be cool :)

This is open still to provide only support for named constants in the
--ssl-version flag.

On Mon, May 2, 2016, 05:43 Benoit Chesneau [email protected] wrote:

let me know what's needed here, if we can close it before a 19.5 release
(ie before wed) would be cool :)


You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
https://github.com/benoitc/gunicorn/issues/1114#issuecomment-216225302

let's forget the 3.x version for now. What are the constant needed to add to support it on all version of python?

Is it possible to Specific different versions of TLS to enable or disable? For example, it is common to want to be able to disable or enable older versions. It is not clear to me from the documentation if "--ssl-version" can be used to disable older versions or if it can be used more than once (e.g. disable SSL3 and disable TLS1.0).

I think that would require switching to use SSLContext and adding a new option --ssl-options (we will probably just use ssl.create_default_context() so SSLv3 and TLS 1.0 will be disabled by default) Since there are too many combinations I don't think --ssl-version would accept named constant but users can still put it in a Gunicorn configuration file:

ssl_options = ssl.OP_NO_COMPRESSION | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3

Use of PROTOCOL_* to specify both the protocol _and exact version_ seems to be increasing deprecated. Assuming ssl_options is not currently implemented, I raised #1680.

1890 fixed it.

Perhaps someone may have some guidance for me? I have been struggling with this for a while now. Here's my environment:

Python 3.4.3
gunicorn==19.9.0
pyOpenSSL==18.0.0
cryptography==2.4.2

I'm also using the gunicorn flag --ssl-version 5

My server seems to be serving up TLS 1.2, but it allows a ton of insecure cipher suites. How can I limit the cipher suites? I tried the --ciphers flag, but had no luck with that.

Thanks!

--ssl-version 5 --ciphers ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256
work just fine:

~# openssl s_client -connect localhost:443 -cipher ECDHE-RSA-AES256-GCM-SHA384
CONNECTED(00000003)
depth=3...

~# openssl s_client -connect localhost:443 -cipher DHE-RSA-AES256-GCM-SHA384
CONNECTED(00000003)
140497569816640:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1407:SSL alert number 40

Thanks @lemrouch. Not sure why that didn't work for me before. It's working for me now!

How to use TLS 1.2 instead of TLS1.0 for gunicorn flask application? Could some please share the terminal command or code that has to be put in?

@rkbala This is what the relevant code looks like for my flask app:

...
from ssl import SSLContext, PROTOCOL_TLSv1_2, OP_NO_SSLv3, OP_NO_TLSv1, OP_NO_TLSv1_1
...
ssl = SSLContext(PROTOCOL_TLSv1_2)
ssl.set_ciphers('ECDH+AES256:ECDH+AES128:' + 
                '!aNULL:!eNULL:!MD5:!DSS:!RC4:!SSLv2:!SSLv3:!TLSv1')
ssl.options |= OP_NO_SSLv3
ssl.options |= OP_NO_TLSv1
ssl.options |= OP_NO_TLSv1_1
ssl.load_cert_chain('/etc/letsencrypt/live/fullchain.pem',
                    '/etc/letsencrypt/live/privkey.pem')
...
if __name__ == '__main__':
    app.run(host='0.0.0.0', port='443', ssl_context=ssl)

@rkbala This is what the relevant code looks like for my flask app:

...
from ssl import SSLContext, PROTOCOL_TLSv1_2, OP_NO_SSLv3, OP_NO_TLSv1, OP_NO_TLSv1_1
...
ssl = SSLContext(PROTOCOL_TLSv1_2)
ssl.set_ciphers('ECDH+AES256:ECDH+AES128:' + 
                '!aNULL:!eNULL:!MD5:!DSS:!RC4:!SSLv2:!SSLv3:!TLSv1')
ssl.options |= OP_NO_SSLv3
ssl.options |= OP_NO_TLSv1
ssl.options |= OP_NO_TLSv1_1
ssl.load_cert_chain('/etc/letsencrypt/live/fullchain.pem',
                    '/etc/letsencrypt/live/privkey.pem')
...
if __name__ == '__main__':
    app.run(host='0.0.0.0', port='443', ssl_context=ssl)

@pixelrebel Thank you!! I will give it a try and let you know.

What does this ssl.options |=OP_NO_* means?

Can we change cipher? Or the above set_ciphers will Set random cipher?

@rkbala I can't remember why the bitwise-or operation was needed. It may not be necessary...worth a test.

You can change the cipher to whatever is compatible with TLS 1.2. In my case, my app provides an API for Slack to communicate with it. So I only needed to support the cipher Slackbot uses.

@rkbala IO should clarify I don't remember why I needed that syntax, but I believe at the time, I needed to explicitly specify the OP_NO_* options to make my app only use TLS1.2 and no lower versions.

@rkbala I can't remember why the bitwise-or operation was needed. It may not be necessary...worth a test.

You can change the cipher to whatever is compatible with TLS 1.2. In my case, my app provides an API for Slack to communicate with it. So I only needed to support the cipher Slackbot uses.

Ok thank you. I think gunicorn takes default python cipher if we do not specify one. Correct me if I am wrong.

Also does the above flask code makes gunicorn to start the flask app only in TLSv1.2? The question is because I want to run flask app via gunicorn. I just learned today that gunicorn terminal command “gunicorn —ssl-version TLSV1_2 project:app” will start flask app in tls v1.2. Which way is more efficient?

Yes, that does TLS 1.2 only. Everything else is deprecated AFAIK. I assume they have the same effect, but I'd guess using command line options overrides the options in the script. I'm not an expert with gunicorn by any means. So take my advice with a grain of salt.

Yes, that does TLS 1.2 only. Everything else is deprecated AFAIK. I assume they have the same effect, but I'd guess using command line options overrides the options in the script. I'm not an expert with gunicorn by any means. So take my advice with a grain of salt.

Thank you @pixelrebel I will explore on that part.

Was this page helpful?
0 / 5 - 0 ratings