From d2a1a11b83cb8cc7ebb89b14d24332fc7be6a874 Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: Fri, 29 May 2026 05:09:42 +0000 Subject: [PATCH] Replace custom reverse proxy middleware with werkzeug ProxyFix The custom middleware duplicated the Host header behind multi-layer proxies, which werkzeug 3.1.7+ rejects with a 400 error. Fixes #366 JIRA: RHELWF-14040 Assisted-by: Claude Code (claude-opus-4-6) --- resultsdb/__init__.py | 5 ++-- resultsdb/proxy.py | 60 ------------------------------------------- tests/test_app.py | 14 ++++++++++ 3 files changed, 16 insertions(+), 63 deletions(-) delete mode 100644 resultsdb/proxy.py diff --git a/resultsdb/__init__.py b/resultsdb/__init__.py index b4c316c..9c54343 100644 --- a/resultsdb/__init__.py +++ b/resultsdb/__init__.py @@ -34,6 +34,7 @@ ) from flask_pyoidc.user_session import UserSession from flask_session import Session +from werkzeug.middleware.proxy_fix import ProxyFix from resultsdb.controllers.api_v2 import api as api_v2 from resultsdb.controllers.api_v3 import api as api_v3 @@ -41,7 +42,6 @@ from resultsdb.controllers.main import main from resultsdb.messaging import load_messaging_plugin from resultsdb.models import db -from resultsdb.proxy import ReverseProxied from resultsdb.tracing import setup_tracing from . import config @@ -56,8 +56,7 @@ def create_app(config_obj=None): app = Flask(__name__) app.secret_key = "replace-me-with-something-random" # nosec # NOSONAR - # make sure app behaves when behind a proxy - app.wsgi_app = ReverseProxied(app.wsgi_app) + app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1) # Expose the __version__ variable in templates app.jinja_env.globals["app_version"] = __version__ diff --git a/resultsdb/proxy.py b/resultsdb/proxy.py deleted file mode 100644 index 81c9efc..0000000 --- a/resultsdb/proxy.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2009-2014, Red Hat, Inc. -# License: GPL-2.0+ - -""" -Makes fedocal an application behind a reverse proxy and thus ensure the -redirects are using ``https``. - -Original Source: https://flask.pocoo.org/snippets/35/ by Peter Hansen -Source: https://github.com/fedora-infra/fedocal/blob/master/fedocal/proxy.py -""" - - -class ReverseProxied: - """Wrap the application in this middleware and configure the - front-end server to add these headers, to let you quietly bind - this to a URL other than / and to an HTTP scheme that is - different than what is used locally. - - In apache: - RewriteEngine On - - - ProxyPass https://192.168.0.1:5001/ - ProxyPassReverse https://192.168.0.1:5001/ - RequestHeader set X-Forwarded-Scheme $scheme - RequestHeader add X-Script-Name /myprefix/ - - - In nginx: - location /myprefix { - proxy_pass https://192.168.0.1:5001; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Script-Name /myprefix; - } - - :param app: the WSGI application - """ - - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - script_name = environ.get("HTTP_X_SCRIPT_NAME", "") - if script_name: - environ["SCRIPT_NAME"] = script_name - path_info = environ["PATH_INFO"] - if path_info.startswith(script_name): - prefix_len = len(script_name) - environ["PATH_INFO"] = path_info[prefix_len:] - - server = environ.get("HTTP_X_FORWARDED_HOST", "") - if server: - environ["HTTP_HOST"] = server - - scheme = environ.get("HTTP_X_FORWARDED_SCHEME", "") - if scheme: - environ["wsgi.url_scheme"] = scheme - return self.app(environ, start_response) diff --git a/tests/test_app.py b/tests/test_app.py index 5103e12..3197f7a 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,10 +1,24 @@ from unittest.mock import Mock from pytest import raises +from werkzeug.test import EnvironBuilder from resultsdb import setup_messaging +def test_proxy_fix_extracts_single_host(app): + """Regression test for https://github.com/release-engineering/resultsdb/issues/366""" + builder = EnvironBuilder(method="GET", path="/api/v2.0/") + env = builder.get_environ() + env["HTTP_X_FORWARDED_HOST"] = "resultsdb.example.com, resultsdb.example.com" + + responses = [] + app.wsgi_app(env, lambda status, headers: responses.append(status)) + + assert responses == ["300 MULTIPLE CHOICES"] + assert env["HTTP_HOST"] == "resultsdb.example.com" + + def test_app_messaging(app): assert app.messaging_plugin is not None assert type(app.messaging_plugin).__name__ == "DummyPlugin"