From 4e18ab2f2f2a4c011b99b9a8e8efa9b4f55d5468 Mon Sep 17 00:00:00 2001 From: mateo Date: Fri, 11 Sep 2026 22:17:08 +0000 Subject: [PATCH] feat(proxy): warn at startup when the master key is the example sk-1234 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/proxy/auth/master_key_policy.py | 15 ++++++++++++ litellm/proxy/proxy_server.py | 5 ++++ .../proxy/auth/test_master_key_policy.py | 24 +++++++++++++++++++ .../proxy/proxy_server/test_lifecycle.py | 20 ++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 litellm/proxy/auth/master_key_policy.py create mode 100644 tests/test_litellm/proxy/auth/test_master_key_policy.py diff --git a/litellm/proxy/auth/master_key_policy.py b/litellm/proxy/auth/master_key_policy.py new file mode 100644 index 00000000000..318b722b0ff --- /dev/null +++ b/litellm/proxy/auth/master_key_policy.py @@ -0,0 +1,15 @@ +from typing import Final + +INSECURE_MASTER_KEYS: Final = frozenset({"sk-1234"}) + + +def insecure_master_key_warning(master_key: str | None) -> str | None: + if master_key not in INSECURE_MASTER_KEYS: + return None + return ( + "LITELLM_MASTER_KEY is set to the example key 'sk-1234' from the docs. " + "Anyone who has read the docs can administer this gateway, and publicly reachable " + "gateways using this key have been compromised. Set a strong random master key " + "(e.g. `python -c \"import secrets; print('sk-' + secrets.token_urlsafe(32))\"`). " + "A future release will refuse to start with this key." + ) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c8fd7edfed6..5aa75e360f2 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -324,6 +324,7 @@ from litellm.proxy.auth.auth_utils import ( from litellm.proxy.auth.fallback_model_access import router_fallback_access_check from litellm.proxy.auth.handle_jwt import JWTHandler from litellm.proxy.auth.litellm_license import AUTO_ROUTER_LICENSE_REMEDY, LicenseCheck +from litellm.proxy.auth.master_key_policy import insecure_master_key_warning from litellm.proxy.auth.model_checks import ( expand_wildcard_deployments_for_model_info, get_all_fallbacks, @@ -1159,6 +1160,10 @@ async def proxy_startup_event(app: FastAPI) -> AsyncGenerator[None, None]: if isinstance(worker_config, dict): await initialize(**worker_config) + _insecure_master_key_warning: Final = insecure_master_key_warning(master_key) + if _insecure_master_key_warning is not None: + verbose_proxy_logger.warning(_insecure_master_key_warning) + # check if DATABASE_URL in environment - load from there if prisma_client is None: _db_url: Final[str | None] = get_secret("DATABASE_URL", None) diff --git a/tests/test_litellm/proxy/auth/test_master_key_policy.py b/tests/test_litellm/proxy/auth/test_master_key_policy.py new file mode 100644 index 00000000000..6c949fc3783 --- /dev/null +++ b/tests/test_litellm/proxy/auth/test_master_key_policy.py @@ -0,0 +1,24 @@ +from litellm.litellm_core_utils.secret_redaction import redact_string +from litellm.proxy.auth.master_key_policy import insecure_master_key_warning + + +def test_insecure_master_key_warning_returned_for_example_key(): + warning = insecure_master_key_warning("sk-1234") + + assert warning is not None + assert "sk-1234" in warning + + +def test_insecure_master_key_warning_none_for_strong_key(): + assert insecure_master_key_warning("sk-strong-random-key") is None + + +def test_insecure_master_key_warning_none_for_none(): + assert insecure_master_key_warning(None) is None + + +def test_insecure_master_key_warning_survives_redaction(): + warning = insecure_master_key_warning("sk-1234") + + assert warning is not None + assert "secrets.token_urlsafe" in redact_string(warning) diff --git a/tests/test_litellm/proxy/proxy_server/test_lifecycle.py b/tests/test_litellm/proxy/proxy_server/test_lifecycle.py index deb7289d2d1..785a0dacdf2 100644 --- a/tests/test_litellm/proxy/proxy_server/test_lifecycle.py +++ b/tests/test_litellm/proxy/proxy_server/test_lifecycle.py @@ -1073,3 +1073,23 @@ async def test_prometheus_fallback_stats_job_runs_when_the_lock_is_free_or_absen await jobs["prometheus_fallback_stats_job"]() assert send_fallback_stats.await_count == 2 + + +@pytest.mark.asyncio +async def test_proxy_startup_event_warns_but_does_not_raise_for_docs_example_master_key(monkeypatch): + """With LITELLM_MASTER_KEY=sk-1234 the lifespan logs a loud warning and keeps booting.""" + monkeypatch.setenv("LITELLM_MASTER_KEY", "sk-1234") + + with patch.object(ps.verbose_proxy_logger, "warning") as mock_warning: + try: + async with proxy_startup_event(app=None): + pass + except ValueError as e: + if "sk-1234" in str(e): + pytest.fail("proxy_startup_event refused to boot on the docs example key") + except Exception: + pass + + assert any("sk-1234" in str(call.args[0]) for call in mock_warning.call_args_list), ( + "startup should log the insecure master key warning" + )