From c4c0fa033aeb08646008e4969519619178d4a12b Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Fri, 13 Feb 2015 16:26:17 +1100 Subject: [PATCH] Add "force_wsgi_environ" config option. This is the nuclear option for when your reverse proxy setup doesn't place nicely with our request-signing thing - it causes the app to unilaterally clobber its WSGI environment with values from public_url. --- syncserver.ini | 10 +++++++++- syncserver/__init__.py | 35 +++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/syncserver.ini b/syncserver.ini index 63cb2d5..aa7105f 100644 --- a/syncserver.ini +++ b/syncserver.ini @@ -27,8 +27,16 @@ public_url = http://localhost:5000/ # Only request by existing accounts will be honoured. # allow_new_users = false +# Set this to "true" to work around a mismatch between public_url and +# the application URL as seen by python, which can happen in certain reverse- +# proxy hosting setups. It will overwrite the WSGI environ dict with the +# details from public_url. This could have security implications if e.g. +# you tell the app that it's on HTTPS but it's really on HTTP, so it should +# only be used as a last resort and after careful checking of server config. +force_wsgi_environ = false + # Uncomment and edit the following to use a local BrowserID verifier -# rather than posing assertions to the mozilla-hosted verifier. +# rather than posting assertions to the mozilla-hosted verifier. # Audiences should be set to your public_url without a trailing slash. #[browserid] #backend = tokenserver.verifiers.LocalVerifier diff --git a/syncserver/__init__.py b/syncserver/__init__.py index 170cd4a..696a430 100644 --- a/syncserver/__init__.py +++ b/syncserver/__init__.py @@ -117,24 +117,27 @@ def reconcile_wsgi_environ_with_public_url(event): # is serving us at some sub-path. if not request.script_name: request.script_name = p_public_url.path.rstrip("/") - # If the public_url claims we're on a non-standard port but the environ - # says we're on a standard port, assume the public_url is correct. - # This is often the case with e.g. apache mod_wsgi. - if p_public_url.port not in (None, 80, 443): - port_str = str(p_public_url.port) - if request.host_port != port_str: - if request.host_port in (None, "80", "443"): - request.host = p_public_url.netloc - # Log a noisy error if the application url is different to what we'd - # expect based on public_url setting. + # If the environ does not match public_url, requests are almost certainly + # going to fail due to auth errors. We can either bail out early, or we + # can forcibly clobber the WSGI environ with the values from public_url. + # This is a security risk if you've e.g. mis-configured the server, so + # it's not enabled by default. application_url = request.application_url if public_url != application_url: - msg = "The public_url setting does not match the application url.\n" - msg += "This will almost certainly cause authentication failures!\n" - msg += " public_url setting is: %s\n" % (public_url,) - msg += " application url is: %s\n" % (application_url,) - logger.error(msg) - raise _JSONError([msg], status_code=500) + if not request.registry.settings.get("syncserver.force_wsgi_environ"): + msg = "\n".join(( + "The public_url setting doesn't match the application url.", + "This will almost certainly cause authentication failures!", + " public_url setting is: %s" % (public_url,), + " application url is: %s" % (application_url,), + "You can disable this check by setting the force_wsgi_environ", + "option in your config file, but do so at your own risk.", + )) + logger.error(msg) + raise _JSONError([msg], status_code=500) + request.scheme = p_public_url.scheme + request.host = p_public_url.netloc + request.script_name = p_public_url.path.rstrip("/") def get_configurator(global_config, **settings):