mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix(proxy): reject blank trusted_proxy_ranges entries before they are dropped
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
cacd12b87d
commit
8fc74a78bc
2 changed files with 27 additions and 7 deletions
|
|
@ -35,7 +35,7 @@ from litellm.constants import (
|
|||
LOGIN_THROTTLE_UNKNOWN_SOURCE,
|
||||
)
|
||||
from litellm.proxy._types import ProxyErrorTypes, ProxyException
|
||||
from litellm.proxy.auth.network import TrustedProxyConfig, normalize_cidr_ranges, resolve_client_ip
|
||||
from litellm.proxy.auth.network import TrustedProxyConfig, resolve_client_ip
|
||||
from litellm.secret_managers.main import get_secret_bool
|
||||
|
||||
DEFAULT_MAX_FAILED_LOGIN_ATTEMPTS_PER_SOURCE: Final = 10
|
||||
|
|
@ -54,6 +54,7 @@ TRUSTED_PROXY_RANGES_KEY: Final = "trusted_proxy_ranges"
|
|||
_REDIS_FAILURES: Final = (RedisError, RedisCircuitBreakerOpenError, OSError, asyncio.TimeoutError)
|
||||
_LOCAL_BLOCK_EXPIRY: Final = TypeAdapter[float | None](float | None)
|
||||
_SOURCE_LIMIT_OVERRIDES: Final = TypeAdapter[Mapping[str, object]](Mapping[str, object])
|
||||
_RANGE_ENTRIES: Final = TypeAdapter[tuple[object, ...]](tuple[object, ...])
|
||||
|
||||
Scope: TypeAlias = Literal["user", "source"]
|
||||
|
||||
|
|
@ -134,13 +135,27 @@ def declared_proxy_ranges(settings: Mapping[str, object]) -> tuple[str, ...] | N
|
|||
An unset key, a value that is not a list of ranges, or a list with an entry that is not an address
|
||||
or range leaves it unknown and the source scope off.
|
||||
"""
|
||||
raw_ranges: Final = settings.get(TRUSTED_PROXY_RANGES_KEY)
|
||||
if isinstance(raw_ranges, (list, tuple, set)) and not raw_ranges:
|
||||
return ()
|
||||
cidrs: Final = tuple(normalize_cidr_ranges(raw_ranges, setting_name=TRUSTED_PROXY_RANGES_KEY))
|
||||
if not cidrs or any(_parse_network(cidr, TRUSTED_PROXY_RANGES_KEY) is None for cidr in cidrs):
|
||||
entries: Final = _configured_range_entries(settings.get(TRUSTED_PROXY_RANGES_KEY))
|
||||
if entries is None or any(_parse_network(entry, TRUSTED_PROXY_RANGES_KEY) is None for entry in entries):
|
||||
return None
|
||||
return entries
|
||||
|
||||
|
||||
def _configured_range_entries(raw_ranges: object) -> tuple[str, ...] | None:
|
||||
"""Every configured entry, blanks included, so a stray empty string fails validation like any other typo."""
|
||||
if raw_ranges is None:
|
||||
return None
|
||||
if isinstance(raw_ranges, str):
|
||||
return tuple(part.strip() for part in raw_ranges.split(","))
|
||||
try:
|
||||
return tuple(str(entry).strip() for entry in _RANGE_ENTRIES.validate_python(raw_ranges))
|
||||
except ValidationError:
|
||||
verbose_proxy_logger.warning(
|
||||
"Invalid %s value: expected a list of address ranges, got %s",
|
||||
TRUSTED_PROXY_RANGES_KEY,
|
||||
type(raw_ranges).__name__,
|
||||
)
|
||||
return None
|
||||
return cidrs
|
||||
|
||||
|
||||
def _positive_int(raw: object, key: str, default: int) -> int:
|
||||
|
|
|
|||
|
|
@ -944,7 +944,12 @@ async def test_an_empty_trusted_proxy_ranges_means_the_peer_is_the_client_and_th
|
|||
["not-a-range"],
|
||||
["10.0.0.0/8, 172.16.0.0/12"],
|
||||
["10.0.0.0/8", "10.0.0.0/33"],
|
||||
["10.0.0.0/8", " "],
|
||||
["10.0.0.0/8", ""],
|
||||
["10.0.0.0/8", None],
|
||||
"10.0.0.0/8;172.16.0.0/12",
|
||||
"10.0.0.0/8,",
|
||||
"",
|
||||
],
|
||||
)
|
||||
def test_a_trusted_proxy_ranges_value_that_names_no_ranges_leaves_the_topology_unknown(configured):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue