Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion litellm/proxy/proxy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,13 @@ async def proxy_startup_event(app: FastAPI): # noqa: PLR0915
premium_user = _license_check.is_premium()

## CHECK MASTER KEY IN ENVIRONMENT ##
master_key = get_secret_str("LITELLM_MASTER_KEY")
# Only overwrite master_key from env var if it is actually set.
# initialize() may have already configured master_key from a config file;
# unconditionally assigning None here would discard that value when the
# LITELLM_MASTER_KEY env var is absent (e.g. programmatic startup, tests).
_env_master_key = get_secret_str("LITELLM_MASTER_KEY")
if _env_master_key is not None:
master_key = _env_master_key
Comment thread
giulio-leone marked this conversation as resolved.
### LOAD CONFIG ###
worker_config: Optional[Union[str, dict]] = get_secret("WORKER_CONFIG") # type: ignore
env_config_yaml: Optional[str] = get_secret_str("CONFIG_FILE_PATH")
Expand Down
49 changes: 49 additions & 0 deletions tests/test_litellm/proxy/test_preserve_master_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Regression test for #22330:
A master_key set by initialize() (via config file) must be preserved when
LITELLM_MASTER_KEY is not set as an environment variable.
"""

from unittest.mock import AsyncMock, patch

import pytest

from litellm.proxy import proxy_server


@pytest.mark.asyncio
async def test_master_key_preserved_when_env_var_absent():
"""proxy_startup_event must NOT overwrite a config-provided master_key."""
config_key = "sk-from-config-file-1234"

# Simulate initialize() having set master_key from a config file
original_master_key = proxy_server.master_key
proxy_server.master_key = config_key

try:
with patch(
"litellm.proxy.proxy_server.get_secret_str", return_value=None
), patch(
"litellm.proxy.proxy_server.get_secret", return_value=None
), patch(
"litellm.proxy.proxy_server.init_verbose_loggers"
), patch(
"litellm.proxy.proxy_server._license_check"
):
# proxy_startup_event is an @asynccontextmanager, so we must
# enter it with `async with` — a bare `await` would only create
# the generator object without executing the function body.
try:
async with proxy_server.proxy_startup_event(app=AsyncMock()):
pass
except Exception:
# Startup will fail on DB/router setup — we only care about
# the master_key guard at the top of the function.
pass
Comment thread
giulio-leone marked this conversation as resolved.

assert proxy_server.master_key == config_key, (
f"master_key was overwritten: expected {config_key!r}, "
f"got {proxy_server.master_key!r}"
)
finally:
proxy_server.master_key = original_master_key
Comment thread
giulio-leone marked this conversation as resolved.
Loading