mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Deprecate ssl_version option
This change defaults SSLContext to Python's ssl.create_default_context() and marks ssl_version option as deprecated. The option value will be ignored and warnign will be printed in stderr. The ssl_version option was depending on old method of setting TLS min/max version, which has not worked well anymore with modern Python versions.
This commit is contained in:
parent
f859de498a
commit
d8c3b1490e
@ -48,7 +48,9 @@ def format_settings(app):
|
||||
|
||||
|
||||
def fmt_setting(s):
|
||||
if callable(s.default):
|
||||
if hasattr(s, "default_doc"):
|
||||
val = s.default_doc
|
||||
elif callable(s.default):
|
||||
val = inspect.getsource(s.default)
|
||||
val = "\n".join(" %s" % line for line in val.splitlines())
|
||||
val = "\n\n.. code-block:: python\n\n" + val
|
||||
|
||||
@ -32,7 +32,7 @@ Config File
|
||||
|
||||
**Default:** ``'./gunicorn.conf.py'``
|
||||
|
||||
The Gunicorn config file.
|
||||
:ref:`The Gunicorn config file<configuration_file>`.
|
||||
|
||||
A string of the form ``PATH``, ``file:PATH``, or ``python:MODULE_NAME``.
|
||||
|
||||
@ -305,16 +305,6 @@ The log config file to use.
|
||||
Gunicorn uses the standard Python logging module's Configuration
|
||||
file format.
|
||||
|
||||
.. _logconfig-json:
|
||||
|
||||
logiconfig_json
|
||||
~~~~~~~~~
|
||||
|
||||
* ``--log-config-json FILE``
|
||||
* ``None``
|
||||
|
||||
The log config file written in JSON.
|
||||
|
||||
.. _logconfig-dict:
|
||||
|
||||
``logconfig_dict``
|
||||
@ -324,9 +314,9 @@ The log config file written in JSON.
|
||||
|
||||
The log config dictionary to use, using the standard Python
|
||||
logging module's dictionary configuration format. This option
|
||||
takes precedence over the :ref:`logconfig` and :ref:`logConfigJson` options, which uses the
|
||||
older file configuration format and JSON respectively.
|
||||
|
||||
takes precedence over the :ref:`logconfig` and :ref:`logConfigJson` options,
|
||||
which uses the older file configuration format and JSON
|
||||
respectively.
|
||||
|
||||
Format: https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig
|
||||
|
||||
@ -334,6 +324,21 @@ For more context you can look at the default configuration dictionary for loggin
|
||||
|
||||
.. versionadded:: 19.8
|
||||
|
||||
.. _logconfig-json:
|
||||
|
||||
``logconfig_json``
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Command line:** ``--log-config-json FILE``
|
||||
|
||||
**Default:** ``None``
|
||||
|
||||
The log config to read config from a JSON file
|
||||
|
||||
Format: https://docs.python.org/3/library/logging.config.html#logging.config.jsonConfig
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. _syslog-addr:
|
||||
|
||||
``syslog_addr``
|
||||
@ -519,7 +524,10 @@ SSL certificate file
|
||||
|
||||
**Default:** ``<_SSLMethod.PROTOCOL_TLS: 2>``
|
||||
|
||||
SSL version to use.
|
||||
SSL version to use (see stdlib ssl module's).
|
||||
|
||||
.. deprecated:: 20.2
|
||||
The option is deprecated and it is currently ignored. Use :ref:`ssl-context` instead.
|
||||
|
||||
============= ============
|
||||
--ssl-version Description
|
||||
@ -542,6 +550,9 @@ TLS_SERVER Auto-negotiate the highest protocol version like TLS,
|
||||
.. versionchanged:: 20.0
|
||||
This setting now accepts string names based on ``ssl.PROTOCOL_``
|
||||
constants.
|
||||
.. versionchanged:: 20.0.1
|
||||
The default value has been changed from ``ssl.PROTOCOL_SSLv23`` to
|
||||
``ssl.PROTOCOL_TLS`` when Python >= 3.6 .
|
||||
|
||||
.. _cert-reqs:
|
||||
|
||||
@ -554,15 +565,13 @@ TLS_SERVER Auto-negotiate the highest protocol version like TLS,
|
||||
|
||||
Whether client certificate is required (see stdlib ssl module's)
|
||||
|
||||
**Options:**
|
||||
|
||||
`--cert-reqs=0` --- no client veirifcation
|
||||
|
||||
`--cert-reqs=1` --- ssl.CERT_OPTIONAL
|
||||
|
||||
`--cert-reqs=2` --- ssl.CERT_REQUIRED
|
||||
|
||||
|
||||
=========== ===========================
|
||||
--cert-reqs Description
|
||||
=========== ===========================
|
||||
`0` no client veirifcation
|
||||
`1` ssl.CERT_OPTIONAL
|
||||
`2` ssl.CERT_REQUIRED
|
||||
=========== ===========================
|
||||
|
||||
.. _ca-certs:
|
||||
|
||||
@ -954,8 +963,7 @@ The callable needs to accept a single instance variable for the Arbiter.
|
||||
|
||||
Called when SSLContext is needed.
|
||||
|
||||
Allows fully customized SSL context to be used in place of the default
|
||||
context.
|
||||
Allows customizing SSL context.
|
||||
|
||||
The callable needs to accept an instance variable for the Config and
|
||||
a factory function that returns default SSLContext which is initialized
|
||||
@ -963,6 +971,18 @@ 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.
|
||||
|
||||
Following example shows a configuration file that sets the minimum TLS version to 1.3:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def ssl_context(conf, default_ssl_context_factory):
|
||||
import ssl
|
||||
context = default_ssl_context_factory()
|
||||
context.minimum_version = ssl.TLSVersion.TLSv1_3
|
||||
return context
|
||||
|
||||
.. versionadded:: 20.2
|
||||
|
||||
Server Mechanics
|
||||
----------------
|
||||
|
||||
@ -1107,7 +1127,7 @@ If not set, the default temporary directory will be used.
|
||||
|
||||
**Command line:** ``-u USER`` or ``--user USER``
|
||||
|
||||
**Default:** ``501``
|
||||
**Default:** ``os.geteuid()``
|
||||
|
||||
Switch worker processes to run as this user.
|
||||
|
||||
@ -1122,7 +1142,7 @@ change the worker process user.
|
||||
|
||||
**Command line:** ``-g GROUP`` or ``--group GROUP``
|
||||
|
||||
**Default:** ``20``
|
||||
**Default:** ``os.getegid()``
|
||||
|
||||
Switch worker process to run as this group.
|
||||
|
||||
@ -1226,8 +1246,9 @@ variable. If it is not defined, the default is ``"127.0.0.1"``.
|
||||
.. note::
|
||||
|
||||
The interplay between the request headers, the value of ``forwarded_allow_ips``, and the value of
|
||||
``secure_scheme_headers`` is complex. Various scenarios are documented below to further elaborate. In each case, we
|
||||
have a request from the remote address 134.213.44.18, and the default value of ``secure_scheme_headers``:
|
||||
``secure_scheme_headers`` is complex. Various scenarios are documented below to further elaborate.
|
||||
In each case, we have a request from the remote address 134.213.44.18, and the default value of
|
||||
``secure_scheme_headers``:
|
||||
|
||||
.. code::
|
||||
|
||||
@ -1238,7 +1259,7 @@ variable. If it is not defined, the default is ``"127.0.0.1"``.
|
||||
}
|
||||
|
||||
|
||||
.. list-table::
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:align: center
|
||||
:widths: auto
|
||||
@ -1247,35 +1268,35 @@ variable. If it is not defined, the default is ``"127.0.0.1"``.
|
||||
- Secure Request Headers
|
||||
- Result
|
||||
- Explanation
|
||||
* - .. code::
|
||||
* - .. code::
|
||||
|
||||
["127.0.0.1"]
|
||||
- .. code::
|
||||
|
||||
X-Forwarded-Proto: https
|
||||
- .. code::
|
||||
- .. code::
|
||||
|
||||
wsgi.url_scheme = "http"
|
||||
- IP address was not allowed
|
||||
* - .. code::
|
||||
* - .. code::
|
||||
|
||||
"*"
|
||||
- <none>
|
||||
- .. code::
|
||||
- .. code::
|
||||
|
||||
wsgi.url_scheme = "http"
|
||||
- IP address allowed, but no secure headers provided
|
||||
* - .. code::
|
||||
* - .. code::
|
||||
|
||||
"*"
|
||||
- .. code::
|
||||
|
||||
X-Forwarded-Proto: https
|
||||
- .. code::
|
||||
- .. code::
|
||||
|
||||
wsgi.url_scheme = "https"
|
||||
- IP address allowed, one request header matched
|
||||
* - .. code::
|
||||
* - .. code::
|
||||
|
||||
["134.213.44.18"]
|
||||
- .. code::
|
||||
|
||||
@ -364,25 +364,9 @@ def validate_pos_int(val):
|
||||
|
||||
|
||||
def validate_ssl_version(val):
|
||||
ssl_versions = {}
|
||||
for protocol in [p for p in dir(ssl) if p.startswith("PROTOCOL_")]:
|
||||
ssl_versions[protocol[9:]] = getattr(ssl, protocol)
|
||||
if val in ssl_versions:
|
||||
# string matching PROTOCOL_...
|
||||
return ssl_versions[val]
|
||||
|
||||
try:
|
||||
intval = validate_pos_int(val)
|
||||
if intval in ssl_versions.values():
|
||||
# positive int matching a protocol int constant
|
||||
return intval
|
||||
except (ValueError, TypeError):
|
||||
# negative integer or not an integer
|
||||
# drop this in favour of the more descriptive ValueError below
|
||||
pass
|
||||
|
||||
raise ValueError("Invalid ssl_version: %s. Valid options: %s"
|
||||
% (val, ', '.join(ssl_versions)))
|
||||
if val != SSLVersion.default:
|
||||
sys.stderr.write("Warning: option `ssl_version` is deprecated and it is ignored. Use ssl_context instead.\n")
|
||||
return val
|
||||
|
||||
|
||||
def validate_string(val):
|
||||
@ -736,7 +720,7 @@ class WorkerConnections(Setting):
|
||||
desc = """\
|
||||
The maximum number of simultaneous clients.
|
||||
|
||||
This setting only affects the Eventlet and Gevent worker types.
|
||||
This setting only affects the ``gthread``, ``eventlet`` and ``gevent`` worker types.
|
||||
"""
|
||||
|
||||
|
||||
@ -1066,6 +1050,7 @@ class Chdir(Setting):
|
||||
cli = ["--chdir"]
|
||||
validator = validate_chdir
|
||||
default = util.getcwd()
|
||||
default_doc = "``'.'``"
|
||||
desc = """\
|
||||
Change directory to specified directory before loading apps.
|
||||
"""
|
||||
@ -1157,6 +1142,7 @@ class User(Setting):
|
||||
meta = "USER"
|
||||
validator = validate_user
|
||||
default = os.geteuid()
|
||||
default_doc = "``os.geteuid()``"
|
||||
desc = """\
|
||||
Switch worker processes to run as this user.
|
||||
|
||||
@ -1173,6 +1159,7 @@ class Group(Setting):
|
||||
meta = "GROUP"
|
||||
validator = validate_group
|
||||
default = os.getegid()
|
||||
default_doc = "``os.getegid()``"
|
||||
desc = """\
|
||||
Switch worker process to run as this group.
|
||||
|
||||
@ -2019,14 +2006,25 @@ class NewSSLContext(Setting):
|
||||
desc = """\
|
||||
Called when SSLContext is needed.
|
||||
|
||||
Allows fully customized SSL context to be used in place of the default
|
||||
context.
|
||||
Allows customizing SSL context.
|
||||
|
||||
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.
|
||||
|
||||
Following example shows a configuration file that sets the minimum TLS version to 1.3:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def ssl_context(conf, default_ssl_context_factory):
|
||||
import ssl
|
||||
context = default_ssl_context_factory()
|
||||
context.minimum_version = ssl.TLSVersion.TLSv1_3
|
||||
return context
|
||||
|
||||
.. versionadded:: 20.2
|
||||
"""
|
||||
|
||||
class ProxyProtocol(Setting):
|
||||
@ -2105,17 +2103,12 @@ class SSLVersion(Setting):
|
||||
else:
|
||||
default = ssl.PROTOCOL_SSLv23
|
||||
|
||||
desc = """\
|
||||
SSL version to use (see stdlib ssl module's)
|
||||
|
||||
.. versionchanged:: 20.0.1
|
||||
The default value has been changed from ``ssl.PROTOCOL_SSLv23`` to
|
||||
``ssl.PROTOCOL_TLS`` when Python >= 3.6 .
|
||||
|
||||
"""
|
||||
default = ssl.PROTOCOL_SSLv23
|
||||
desc = """\
|
||||
SSL version to use.
|
||||
SSL version to use (see stdlib ssl module's).
|
||||
|
||||
.. deprecated:: 20.2
|
||||
The option is deprecated and it is currently ignored. Use :ref:`ssl-context` instead.
|
||||
|
||||
============= ============
|
||||
--ssl-version Description
|
||||
@ -2138,6 +2131,9 @@ class SSLVersion(Setting):
|
||||
.. versionchanged:: 20.0
|
||||
This setting now accepts string names based on ``ssl.PROTOCOL_``
|
||||
constants.
|
||||
.. versionchanged:: 20.0.1
|
||||
The default value has been changed from ``ssl.PROTOCOL_SSLv23`` to
|
||||
``ssl.PROTOCOL_TLS`` when Python >= 3.6 .
|
||||
"""
|
||||
|
||||
|
||||
@ -2149,13 +2145,14 @@ class CertReqs(Setting):
|
||||
default = ssl.CERT_NONE
|
||||
desc = """\
|
||||
Whether client certificate is required (see stdlib ssl module's)
|
||||
============== ===========================
|
||||
`--cert-reqs=0` --- no client veirifcation
|
||||
|
||||
`--cert-reqs=1` --- ssl.CERT_OPTIONAL
|
||||
|
||||
`--cert-reqs=2` --- ssl.CERT_REQUIRED
|
||||
============== ===========================
|
||||
=========== ===========================
|
||||
--cert-reqs Description
|
||||
=========== ===========================
|
||||
`0` no client veirifcation
|
||||
`1` ssl.CERT_OPTIONAL
|
||||
`2` ssl.CERT_REQUIRED
|
||||
=========== ===========================
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@ -213,13 +213,11 @@ def close_sockets(listeners, unlink=True):
|
||||
|
||||
def ssl_context(conf):
|
||||
def default_ssl_context_factory():
|
||||
context = ssl.SSLContext(conf.ssl_version)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=conf.ca_certs)
|
||||
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 conf.ssl_context(conf, default_ssl_context_factory)
|
||||
|
||||
@ -453,41 +453,6 @@ def test_umask_config(options, expected):
|
||||
assert app.cfg.umask == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("options, expected", [
|
||||
(["--ssl-version", "SSLv23"], 2),
|
||||
(["--ssl-version", "TLSv1"], 3),
|
||||
(["--ssl-version", "2"], 2),
|
||||
(["--ssl-version", "3"], 3),
|
||||
])
|
||||
def test_ssl_version_named_constants_python3(options, expected):
|
||||
_test_ssl_version(options, expected)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6),
|
||||
reason="requires python3.6+")
|
||||
@pytest.mark.parametrize("options, expected", [
|
||||
(["--ssl-version", "TLS"], 2),
|
||||
(["--ssl-version", "TLSv1_1"], 4),
|
||||
(["--ssl-version", "TLSv1_2"], 5),
|
||||
(["--ssl-version", "TLS_SERVER"], 17),
|
||||
])
|
||||
def test_ssl_version_named_constants_python36(options, expected):
|
||||
_test_ssl_version(options, expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ssl_version", [
|
||||
"FOO",
|
||||
"-99",
|
||||
"99991234"
|
||||
])
|
||||
def test_ssl_version_bad(ssl_version):
|
||||
c = config.Config()
|
||||
with pytest.raises(ValueError) as exc:
|
||||
c.set("ssl_version", ssl_version)
|
||||
assert 'Valid options' in str(exc.value)
|
||||
assert "TLSv" in str(exc.value)
|
||||
|
||||
|
||||
def _test_ssl_version(options, expected):
|
||||
cmdline = ["prog_name"]
|
||||
cmdline.extend(options)
|
||||
|
||||
@ -32,14 +32,6 @@ def test_certfile():
|
||||
assert CertFile.default is None
|
||||
|
||||
|
||||
def test_ssl_version():
|
||||
assert issubclass(SSLVersion, Setting)
|
||||
assert SSLVersion.name == 'ssl_version'
|
||||
assert SSLVersion.section == 'SSL'
|
||||
assert SSLVersion.cli == ['--ssl-version']
|
||||
assert SSLVersion.default == ssl.PROTOCOL_SSLv23
|
||||
|
||||
|
||||
def test_cacerts():
|
||||
assert issubclass(CACerts, Setting)
|
||||
assert CACerts.name == 'ca_certs'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user