Phusion Passenger WSGI Force HTTPS

107 views
Skip to first unread message

i...@adharmonics.com

unread,
Apr 10, 2017, 7:37:43 PM4/10/17
to Phusion Passenger Discussions
Hi,

I'm running a Python application with nginx + Phusion Passenger behind an AWS ELB. This ELB listens on port 443 for HTTPS requests and forwards to the instance running nginx, listening on port 80. The issue is that my application uses the wsgi.url_scheme attribute to determine whether links that the app generates should be prefixed with "http://" or "https://". The application incorrectly believes that it is being run in an "http://" context since the ELB strips TLS before forwarding to the application server.

On other application servers that integrate with nginx (e.g. uwsgi, gunicorn), I can usually set some config file that forces the application server to set wsgi.url_scheme to https and override the default automatic detection. gunicorn will actually detect this based on the following headers:

{'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
[source]

Setting "passenger_pass_header X-Forwarded-Protocol" etc. would not work because it's actually Phusion Passenger's responsibility to set the wsgi.url_scheme before forwarding to the application. The WSGI application ignores these headers for the most part. Also, I'm not even sure that these headers would be set correctly since nginx would have to forward them from the ELB.

My workaround (based on this StackOverflow answer) is setting my passenger_wsgi.py file to look like this:

"""WSGI application for Phusion Passenger."""

from my_app_package import app


def _force_https(app):
    def wrapper(environ, start_response):
        environ['wsgi.url_scheme'] = 'https'
        return app(environ, start_response)
    return wrapper


application = _force_https(app)


Basically, I've hacked around Phusion Passenger not correctly setting "wsgi.url_scheme" based on my config by just setting it before making a call.

Is there a better solution?

Thanks,
Ian

Daniel Knoppel

unread,
Apr 12, 2017, 6:24:02 AM4/12/17
to Phusion Passenger Discussions
The incoming connection for Passenger is HTTP, so it's setting "wsgi.url_scheme" correctly to the best of its knowledge.

I'd say the better solution would be to not generate absolute URLs but relative ones instead, and avoid the issue altogether. The other options are hacking your app (as you did) or hacking Passenger. In both cases it would be a hack because neither can actually verify if the client is using HTTP or HTTPS.

I remember a patch request to have an override in Passenger (https://github.com/phusion/passenger/issues/1827) but we can't accept it in the present form due to its vulnerability to malicious clients. In the GUnicorn doc they also warn about this vulnerability (but I think at least there you can turn it off if you're not using it, so at least that would have to be added in the PR as well).

- Daniel

i...@adharmonics.com

unread,
Apr 14, 2017, 5:38:39 PM4/14/17
to Phusion Passenger Discussions
Thanks for the suggestions!

Unfortunately, I need to generate an absolute URL because it's being passed to Google as a callback for OAuth.

Would you accept a patch request that adds a configuration for "passenger_wsgi_url_scheme_http_header" and its associated value (similar to gunicorn) if the default behavior was to do nothing (i.e. no decision on setting wsgi.url_scheme based on forwarded headers by default)?

From the gunicorn doc warning you cited:

>It is important that your front-end proxy configuration ensures that the headers defined here can not be passed directly from the client.

The ELB docs seem to indicate that it will set the X-Forwarded-Proto header regardless of any malicious clients, but I might have to do some testing to validate that. If this is true and we can trust the ELB, then I think we have a valid use case for configuring Passenger (without hacking) to set the wsgi.url_scheme based on the header.

Can't think of a more elegant solution right now, but the user story is pretty specific. I guess I'll stick with the hack for now, and I would rather hack the application than Passenger itself. Thanks for the suggestion!

Please let me know what you think of my proposal for a new config option (at least for the nginx + Python stack specifically.)

Best,
Ian

Camden Narzt

unread,
Apr 18, 2017, 9:06:32 AM4/18/17
to Phusion Passenger Discussions
A link url without a schema will inherit the scheme from the page it appears on.

So on an https page a link to //twitter.com will go to https://twitter.com and on an http page it will go to http://twitter.com, since the page is being served as https b/c of the load-balancer that could work for you. See http://stackoverflow.com/questions/4831741/can-i-change-all-my-http-links-to-just

i...@adharmonics.com

unread,
Apr 18, 2017, 10:23:31 AM4/18/17
to Phusion Passenger Discussions


Thanks for the suggestion. Google's developer console requires that you provide a full URI, including the protocol. See image:








Also, further research seems to indicate that I should always generate HTTPS URLs where possible instead of using protocol-relative URLs.

Will keep digging around for other solutions.

Thanks,
Ian
Reply all
Reply to author
Forward
0 new messages