fix(proxy): resolve the login rate limit kill switch once per process

Reading LITELLM_DISABLE_LOGIN_RATE_LIMIT through get_secret_bool on every
unauthenticated sign-in attempt meant a hosted secret manager in read mode
was queried once per password guess, before any counter was checked

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-11 08:34:11 +00:00
parent 82902e83c2
commit ec9a926e84
2 changed files with 32 additions and 1 deletions

View file

@ -76,6 +76,12 @@ def warn_login_counters_are_per_worker(num_workers: str) -> None:
)
@cache
def _rate_limit_disabled() -> bool:
"""Resolved once per process so an unauthenticated flood never reaches the secret manager."""
return bool(get_secret_bool("LITELLM_DISABLE_LOGIN_RATE_LIMIT", False))
async def _sleep(seconds: float) -> None:
"""The wait a rejected sign-in is held for. Replaced in tests so the suite pays no wall clock."""
await asyncio.sleep(seconds)
@ -161,7 +167,7 @@ class LoginThrottle:
username_cache=_FAILED_LOGIN_USERNAME_CACHE,
source_cache=_FAILED_LOGIN_SOURCE_CACHE,
redis_cache=redis_usage_cache,
enabled=not get_secret_bool("LITELLM_DISABLE_LOGIN_RATE_LIMIT", False),
enabled=not _rate_limit_disabled(),
)
@staticmethod

View file

@ -1267,6 +1267,31 @@ def test_settings_that_arrive_as_environment_strings_are_honored(monkeypatch):
assert throttle.window_seconds == 900, "garbage still falls back to the default"
def test_the_disable_flag_is_read_once_not_per_login_attempt(monkeypatch):
"""Regression: the kill switch was read through the secret manager on every unauthenticated request.
With a hosted secret manager in read mode that is a synchronous network call per guess, so a
flood of wrong passwords could exhaust the secret manager even after the source was refused.
"""
from litellm.proxy import proxy_server as ps
from litellm.proxy.auth import login_throttle
reads: Final[list[str]] = [] # mutable-ok: test-only call recorder
monkeypatch.setattr(login_throttle, "get_secret_bool", lambda name, default: reads.append(name) or default)
login_throttle._rate_limit_disabled.cache_clear()
monkeypatch.setattr(ps, "general_settings", {})
request = MagicMock()
request.headers = {}
request.client = MagicMock()
request.client.host = "1.2.3.4"
for _ in range(50):
assert login_throttle.LoginThrottle.from_request(request).enabled is True
login_throttle._rate_limit_disabled.cache_clear()
assert reads == ["LITELLM_DISABLE_LOGIN_RATE_LIMIT"]
def test_a_negative_or_boolean_setting_falls_back_to_the_default(monkeypatch):
"""A limit below one would refuse everyone; a bool is a typo, not a count."""
from litellm.proxy import proxy_server as ps