Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gunicorn 23 schannel: failed to receive handshake, SSL/TLS connection failed #3279

Open
juparker37 opened this issue Aug 16, 2024 · 4 comments

Comments

@juparker37
Copy link

I am trying to configure TLS support for my Gunicorn and Django app. Reviewing https://docs.gunicorn.org/en/stable/settings.html and configure the gunicorn.conf.py file or using the CLI for TLS cert/key/cacerts does not work.

I think the settings documentation could be improved showing what config file varaibles are needed and an example to get TLS 1.2 working and TLS 1.3 ssl_context working.

Is their an example out their to go by? My requirements are that traffic between the Nginx reverse proxy and Gunicorn use TLS. I have a 3rd party CA signed certificate, dir below.

Below is the curl trace and gunicorn.conf.py file.

#!/usr/bin/python
#  /home/djangoweb, gunicorn -c cloudmonitor/gunicorn.conf.py --error-logfile -,
#  tail -f /home/djangoweb/logs/httpd_error.log
import multiprocessing
#
wsgi_app = "cloudmonitor.wsgi:application"
bind = "192.168.46.69:9450"
daemon = 'True'
default_proc_name = 'gunicorn_httpd'
reload = 'True'
#
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 2
#
errorlog = '/home/djangoweb/logs/httpd_error.log'
accesslog = '/home/djangoweb/logs/httpd_access.log'
#
ssl_version = 'TLS_SERVER'
cert_reqs = "2"
certfile = '/home/djangoweb/certs/star.mydomain.com.pem'
keyfile = '/home/djangoweb/certs/private/star.mydomain.key'
cacert = '/home/djangoweb/certs/cacerts/DigiCertCA.crt'
def ssl_context(conf, default_ssl_context_factory):
    import ssl
    context = default_ssl_context_factory()
    context.minimum_version = ssl.TLSVersion.TLSv1_3
    return context
do_handshake_on_connect = 'true'
curl -v --trace - https://server01.mydomain.com:9450/dashboard
Warning: --trace overrides an earlier trace/verbose option
== Info: Host server01.mydomain.com:9450 was resolved.
== Info: IPv6: (none)
== Info: IPv4: 192.168.46.69
== Info:   Trying 192.168.46.69:9450...
== Info: Connected to inap-aws-cm01.mydomain.com (192.168.46.69) port 9450
== Info: schannel: disabled automatic use of client certificate
== Info: ALPN: curl offers http/1.1
== Info: schannel: failed to receive handshake, SSL/TLS connection failed
== Info: Closing connection
== Info: schannel: shutting down SSL/TLS connection with server01.mydomain.com port 9450
curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed
ls -la /home/djangoweb/certs/
total 24
drwxr-xr-x  4 django django 4096 Aug 16 00:21 .
drwx------ 13 django django 4096 Aug 16 01:07 ..
drwxr-xr-x  2 django django 4096 Aug 16 00:20 cacerts
drwxr-xr-x  2 django django 4096 Aug 16 00:22 private
-rw-r--r--  1 django django 7831 Aug 16 00:21 star.mydomain.com.pem
total 12
drwxr-xr-x 2 django django 4096 Aug 16 00:22 .
drwxr-xr-x 4 django django 4096 Aug 16 00:21 ..
-rw------- 1    600    600 3272 Aug 16 00:21 star.mydomain.com.key
ls -la /home/djangoweb/certs/cacerts/
total 40
drwxr-xr-x 2 django django  4096 Aug 16 00:20 .
drwxr-xr-x 4 django django  4096 Aug 16 00:21 ..
-rw-r--r-- 1 django django 30720 Aug 16 00:19 DigiCertCA.crt
@pajod
Copy link
Contributor

pajod commented Aug 16, 2024

Are you certain you meant to configure cert_reqs, your curl test is not using client auth. Also, reread the logs emitted by Gunicorn. If you have not seen the warning telling you ssl_version is deprecated and ignored, maybe you missed something else there?

@juparker37
Copy link
Author

Are you certain you meant to configure cert_reqs, your curl test is not using client auth. Also, reread the logs emitted by Gunicorn. If you have not seen the warning telling you ssl_version is deprecated and ignored, maybe you missed something else there?

I am trying to configure mTLS actually between Ngnix reverse proxy and Gunicorn. I assume the cert_reqs would be needed and both sides need the TLS CA certificate installed.

Yes, you are correct on the Curl command used. I changed it to just a Curl request without options. But when trying to hit the proxied "/dashboard/" via 9443 it has a 301 redirect but the TLS connection fails.

curl https://server01.mydomain.com:9443/dashboard
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.14.1</center>
</body>
</html>

curl https://server01.mydoamin.com:9450/dashboard
curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed

@juparker37
Copy link
Author

Are we assuming that Gunicorn does not support mTLS (mutual TLS) to secure the backend instead of terminating the TLS connection a Nginx and the plaintext talking to the app?

@juparker37
Copy link
Author

juparker37 commented Aug 16, 2024

When I go to https://192.168.46.69:9450/dashboard using Incognito directly and bypass proxy, the Gunicorn TLS config is still not working. Browser is still saying connection not secure.

I tried to comment out all lines in the gunicorn.conf.py and use the cli

gunicorn --certfile /home/djangoweb/certs/star.mydomain.com.pem --keyfile /home/djangoweb/certs/private/star.mydoamin.com.key -c cloudmonitor/gunicorn.conf.py --error-logfile -
2024/08/16 11:54:54 [error] 4169609#0: *1 peer closed connection in SSL handshake (104: Connection reset by peer) while SSL handshaking to upstream, client: 10.206.10.11, server: server01.oversightsystems.com, request: "GET /dashboard/ HTTP/1.1", upstream: "https://192.168.46.69:9450/dashboard/", host: "server01.mydomain:9443"

For sanity, I may try my Django app with uwsgi to see if I can reproduce the issue or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants