mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
handle ssl connections.
gunicorn is now supporting ssl connections:
$ gunicorn -w3 --certfile=server.crt --keyfile=server.key test:app
works with all supported workers
This commit is contained in:
parent
b7b51adf13
commit
1198cc2723
21
examples/server.crt
Normal file
21
examples/server.crt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDdDCCAlwCCQC3MfdcOMwt6DANBgkqhkiG9w0BAQUFADB8MQswCQYDVQQGEwJG
|
||||||
|
UjERMA8GA1UECBMIUGljYXJkaWUxDjAMBgNVBAcTBUNyZWlsMREwDwYDVQQKEwhn
|
||||||
|
dW5pY29ybjEVMBMGA1UEAxMMZ3VuaWNvcm4ub3JnMSAwHgYJKoZIhvcNAQkBFhF1
|
||||||
|
c2VyQGd1bmljb3JuLm9yZzAeFw0xMjEyMTQwODI2MDJaFw0xMzEyMTQwODI2MDJa
|
||||||
|
MHwxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhQaWNhcmRpZTEOMAwGA1UEBxMFQ3Jl
|
||||||
|
aWwxETAPBgNVBAoTCGd1bmljb3JuMRUwEwYDVQQDEwxndW5pY29ybi5vcmcxIDAe
|
||||||
|
BgkqhkiG9w0BCQEWEXVzZXJAZ3VuaWNvcm4ub3JnMIIBIjANBgkqhkiG9w0BAQEF
|
||||||
|
AAOCAQ8AMIIBCgKCAQEAy9RQSiGpB+HyjMpRCEfV9M/4g7gXq/qRizxDspJujoBz
|
||||||
|
SW0d4FqMHaSRX2QOA+euhtlOYTgsvWZcyv5cvDfL1CtrNWSVBrlo7wIy5tg60Z3A
|
||||||
|
JnWT/Zxj4WIqkPwdglB1sRBsI1Fn0o6nJu4HekZedXDK6fua4lOPfsQG84EhRQKS
|
||||||
|
Mz2o7Nesk8/UMjb+5WoRmG7mxrpe0/OYlnydqzqwHUQ+I5CHl1kOhePo9ZBTFMA5
|
||||||
|
Ece8kGQs37rFCEy92xCYHDgp+CjjyYbeBskF3o0/a88K2bt8J7uXkn4h14HjtFHq
|
||||||
|
fYnqn60cwyIx3T/uMUh6EmhKQezaw60xyIivmjH8tQIDAQABMA0GCSqGSIb3DQEB
|
||||||
|
BQUAA4IBAQAKu7kzTAqONFI1qC6mnwAixSd7ml6RtyQRiIWjg4FyTJmS2NMlqUSI
|
||||||
|
CiV1g1+pv2cy9amld8hoO17ISYFZqMoRxJgD5GuN4y1lUefFe95GHI9loubIJZlR
|
||||||
|
5KlZEvCiaAQoGvYiacf4BNkljyrwgPVM5e71dGon7jyghmV6yUaUL6+1J8BU/KYg
|
||||||
|
jz8RtMtptqkwKPKQVfuDcr/eoH6uZwPRbyfqSui8SuMz3Df6Dnx1hOtlQRJC6eNo
|
||||||
|
U9L3jkmCsbbMNBAz6iQjyFHFa9iqzwL7nvqZTryjmI5Dpn+BnT7Q5cduK+N5vt4+
|
||||||
|
RjNVrz/l6+nR68B5GO96zUTV3/KrEmFr
|
||||||
|
-----END CERTIFICATE-----
|
||||||
27
examples/server.key
Normal file
27
examples/server.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAy9RQSiGpB+HyjMpRCEfV9M/4g7gXq/qRizxDspJujoBzSW0d
|
||||||
|
4FqMHaSRX2QOA+euhtlOYTgsvWZcyv5cvDfL1CtrNWSVBrlo7wIy5tg60Z3AJnWT
|
||||||
|
/Zxj4WIqkPwdglB1sRBsI1Fn0o6nJu4HekZedXDK6fua4lOPfsQG84EhRQKSMz2o
|
||||||
|
7Nesk8/UMjb+5WoRmG7mxrpe0/OYlnydqzqwHUQ+I5CHl1kOhePo9ZBTFMA5Ece8
|
||||||
|
kGQs37rFCEy92xCYHDgp+CjjyYbeBskF3o0/a88K2bt8J7uXkn4h14HjtFHqfYnq
|
||||||
|
n60cwyIx3T/uMUh6EmhKQezaw60xyIivmjH8tQIDAQABAoIBAQDFzhTc3C2daLhp
|
||||||
|
yS06S/xmyCz0JwNR8qir5qAL++8ue5ll+G61+yle2wX4/LBdOck1NE3MKye/5kbG
|
||||||
|
+HImdj9od3pjJmk5TVV4HToorE7ofZ6rtA8aX1rOruWALiq0/EA6xSUsYSPQQoAU
|
||||||
|
V4sKLqAceIly6Kk2WsE21CWqyfXvcQOtfBYmFwmPCImWecJLypeheEpz1U2EYl65
|
||||||
|
u6b0NsXeODrLXEAEFjdb6UBJtzRtTJ/OnbDvghu9xMjlT0Pj+inoAv/ePZB8bmvH
|
||||||
|
XGhZo7dzgsDZ+eys7XnbeggUOhFImzCjO1f3pIVXWThGDgKIrpc9Evac2Q3AjTFY
|
||||||
|
NV9HBV9BAoGBAOyWq7HDgeCEu54orPAmdkO4j/HFX+262BTQoVCg4OX3Iv9A/lH4
|
||||||
|
lpVGrFlK0qJF9jb7mjDmXP2LW0fwzyHe42DGFbZkKdfiMBuE+qoPeAV9s+SjE4H3
|
||||||
|
l3tHoAOFUt2wITcHK4EYjoLMAgrbRNiv9t/gqiMm1oIb3fkUbpOoGG3LAoGBANyN
|
||||||
|
kLop3JfN1Kzto7gJq/tLS21joexTU+s4EJ+a4Q8KH1d47DLI+4119Ot+NWwi9X3S
|
||||||
|
sbOKZOjfrGw9+HPI64i7hD9HPSK58IUbsfVR9vPlPei45inRfi6s7+EUzKifOKZU
|
||||||
|
o1ecpOSPYQHZtDToGcQCTS0IFwMXHgrkP380We9/AoGBAI1wljyz8RVUxQWMs7bu
|
||||||
|
h4187TFRGkR5i20GPSqCw3E4CkgnhuNihkO/+JF5VeuFf+jnCgtp7PX3Nh8QLATH
|
||||||
|
x4+3XIup3goeQzxwh5rbnJlLyRxLEgKFDp6490SjlCLMhU7sjmmjUK+JXz82TzZs
|
||||||
|
HF9DZPOW6G7oUg/y0xibSd95AoGAXpDEcU3pq50xh0QNYqei+gh6uthxYScJYF2V
|
||||||
|
oxmBTjWE4riSbeQHF8xvy1k+BrOmluB0GQtJ4R+minK3yM1pUCM2vPsKl40qN6h8
|
||||||
|
UTdnr4OnW9WLunp8o/66i8OjTNmYLJk1wCcF/IoNigGSZuztv0FNXfWOCGEtHHZp
|
||||||
|
U11bAnkCgYEAoU0sdFL3IfmxnNQ9CDmgXdJM0SpUm4ECd2jM/fRdgLelL+WislCF
|
||||||
|
gHjZw+3mplIzqQ9DMwRkjbaIxP0H92OopOBIqmShWUuzWw/Dj0L8PGe/7skcwsGD
|
||||||
|
/VLEkGzrxJwP4kokUu1kvLOqHM429JXsb8wO16iMQAB93yUZ+X8PGfQ=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@ -115,6 +115,21 @@ class Config(object):
|
|||||||
logger_class.install()
|
logger_class.install()
|
||||||
return logger_class
|
return logger_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_ssl(self):
|
||||||
|
return self.certfile or self.keyfile
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssl_options(self):
|
||||||
|
opts = {}
|
||||||
|
if self.certfile:
|
||||||
|
opts['certfile'] = self.certfile
|
||||||
|
|
||||||
|
if self.keyfile:
|
||||||
|
opts['keyfile'] = self.keyfile
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
|
||||||
class SettingMeta(type):
|
class SettingMeta(type):
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
@ -1066,3 +1081,25 @@ class ProxyAllowFrom(Setting):
|
|||||||
desc = """\
|
desc = """\
|
||||||
Front-end's IPs from which allowed accept proxy requests (comma separate).
|
Front-end's IPs from which allowed accept proxy requests (comma separate).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class KeyFile(Setting):
|
||||||
|
name = "keyfile"
|
||||||
|
section = "Ssl"
|
||||||
|
cli = ["--keyfile"]
|
||||||
|
meta = "FILE"
|
||||||
|
validator = validate_string
|
||||||
|
default = None
|
||||||
|
desc = """\
|
||||||
|
SSL key file
|
||||||
|
"""
|
||||||
|
|
||||||
|
class CertFile(Setting):
|
||||||
|
name = "certfile"
|
||||||
|
section = "Ssl"
|
||||||
|
cli = ["--certfile"]
|
||||||
|
meta = "FILE"
|
||||||
|
validator = validate_string
|
||||||
|
default = None
|
||||||
|
desc = """\
|
||||||
|
SSL certificate file
|
||||||
|
"""
|
||||||
|
|||||||
@ -117,6 +117,14 @@ def create_sockets(conf, log):
|
|||||||
laddr = conf.address
|
laddr = conf.address
|
||||||
listeners = []
|
listeners = []
|
||||||
|
|
||||||
|
# check ssl config early to raise the error on startup
|
||||||
|
# only the certfile is needed since it can contains the keyfile
|
||||||
|
if conf.certfile and not os.path.exists(conf.certfile):
|
||||||
|
raise ValueError('certfile "%s" does not exist' % conf.certfile)
|
||||||
|
|
||||||
|
if conf.keyfile and not os.path.exists(conf.keyfile):
|
||||||
|
raise ValueError('certfile "%s" does not exist' % conf.keyfile)
|
||||||
|
|
||||||
# sockets are already bound
|
# sockets are already bound
|
||||||
if 'GUNICORN_FD' in os.environ:
|
if 'GUNICORN_FD' in os.environ:
|
||||||
fds = os.environ.pop('GUNICORN_FD').split(',')
|
fds = os.environ.pop('GUNICORN_FD').split(',')
|
||||||
|
|||||||
@ -3,11 +3,10 @@
|
|||||||
# This file is part of gunicorn released under the MIT license.
|
# This file is part of gunicorn released under the MIT license.
|
||||||
# See the NOTICE for more information.
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
from __future__ import with_statement
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import errno
|
import errno
|
||||||
import socket
|
import socket
|
||||||
|
import ssl
|
||||||
|
|
||||||
import gunicorn.http as http
|
import gunicorn.http as http
|
||||||
import gunicorn.http.wsgi as wsgi
|
import gunicorn.http.wsgi as wsgi
|
||||||
@ -47,10 +46,19 @@ class AsyncWorker(base.Worker):
|
|||||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
self.log.debug("Closing connection. %s", e)
|
self.log.debug("Closing connection. %s", e)
|
||||||
|
except ssl.SSLError:
|
||||||
|
raise # pass to next try-except level
|
||||||
except socket.error:
|
except socket.error:
|
||||||
raise # pass to next try-except level
|
raise # pass to next try-except level
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.handle_error(req, client, addr, e)
|
self.handle_error(req, client, addr, e)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
if e.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
self.log.debug("ssl connection closed")
|
||||||
|
client.close()
|
||||||
|
else:
|
||||||
|
self.log.debug("Error processing SSL request.")
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.args[0] not in (errno.EPIPE, errno.ECONNRESET):
|
if e.args[0] not in (errno.EPIPE, errno.ECONNRESET):
|
||||||
self.log.exception("Socket error processing request.")
|
self.log.exception("Socket error processing request.")
|
||||||
|
|||||||
@ -31,6 +31,14 @@ class EventletWorker(AsyncWorker):
|
|||||||
def timeout_ctx(self):
|
def timeout_ctx(self):
|
||||||
return eventlet.Timeout(self.cfg.keepalive or None, False)
|
return eventlet.Timeout(self.cfg.keepalive or None, False)
|
||||||
|
|
||||||
|
def handle(self, listener, client, addr):
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
client = eventlet.wrap_ssl(client, server_side=True,
|
||||||
|
do_handshake_on_connect=False,
|
||||||
|
**self.cfg.ssl_options)
|
||||||
|
|
||||||
|
super(EventletWorker, self).handle(listener, client, addr)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
acceptors = []
|
acceptors = []
|
||||||
for sock in self.sockets:
|
for sock in self.sockets:
|
||||||
|
|||||||
@ -55,16 +55,22 @@ class GeventWorker(AsyncWorker):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
servers = []
|
servers = []
|
||||||
|
ssl_args = {}
|
||||||
|
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
ssl_args = dict(server_side=True,
|
||||||
|
do_handshake_on_connect=False, **self.cfg.ssl_options)
|
||||||
|
|
||||||
for s in self.sockets:
|
for s in self.sockets:
|
||||||
s.setblocking(1)
|
s.setblocking(1)
|
||||||
pool = Pool(self.worker_connections)
|
pool = Pool(self.worker_connections)
|
||||||
if self.server_class is not None:
|
if self.server_class is not None:
|
||||||
server = self.server_class(
|
server = self.server_class(
|
||||||
s, application=self.wsgi, spawn=pool, log=self.log,
|
s, application=self.wsgi, spawn=pool, log=self.log,
|
||||||
handler_class=self.wsgi_handler)
|
handler_class=self.wsgi_handler, **ssl_args)
|
||||||
else:
|
else:
|
||||||
hfun = partial(self.handle, s)
|
hfun = partial(self.handle, s)
|
||||||
server = StreamServer(s, handle=hfun, spawn=pool)
|
server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args)
|
||||||
|
|
||||||
server.start()
|
server.start()
|
||||||
servers.append(server)
|
servers.append(server)
|
||||||
|
|||||||
@ -75,7 +75,8 @@ class TornadoWorker(Worker):
|
|||||||
httpserver.HTTPConnection.finish = finish
|
httpserver.HTTPConnection.finish = finish
|
||||||
sys.modules["tornado.httpserver"] = httpserver
|
sys.modules["tornado.httpserver"] = httpserver
|
||||||
|
|
||||||
server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop)
|
server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop,
|
||||||
|
ssl_options=self.cfg.ssl_options)
|
||||||
for s in self.sockets:
|
for s in self.sockets:
|
||||||
s.setblocking(0)
|
s.setblocking(0)
|
||||||
if hasattr(server, "add_socket"): # tornado > 2.0
|
if hasattr(server, "add_socket"): # tornado > 2.0
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import errno
|
|||||||
import os
|
import os
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
|
import ssl
|
||||||
|
|
||||||
import gunicorn.http as http
|
import gunicorn.http as http
|
||||||
import gunicorn.http.wsgi as wsgi
|
import gunicorn.http.wsgi as wsgi
|
||||||
@ -75,6 +76,11 @@ class SyncWorker(base.Worker):
|
|||||||
def handle(self, listener, client, addr):
|
def handle(self, listener, client, addr):
|
||||||
req = None
|
req = None
|
||||||
try:
|
try:
|
||||||
|
if self.cfg.is_ssl:
|
||||||
|
client = ssl.wrap_socket(client, server_side=True,
|
||||||
|
do_handshake_on_connect=False,
|
||||||
|
**self.cfg.ssl_options)
|
||||||
|
|
||||||
parser = http.RequestParser(self.cfg, client)
|
parser = http.RequestParser(self.cfg, client)
|
||||||
req = six.next(parser)
|
req = six.next(parser)
|
||||||
self.handle_request(listener, req, client, addr)
|
self.handle_request(listener, req, client, addr)
|
||||||
@ -82,6 +88,13 @@ class SyncWorker(base.Worker):
|
|||||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
self.log.debug("Closing connection. %s", e)
|
self.log.debug("Closing connection. %s", e)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
if e.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
self.log.debug("ssl connection closed")
|
||||||
|
client.close()
|
||||||
|
else:
|
||||||
|
self.log.debug("Error processing SSL request.")
|
||||||
|
self.handle_error(req, client, addr, e)
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.args[0] != errno.EPIPE:
|
if e.args[0] != errno.EPIPE:
|
||||||
self.log.exception("Error processing request.")
|
self.log.exception("Error processing request.")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user