mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-09 10:22:01 +00:00
fix(mcp): warn once when use_x_forwarded_for is on but no trusted ranges
Greptile P1: deployments that today have ``use_x_forwarded_for: true`` but never configured ``mcp_trusted_proxy_ranges`` would silently see their MCP OAuth discovery URLs revert to the proxy's literal bind address after this change, with no log line explaining why. Emit a one-shot WARNING the first time the gate denies for that specific reason, telling the operator exactly which setting to add. The warning is module-scoped (not per-request) so the proxy log stays quiet after the first hit.
This commit is contained in:
@@ -13,6 +13,10 @@ from fastapi import Request
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.auth.auth_utils import _get_request_ip_address
|
||||
|
||||
# One-shot warning so operators upgrading from the prior "always trust X-Forwarded-*"
|
||||
# behaviour see an actionable message in their logs the first time it triggers.
|
||||
_warned_xff_without_trusted_ranges = False
|
||||
|
||||
|
||||
class IPAddressUtils:
|
||||
"""Static utilities for IP-based MCP access control."""
|
||||
@@ -144,6 +148,17 @@ class IPAddressUtils:
|
||||
|
||||
trusted_ranges = general_settings.get("mcp_trusted_proxy_ranges")
|
||||
if not trusted_ranges:
|
||||
global _warned_xff_without_trusted_ranges
|
||||
if not _warned_xff_without_trusted_ranges:
|
||||
verbose_proxy_logger.warning(
|
||||
"use_x_forwarded_for is enabled but mcp_trusted_proxy_ranges "
|
||||
"is not configured. X-Forwarded-* headers will NOT be "
|
||||
"trusted, so MCP OAuth discovery URLs will use the proxy's "
|
||||
"literal base URL. Set mcp_trusted_proxy_ranges in "
|
||||
"general_settings to your reverse-proxy CIDR(s) to allow "
|
||||
"X-Forwarded-* through."
|
||||
)
|
||||
_warned_xff_without_trusted_ranges = True
|
||||
return False
|
||||
|
||||
direct_ip = request.client.host if request.client else None
|
||||
|
||||
@@ -1222,6 +1222,51 @@ def test_get_request_base_url_xff_trust_gate(
|
||||
assert result == "http://localhost:4000"
|
||||
|
||||
|
||||
def test_xff_misconfig_warning_emitted_once(caplog):
|
||||
"""Operators upgrading from the old "always trust X-Forwarded-*" behaviour
|
||||
get a one-shot warning when they have ``use_x_forwarded_for`` enabled
|
||||
but no ``mcp_trusted_proxy_ranges`` configured. The warning must NOT
|
||||
spam every request."""
|
||||
try:
|
||||
from fastapi import Request
|
||||
|
||||
from litellm.proxy import auth as proxy_auth_pkg # noqa: F401
|
||||
from litellm.proxy._experimental.mcp_server.discoverable_endpoints import (
|
||||
get_request_base_url,
|
||||
)
|
||||
from litellm.proxy.auth import ip_address_utils
|
||||
except ImportError:
|
||||
pytest.skip("MCP discoverable endpoints not available")
|
||||
|
||||
# Reset the module-level one-shot flag so the test is deterministic.
|
||||
ip_address_utils._warned_xff_without_trusted_ranges = False
|
||||
|
||||
mock_request = MagicMock(spec=Request)
|
||||
mock_request.base_url = "http://localhost:4000/"
|
||||
mock_request.client = MagicMock()
|
||||
mock_request.client.host = "203.0.113.5"
|
||||
headers = {"X-Forwarded-Host": "attacker.example.com"}
|
||||
mock_request.headers.get = lambda name, default=None: headers.get(name, default)
|
||||
|
||||
misconfig = {"use_x_forwarded_for": True}
|
||||
|
||||
import logging
|
||||
|
||||
with (
|
||||
caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"),
|
||||
patch("litellm.proxy.proxy_server.general_settings", misconfig, create=True),
|
||||
):
|
||||
for _ in range(3):
|
||||
get_request_base_url(mock_request)
|
||||
|
||||
matching = [
|
||||
rec for rec in caplog.records if "mcp_trusted_proxy_ranges" in rec.getMessage()
|
||||
]
|
||||
assert (
|
||||
len(matching) == 1
|
||||
), f"expected exactly one warning, got {len(matching)}: {[r.getMessage() for r in matching]}"
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Tests for scopes_supported when mcp_server.scopes is None
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user