Pylons / waitress

Waitress - A WSGI server for Python 3
https://docs.pylonsproject.org/projects/waitress/en/latest/
Other
1.44k stars 164 forks source link

[Question] Waitress and Flask (Werkzeug) ProxyFix interaction #436

Closed abceleung closed 5 months ago

abceleung commented 5 months ago

Hi, I have a Flask app behind a NGINX proxy.

app.py:

from flask import Flask,request
from werkzeug.middleware.proxy_fix import ProxyFix

# Application Factory
def create_app():

    app = Flask(__name__)

    # Tell Flask that it is behind a proxy
    app.wsgi_app = ProxyFix(
        app.wsgi_app,
        x_for=1,
        x_proto=1,
        x_host=1,
        x_prefix=1,
    )

    @app.route("/")
    def home():
        return f"Your IP is {request.remote_addr}"

    return app

nginx.conf snippet

    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      localhost.crt;
        ssl_certificate_key  localhost.key;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;

        location / {
            proxy_pass http://127.0.0.1:5000;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header Host $host;
        }

    }

I tried the following:

  1. Launch with python -m flask run, then go to https://192.168.1.100 (IP of my PC). Flask correctly shows my IP.
  2. Launch with waitress-serve --port=5000 --call app:create_app, then go to https://192.168.1.100. Flask shows 127.0.0.1 instead.
  3. Launch with waitress-serve --port=5000 --trusted-proxy=127.0.0.1 --trusted-proxy-headers="x-forwarded-for x-forwarded-host x-forwarded-proto" --call app:create_app, then go to https://192.168.1.100. Flask shows my IP correctly again.

Is this intended behavior? Seems like ProxyFix doesn't do anything when using Waitress. Is it safe to remove ProxyFix from the code?

From Flask docs: Tell Flask it is Behind a Proxy

Note: Windows 10, Python 3.12.2, Flask 3.0.2, Waitress 3.0.0

mmerickel commented 5 months ago

Waitress has very strong support for proxy headers. If you want to use flask’s middleware instead you’ll have to turn off the waitress security features. This means setting clear_untrusted_proxy_headers = False on the waitress server. Alternatively consider configuring waitress instead of using the middleware per the waitress docs.

abceleung commented 5 months ago

Waitress has very strong support for proxy headers. If you want to use flask’s middleware instead you’ll have to turn off the waitress security features. This means setting clear_untrusted_proxy_headers = False on the waitress server. Alternatively consider configuring waitress instead of using the middleware per the waitress docs.

Hi, I would like to choose the path of least resistance. Since ProxyFix and Waitress seems to be doing the same thing, can I safely delete ProxyFix and use Waitress with the long argument list instead?

BTW, should I use Waitress 3.0.0 now? Poetry installed 3.0.0 for me. The docs mark 3.0.0 as (Unreleased)

mmerickel commented 5 months ago

That’s just a bug in the docs that it didn’t get updated as part of the release. With respect to your issue you’ll see 3.0 changes the default for how waitress handles proxy headers which is what I expect is breaking the ProxyFix, going back to my suggestion above to tell waitress to preserve the headers.

I haven’t tried to use waitress with flask personally but I expect you should be able to remove the ProxyFix middleware and then set trusted_proxy_headers and trusted_proxy in waitress. This is how I use waitress behind nginx with Pyramid. A working example is here: https://docs.pylonsproject.org/projects/waitress/en/stable/reverse-proxy.html

abceleung commented 5 months ago

That’s just a bug in the docs that it didn’t get updated as part of the release. With respect to your issue you’ll see 3.0 changes the default for how waitress handles proxy headers which is what I expect is breaking the ProxyFix, going back to my suggestion above to tell waitress to preserve the headers.

I haven’t tried to use waitress with flask personally but I expect you should be able to remove the ProxyFix middleware and then set trusted_proxy_headers and trusted_proxy in waitress. This is how I use waitress behind nginx with Pyramid. A working example is here: https://docs.pylonsproject.org/projects/waitress/en/stable/reverse-proxy.html

Thank you for your answers!

Edit: For future readers: As mmerickel said Waitress 3.0.0 breaks ProxyFix. I tested Waitress 2.x.x and it does not break ProxyFix

digitalresistor commented 4 weeks ago

Just to clarify: Waitress does not break ProxyFix, Waitress protects your application from receiving untrusted and unvalidated proxy headers that may lead to misbehavior unless you explicitly tell Waitress you want to receive those headers and trust them.