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()
|
||||
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):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
@ -1066,3 +1081,25 @@ class ProxyAllowFrom(Setting):
|
||||
desc = """\
|
||||
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
|
||||
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
|
||||
if 'GUNICORN_FD' in os.environ:
|
||||
fds = os.environ.pop('GUNICORN_FD').split(',')
|
||||
|
||||
@ -3,11 +3,10 @@
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
from datetime import datetime
|
||||
import errno
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
import gunicorn.http as http
|
||||
import gunicorn.http.wsgi as wsgi
|
||||
@ -47,10 +46,19 @@ class AsyncWorker(base.Worker):
|
||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||
except StopIteration as e:
|
||||
self.log.debug("Closing connection. %s", e)
|
||||
except ssl.SSLError:
|
||||
raise # pass to next try-except level
|
||||
except socket.error:
|
||||
raise # pass to next try-except level
|
||||
except Exception as 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:
|
||||
if e.args[0] not in (errno.EPIPE, errno.ECONNRESET):
|
||||
self.log.exception("Socket error processing request.")
|
||||
|
||||
@ -31,6 +31,14 @@ class EventletWorker(AsyncWorker):
|
||||
def timeout_ctx(self):
|
||||
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):
|
||||
acceptors = []
|
||||
for sock in self.sockets:
|
||||
|
||||
@ -55,16 +55,22 @@ class GeventWorker(AsyncWorker):
|
||||
|
||||
def run(self):
|
||||
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:
|
||||
s.setblocking(1)
|
||||
pool = Pool(self.worker_connections)
|
||||
if self.server_class is not None:
|
||||
server = self.server_class(
|
||||
s, application=self.wsgi, spawn=pool, log=self.log,
|
||||
handler_class=self.wsgi_handler)
|
||||
handler_class=self.wsgi_handler, **ssl_args)
|
||||
else:
|
||||
hfun = partial(self.handle, s)
|
||||
server = StreamServer(s, handle=hfun, spawn=pool)
|
||||
server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args)
|
||||
|
||||
server.start()
|
||||
servers.append(server)
|
||||
|
||||
@ -75,7 +75,8 @@ class TornadoWorker(Worker):
|
||||
httpserver.HTTPConnection.finish = finish
|
||||
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:
|
||||
s.setblocking(0)
|
||||
if hasattr(server, "add_socket"): # tornado > 2.0
|
||||
|
||||
@ -9,6 +9,7 @@ import errno
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
import gunicorn.http as http
|
||||
import gunicorn.http.wsgi as wsgi
|
||||
@ -75,6 +76,11 @@ class SyncWorker(base.Worker):
|
||||
def handle(self, listener, client, addr):
|
||||
req = None
|
||||
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)
|
||||
req = six.next(parser)
|
||||
self.handle_request(listener, req, client, addr)
|
||||
@ -82,6 +88,13 @@ class SyncWorker(base.Worker):
|
||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||
except StopIteration as 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:
|
||||
if e.args[0] != errno.EPIPE:
|
||||
self.log.exception("Error processing request.")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user