mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Update SSLContext handling
* Change deprecated ssl.wrap_socket() to SSLContext.wrap_context(). * Add new server hook to allow user to create custom SSLContext. * Updated the documentation. Signed-off-by: Tero Saarni <tero.saarni@est.tech>
This commit is contained in:
parent
ff58e0c6da
commit
5a581c0b14
@ -916,6 +916,26 @@ Called just before exiting Gunicorn.
|
||||
|
||||
The callable needs to accept a single instance variable for the Arbiter.
|
||||
|
||||
.. _ssl-context:
|
||||
|
||||
``ssl_context``
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
**Default:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def ssl_context(config):
|
||||
return None
|
||||
|
||||
Called when SSLContext is needed.
|
||||
|
||||
Allows fully customized SSL context to be used in place of the default
|
||||
context.
|
||||
|
||||
The callable needs to accept a single instance variable for the Config.
|
||||
The callable needs to return SSLContext object.
|
||||
|
||||
Server Mechanics
|
||||
----------------
|
||||
|
||||
@ -1506,4 +1526,3 @@ set this to a higher value.
|
||||
.. note::
|
||||
``sync`` worker does not support persistent connections and will
|
||||
ignore this option.
|
||||
|
||||
|
||||
@ -214,3 +214,33 @@ def worker_int(worker):
|
||||
|
||||
def worker_abort(worker):
|
||||
worker.log.info("worker received SIGABRT signal")
|
||||
|
||||
def ssl_context(conf):
|
||||
import ssl
|
||||
|
||||
def set_defaults(context):
|
||||
context.verify_mode = conf.cert_reqs
|
||||
context.minimum_version = ssl.TLSVersion.TLSv1_3
|
||||
if conf.ciphers:
|
||||
context.set_ciphers(conf.ciphers)
|
||||
if conf.ca_certs:
|
||||
context.load_verify_locations(cafile=conf.ca_certs)
|
||||
|
||||
# Return different server certificate depending which hostname the client
|
||||
# uses. Requires Python 3.7 or later.
|
||||
def sni_callback(socket, server_hostname, context):
|
||||
if server_hostname == "foo.127.0.0.1.nip.io":
|
||||
new_context = ssl.SSLContext()
|
||||
new_context.load_cert_chain(certfile="foo.pem", keyfile="foo-key.pem")
|
||||
set_defaults(new_context)
|
||||
socket.context = new_context
|
||||
|
||||
context = ssl.SSLContext(conf.ssl_version)
|
||||
context.sni_callback = sni_callback
|
||||
set_defaults(context)
|
||||
|
||||
# Load fallback certificate that will be returned when there is no match
|
||||
# or client did not set TLS SNI (server_hostname == None)
|
||||
context.load_cert_chain(certfile=conf.certfile, keyfile=conf.keyfile)
|
||||
|
||||
return context
|
||||
|
||||
@ -1967,6 +1967,25 @@ class OnExit(Setting):
|
||||
The callable needs to accept a single instance variable for the Arbiter.
|
||||
"""
|
||||
|
||||
class NewSSLContext(Setting):
|
||||
name = "ssl_context"
|
||||
section = "Server Hooks"
|
||||
validator = validate_callable(1)
|
||||
type = callable
|
||||
|
||||
def ssl_context(config):
|
||||
return None
|
||||
|
||||
default = staticmethod(ssl_context)
|
||||
desc = """\
|
||||
Called when SSLContext is needed.
|
||||
|
||||
Allows fully customized SSL context to be used in place of the default
|
||||
context.
|
||||
|
||||
The callable needs to accept a single instance variable for the Config.
|
||||
The callable needs to return SSLContext object.
|
||||
"""
|
||||
|
||||
class ProxyProtocol(Setting):
|
||||
name = "proxy_protocol"
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
import errno
|
||||
import os
|
||||
import socket
|
||||
import ssl
|
||||
import stat
|
||||
import sys
|
||||
import time
|
||||
@ -203,10 +204,27 @@ def create_sockets(conf, log, fds=None):
|
||||
|
||||
return listeners
|
||||
|
||||
|
||||
def close_sockets(listeners, unlink=True):
|
||||
for sock in listeners:
|
||||
sock_name = sock.getsockname()
|
||||
sock.close()
|
||||
if unlink and _sock_type(sock_name) is UnixSocket:
|
||||
os.unlink(sock_name)
|
||||
|
||||
def ssl_context(conf):
|
||||
context = conf.ssl_context(conf)
|
||||
if context is None:
|
||||
context = ssl.SSLContext(conf.ssl_version)
|
||||
context.load_cert_chain(certfile=conf.certfile, keyfile=conf.keyfile)
|
||||
context.verify_mode = conf.cert_reqs
|
||||
if conf.ciphers:
|
||||
context.set_ciphers(conf.ciphers)
|
||||
if conf.ca_certs:
|
||||
context.load_verify_locations(cafile=conf.ca_certs)
|
||||
|
||||
return context
|
||||
|
||||
def ssl_wrap_socket(sock, conf):
|
||||
return ssl_context(conf).wrap_socket(sock, server_side=True,
|
||||
suppress_ragged_eofs=conf.suppress_ragged_eofs,
|
||||
do_handshake_on_connect=conf.do_handshake_on_connect)
|
||||
|
||||
@ -21,6 +21,7 @@ import eventlet.wsgi
|
||||
import greenlet
|
||||
|
||||
from gunicorn.workers.base_async import AsyncWorker
|
||||
from gunicorn.sock import ssl_wrap_socket
|
||||
|
||||
# ALREADY_HANDLED is removed in 0.30.3+ now it's `WSGI_LOCAL.already_handled: bool`
|
||||
# https://github.com/eventlet/eventlet/pull/544
|
||||
@ -153,9 +154,7 @@ class EventletWorker(AsyncWorker):
|
||||
|
||||
def handle(self, listener, client, addr):
|
||||
if self.cfg.is_ssl:
|
||||
client = eventlet.wrap_ssl(client, server_side=True,
|
||||
**self.cfg.ssl_options)
|
||||
|
||||
client = ssl_wrap_socket(client, self.cfg)
|
||||
super().handle(listener, client, addr)
|
||||
|
||||
def run(self):
|
||||
|
||||
@ -24,6 +24,7 @@ from gevent import hub, monkey, socket, pywsgi
|
||||
|
||||
import gunicorn
|
||||
from gunicorn.http.wsgi import base_environ
|
||||
from gunicorn.sock import ssl_context
|
||||
from gunicorn.workers.base_async import AsyncWorker
|
||||
|
||||
VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__)
|
||||
@ -58,7 +59,7 @@ class GeventWorker(AsyncWorker):
|
||||
ssl_args = {}
|
||||
|
||||
if self.cfg.is_ssl:
|
||||
ssl_args = dict(server_side=True, **self.cfg.ssl_options)
|
||||
ssl_args = dict(ssl_context=ssl_context(self.cfg))
|
||||
|
||||
for s in self.sockets:
|
||||
s.setblocking(1)
|
||||
|
||||
@ -27,6 +27,7 @@ from threading import RLock
|
||||
from . import base
|
||||
from .. import http
|
||||
from .. import util
|
||||
from .. import sock
|
||||
from ..http import wsgi
|
||||
|
||||
|
||||
@ -49,8 +50,7 @@ class TConn(object):
|
||||
if self.parser is None:
|
||||
# wrap the socket if needed
|
||||
if self.cfg.is_ssl:
|
||||
self.sock = ssl.wrap_socket(self.sock, server_side=True,
|
||||
**self.cfg.ssl_options)
|
||||
self.sock = sock.ssl_wrap_socket(self.sock, self.cfg)
|
||||
|
||||
# initialize the parser
|
||||
self.parser = http.RequestParser(self.cfg, self.sock, self.client)
|
||||
|
||||
@ -17,6 +17,7 @@ from tornado.ioloop import IOLoop, PeriodicCallback
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from gunicorn.workers.base import Worker
|
||||
from gunicorn import __version__ as gversion
|
||||
from gunicorn.sock import ssl_context
|
||||
|
||||
|
||||
# Tornado 5.0 updated its IOLoop, and the `io_loop` arguments to many
|
||||
@ -139,16 +140,11 @@ class TornadoWorker(Worker):
|
||||
server_class = _HTTPServer
|
||||
|
||||
if self.cfg.is_ssl:
|
||||
_ssl_opt = copy.deepcopy(self.cfg.ssl_options)
|
||||
# tornado refuses initialization if ssl_options contains following
|
||||
# options
|
||||
del _ssl_opt["do_handshake_on_connect"]
|
||||
del _ssl_opt["suppress_ragged_eofs"]
|
||||
if TORNADO5:
|
||||
server = server_class(app, ssl_options=_ssl_opt)
|
||||
server = server_class(app, ssl_options=ssl_context(self.cfg))
|
||||
else:
|
||||
server = server_class(app, io_loop=self.ioloop,
|
||||
ssl_options=_ssl_opt)
|
||||
ssl_options=ssl_context(self.cfg))
|
||||
else:
|
||||
if TORNADO5:
|
||||
server = server_class(app)
|
||||
|
||||
@ -14,6 +14,7 @@ import sys
|
||||
|
||||
import gunicorn.http as http
|
||||
import gunicorn.http.wsgi as wsgi
|
||||
import gunicorn.sock as sock
|
||||
import gunicorn.util as util
|
||||
import gunicorn.workers.base as base
|
||||
|
||||
@ -128,9 +129,7 @@ class SyncWorker(base.Worker):
|
||||
req = None
|
||||
try:
|
||||
if self.cfg.is_ssl:
|
||||
client = ssl.wrap_socket(client, server_side=True,
|
||||
**self.cfg.ssl_options)
|
||||
|
||||
client = sock.ssl_wrap_socket(client, self.cfg)
|
||||
parser = http.RequestParser(self.cfg, client, addr)
|
||||
req = next(parser)
|
||||
self.handle_request(listener, req, client, addr)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user