Skip to content

Commit

Permalink
Added parameter to ssl_context hook for constructing default context
Browse files Browse the repository at this point in the history
Signed-off-by: Tero Saarni <[email protected]>
  • Loading branch information
tsaarni committed Feb 11, 2022
1 parent 5a581c0 commit 362a52b
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 25 deletions.
30 changes: 12 additions & 18 deletions examples/example_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,32 +215,26 @@ def worker_int(worker):
def worker_abort(worker):
worker.log.info("worker received SIGABRT signal")

def ssl_context(conf):
def ssl_context(conf, default_ssl_context_factory):
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)
# The default SSLContext returned by the factory function is initialized
# with the TLS parameters from config, including TLS certificates and other
# parameters.
context = default_ssl_context_factory()

# Return different server certificate depending which hostname the client
# uses. Requires Python 3.7 or later.
# The SSLContext can be further customized, for example by enforcing
# minimum TLS version.
context.minimum_version = ssl.TLSVersion.TLSv1_3

# Server can also 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 = default_ssl_context_factory()
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
11 changes: 7 additions & 4 deletions gunicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1970,11 +1970,11 @@ def on_exit(server):
class NewSSLContext(Setting):
name = "ssl_context"
section = "Server Hooks"
validator = validate_callable(1)
validator = validate_callable(2)
type = callable

def ssl_context(config):
return None
def ssl_context(config, default_ssl_context_factory):
return default_ssl_context_factory()

default = staticmethod(ssl_context)
desc = """\
Expand All @@ -1983,7 +1983,10 @@ def ssl_context(config):
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 accept an instance variable for the Config and
a factory function that returns default SSLContext which is initialized
with certificates, private key, cert_reqs, and ciphers according to
config and can be further customized by the callable.
The callable needs to return SSLContext object.
"""

Expand Down
6 changes: 3 additions & 3 deletions gunicorn/sock.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,17 @@ def close_sockets(listeners, unlink=True):
os.unlink(sock_name)

def ssl_context(conf):
context = conf.ssl_context(conf)
if context is None:
def default_ssl_context_factory():
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

return context
return conf.ssl_context(conf, default_ssl_context_factory)

def ssl_wrap_socket(sock, conf):
return ssl_context(conf).wrap_socket(sock, server_side=True,
Expand Down

0 comments on commit 362a52b

Please sign in to comment.