From d28366a683ccddf95536847de9cc6acb07e6b675 Mon Sep 17 00:00:00 2001 From: Konstantin Kapustin Date: Tue, 24 Jul 2012 17:45:00 +0400 Subject: [PATCH] Add ForwardedAllowIPS option. --- gunicorn/config.py | 19 +++++++++++++++++++ gunicorn/http/wsgi.py | 10 ++++++---- tests/003-test-config.py | 17 +++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/gunicorn/config.py b/gunicorn/config.py index df4d8076..b9191387 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -212,6 +212,14 @@ def validate_string(val): raise TypeError("Not a string: %s" % val) return val.strip() +def validate_string_to_list(val): + val = validate_string(val) + + if not val: + return [] + + return [v.strip() for v in val.split(",") if v] + def validate_class(val): if inspect.isfunction(val) or inspect.ismethod(val): val = val() @@ -682,6 +690,17 @@ class XForwardedFor(Setting): address of the client connection to gunicorn via a proxy. """ +class ForwardedAllowIPS(Setting): + name = "forwarded_allow_ips" + section = "Server Mechanics" + meta = "STRING" + validator = validate_string_to_list + default = "127.0.0.1" + desc = """\ + Front-end's IPs from which allowed to handle X-Forwarded-* headers. + (comma separate). + """ + class AccessLog(Setting): name = "accesslog" section = "Logging" diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index 0fe76c7d..325f5f50 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -66,22 +66,24 @@ def create(req, sock, client, server, cfg): # authors should be aware that REMOTE_HOST and REMOTE_ADDR # may not qualify the remote addr: # http://www.ietf.org/rfc/rfc3875 - client = client or "127.0.0.1" - forward = client + forward = client or "127.0.0.1" url_scheme = "http" script_name = os.environ.get("SCRIPT_NAME", "") secure_headers = cfg.secure_scheme_headers x_forwarded_for_header = cfg.x_forwarded_for_header + if client and client[0] not in cfg.forwarded_allow_ips: + x_forwarded_for_header = None + secure_headers = {} for hdr_name, hdr_value in req.headers: if hdr_name == "EXPECT": # handle expect if hdr_value.lower() == "100-continue": sock.send("HTTP/1.1 100 Continue\r\n\r\n") - elif hdr_name == x_forwarded_for_header: + elif x_forwarded_for_header and hdr_name == x_forwarded_for_header: forward = hdr_value - elif (hdr_name.upper() in secure_headers and + elif secure_headers and (hdr_name.upper() in secure_headers and hdr_value == secure_headers[hdr_name.upper()]): url_scheme = "https" elif hdr_name == "HOST": diff --git a/tests/003-test-config.py b/tests/003-test-config.py index 4d9ef923..50fb13bc 100644 --- a/tests/003-test-config.py +++ b/tests/003-test-config.py @@ -56,7 +56,8 @@ class NoConfigApp(Application): def test_defaults(): c = config.Config() for s in config.KNOWN_SETTINGS: - t.eq(s.default, c.settings[s.name].get()) + t.eq(c.settings[s.name].validator(s.default), + c.settings[s.name].get()) def test_property_access(): c = config.Config() @@ -129,6 +130,17 @@ def test_str_validation(): t.eq(c.proc_name, "foo") t.raises(TypeError, c.set, "proc_name", 2) +def test_str_to_list_validation(): + c = config.Config() + t.eq(c.forwarded_allow_ips, ["127.0.0.1"]) + c.set("forwarded_allow_ips", "127.0.0.1,192.168.0.1") + t.eq(c.forwarded_allow_ips, ["127.0.0.1", "192.168.0.1"]) + c.set("forwarded_allow_ips", "") + t.eq(c.forwarded_allow_ips, []) + c.set("forwarded_allow_ips", None) + t.eq(c.forwarded_allow_ips, []) + t.raises(TypeError, c.set, "forwarded_allow_ips", 1) + def test_callable_validation(): c = config.Config() def func(a, b): @@ -153,7 +165,8 @@ def test_app_config(): with AltArgs(): app = NoConfigApp() for s in config.KNOWN_SETTINGS: - t.eq(s.default, app.cfg.settings[s.name].get()) + t.eq(app.cfg.settings[s.name].validator(s.default), + app.cfg.settings[s.name].get()) def test_load_config(): with AltArgs(["prog_name", "-c", cfg_file()]):