summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2018-09-22 20:38:16 -0600
committerBert JW Regeer <bertjw@regeer.org>2018-12-02 17:43:55 -0700
commit7b05238c39265c8e4446264a3d4380df74f330fd (patch)
treea1ee93cbce7a304c0dbc75159fe7b3b08fafeee9
parent1fa8f87722af7191f238d9e9b95c6ab4b137abc6 (diff)
downloadwaitress-7b05238c39265c8e4446264a3d4380df74f330fd.tar.gz
Add new adjustments
-rw-r--r--waitress/adjustments.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/waitress/adjustments.py b/waitress/adjustments.py
index ad2d0f4..d9e931f 100644
--- a/waitress/adjustments.py
+++ b/waitress/adjustments.py
@@ -15,6 +15,7 @@
"""
import getopt
import socket
+import warnings
from waitress.compat import (
PY2,
@@ -25,6 +26,15 @@ from waitress.compat import (
truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
+KNOWN_PROXY_HEADERS = frozenset({
+ 'forwarded',
+ 'x-forwarded-by',
+ 'x-forwarded-for',
+ 'x-forwarded-host',
+ 'x-forwarded-port',
+ 'x-forwarded-proto',
+})
+
def asbool(s):
""" Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise
@@ -58,6 +68,9 @@ def aslist(value):
result.extend(subvalues)
return result
+def asset(value):
+ return set(aslist(value))
+
def slash_fixed_str(s):
s = s.strip()
if s:
@@ -80,6 +93,9 @@ class _str_marker(str):
class _int_marker(int):
pass
+class _bool_marker(object):
+ pass
+
class Adjustments(object):
"""This class contains tunable parameters.
"""
@@ -92,6 +108,10 @@ class Adjustments(object):
('listen', aslist),
('threads', int),
('trusted_proxy', str),
+ ('trusted_proxy_count', int),
+ ('trusted_proxy_headers', asset),
+ ('log_untrusted_proxy_headers', asbool),
+ ('clear_untrusted_proxy_headers', asbool),
('url_scheme', str),
('url_prefix', slash_fixed_str),
('backlog', int),
@@ -130,6 +150,35 @@ class Adjustments(object):
# Host allowed to overrid ``wsgi.url_scheme`` via header
trusted_proxy = None
+ # How many proxies we trust when chained
+ #
+ # X-Forwarded-For: 192.0.2.1, "[2001:db8::1]"
+ #
+ # or
+ #
+ # Forwarded: for=192.0.2.1, For="[2001:db8::1]"
+ #
+ # means there were (potentially), two proxies involved. If we know there is
+ # only 1 valid proxy, then that initial IP address "192.0.2.1" is not
+ # trusted and we completely ignore it. If there are two trusted proxies in
+ # the path, this value should be set to a higher number.
+ trusted_proxy_count = 1
+
+ # Which of the proxy headers should we trust, this is a set where you
+ # either specify forwarded or one or more of forwarded-host, forwarded-for,
+ # forwarded-proto, forwarded-port.
+ trusted_proxy_headers = {}
+
+ # Would you like waitress to log warnings about untrusted proxy headers
+ # that were encountered while processing the proxy headers? This only makes
+ # sense to set when you have a trusted_proxy, and you expect the upstream
+ # proxy server to filter invalid headers
+ log_untrusted_proxy_headers = False
+
+ # Should waitress clear any proxy headers that are not deemed trusted from
+ # the environ? Change to True by default in 2.x
+ clear_untrusted_proxy_headers = _bool_marker
+
# default ``wsgi.url_scheme`` value
url_scheme = 'http'
@@ -324,6 +373,43 @@ class Adjustments(object):
except:
raise ValueError('Invalid host/port specified.')
+ if self.trusted_proxy_headers:
+ self.trusted_proxy_headers = {header.lower() for header in self.trusted_proxy_headers}
+
+ unknown_values = self.trusted_proxy_headers - KNOWN_PROXY_HEADERS
+ if unknown_values:
+ raise ValueError(
+ "Received unknown trusted_proxy_headers value (%s) expected one "
+ "of %s" % (", ".join(unknown_values), ", ".join(KNOWN_PROXY_HEADERS))
+ )
+
+ if (
+ 'forwarded' in self.trusted_proxy_headers and
+ self.trusted_proxy_headers - {'forwarded'}
+ ):
+ raise ValueError(
+ "The Forwarded proxy header and the "
+ "X-Forwarded-{By,Host,Proto,Port,For} headers are mutually "
+ "exclusive. Can't trust both!"
+ )
+ elif self.trusted_proxy is not None:
+ warnings.warn(
+ 'No proxy headers were marked as trusted, but trusted_proxy was set. '
+ 'Implicitly trusting X-Forwarded-Proto for backwards compatibility. '
+ 'This will be removed in future versions of waitress.',
+ DeprecationWarning
+ )
+ self.trusted_proxy_headers = {'x-forwarded-proto'}
+
+ if self.clear_untrusted_proxy_headers is _bool_marker:
+ warnings.warn(
+ 'In future versions of Waitress clear_untrusted_proxy_headers will be '
+ 'set to True by default. You may opt-out by setting this value to '
+ 'False, or opt-in explicitly by setting this to True.',
+ DeprecationWarning
+ )
+ self.clear_untrusted_proxy_headers = False
+
self.listen = wanted_sockets
self.check_sockets(self.sockets)