Gunicorn: Can't load any Diffie-Hellman ciphers.

Created on 14 Jun 2018  路  14Comments  路  Source: benoitc/gunicorn

When using SSL, no Diffie-Hellman are being loaded even though they are available.

Improvement help wanted FeaturSSL

Most helpful comment

@javabrett yea you have to pass a dh cert in order to use it so it would be best handled by a opt parameter from cmd line, i simply hard coded mine as i needed to get it going for a client.

ie if dh cert parameter in getopt... then load_dh_params().... etc..

I will try find some time to do a proper push request.

All 14 comments

openssl ciphers -V 'ALL' | grep -i TLSv1.2  | grep DHE | grep Kx=DH
0x00,0xA3 - DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=DSS  Enc=AESGCM(256) Mac=AEAD
0x00,0x9F - DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
0x00,0x6B - DHE-RSA-AES256-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA256
0x00,0x6A - DHE-DSS-AES256-SHA256   TLSv1.2 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA256
0x00,0xA2 - DHE-DSS-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=DSS  Enc=AESGCM(128) Mac=AEAD
0x00,0x9E - DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(128) Mac=AEAD
0x00,0x67 - DHE-RSA-AES128-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA256
0x00,0x40 - DHE-DSS-AES128-SHA256   TLSv1.2 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA256

Can you say more what you mean when you say they are not "being loaded"?

Well... lets say I load the ciphers

ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256

I only get.. when i check the loaded ciphers agaist gunicorn server listening on 443.

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (brainpoolP256r1) - A
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (brainpoolP256r1) - A
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (brainpoolP256r1) - A

DHE's are also listed in the HIGH constant of ciphers... and you will find that no DHE's load / are presented to the client as a cipher.

Are you connecting directly to Gunicorn? Or is there a PaaS you're deploying to?

Direct

The output you pasted looks like elliptic curve Diffie-Hellman is being offered (ECDHE):

| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (brainpoolP256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (brainpoolP256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (brainpoolP256r1) - A

Is that the end of the ssl-enum-ciphers output?

yup via nmap --script ssl-cert,ssl-enum-ciphers -p 443 <host> or you can use https://www.ssllabs.com/ssltest/

@tilgovi I managed to make this work , but I had to rewrite workers/sync.py and make the ssl.wrap_socket a SSLContext.wrap_socket , though this is test.. what I ended up doing was generating a dhparam via openssl and loading it via context.load_dh_params

    def handle(self, listener, client, addr):
        req = None
        try:
            if self.cfg.is_ssl:
                context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
                context.options |= ssl.OP_SINGLE_DH_USE
                context.set_ciphers(self.cfg.ssl_options.get('ciphers'))
                context.load_cert_chain(self.cfg.ssl_options.get('certfile'), keyfile=self.cfg.ssl_options.get('keyfile'))
                context.load_verify_locations(cafile=self.cfg.ssl_options.get('ca_certs'))
                context.load_dh_params('ssl_cert/dhparam.crt')
                client = context.wrap_socket(client, server_side=True, do_handshake_on_connect=False)

... but its something to work off..

scan results now come back with

TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 2048) - A
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (brainpoolP256r1) - A
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (brainpoolP256r1) - A
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (brainpoolP256r1) - A

Rather than create a dhparam (which you isnt required for nginx or apache) just generate a dynamic one on startup?

Reading futher into python ssl library, I don't think the auto generate will be possible... anyway may need to incorporate my changes with a dhparam-cert or something, so people can enable / use DH ciphers.

This is what I get now via the following ciphers with my patch

TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (dh 2048)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 2048)
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (dh 2048)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (brainpoolP256r1)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (brainpoolP256r1)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (brainpoolP256r1)
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (brainpoolP256r1)

Via

--ciphers="EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:DHE+RSA+AES"

We have #1140 and #1680 that could be related. If you can help in any way that would be very much appreciated.

@VeNoMouS I think you have already found the limitation, that load_dh_params() must be called in-order for the DH ciphers to be considered in Python. At least I think that is the case. It is backed-up by this note in the issue when the cipher was made available:

In order to enable DH ciphers the SSL implementation the in the file Modules/_ssl.c, it must issue a DH_generate_parameters() if a cipher is DH.

Since that method is on the context, and ssl.wrap_socket is deprecated (underneath it just delegates to SSLContext, then your patch seems correct. Additional config options would be require to pass the DH params.

@javabrett yea you have to pass a dh cert in order to use it so it would be best handled by a opt parameter from cmd line, i simply hard coded mine as i needed to get it going for a client.

ie if dh cert parameter in getopt... then load_dh_params().... etc..

I will try find some time to do a proper push request.

Was this page helpful?
0 / 5 - 0 ratings