From 59285e67204ce993ab224560c1826d9cf4917d28 Mon Sep 17 00:00:00 2001 From: yucheng Date: Mon, 6 Jul 2026 09:56:15 -0700 Subject: [PATCH 1/2] fix(proxy): resolve os.environ/ refs for all AWS auth params in DB-sourced models PR #30867 removed request-time os.environ/ expansion in BaseAWSLLM.get_credentials to close LIT-3831. That relies on config-load paths pre-resolving os.environ/ refs, but the DB-load path (_resolve_db_litellm_param) only re-expands keys in _DB_LITELLM_PARAM_ENV_REF_KEYS, which covered api_key, aws_access_key_id, and aws_secret_access_key but not the other AWS auth fields. A model stored in Postgres with e.g. aws_role_name: os.environ/BEDROCK_ASSUME_ROLE_ARN lands on the router with the literal string, get_credentials no longer expands it, and STS returns ValidationError: os.environ/BEDROCK_ASSUME_ROLE_ARN is invalid Add the remaining AWS auth params to the allowlist so DB-sourced values resolve at model-load time (trusted, server-side), matching the YAML-config path. Team-scoped DB rows still get resolve_env_refs=False, so the LIT-3831 defense-in-depth path is unchanged and request-body injection is still blocked by _BANNED_REQUEST_BODY_PARAMS. Regression tests pin every added field as an os.environ/ DB value and assert it resolves on the router, plus a team-scoped pin that asserts env refs remain literal. --- litellm/proxy/proxy_server.py | 9 ++ .../proxy/proxy_server/test_proxy_config.py | 93 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index f6e5c118085..b0fff2b63c3 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1125,6 +1125,15 @@ _DB_LITELLM_PARAM_ENV_REF_KEYS = frozenset( "vertex_ai_credentials", "aws_access_key_id", "aws_secret_access_key", + "aws_session_token", + "aws_region_name", + "aws_session_name", + "aws_profile_name", + "aws_role_name", + "aws_web_identity_token", + "aws_sts_endpoint", + "aws_external_id", + "aws_bedrock_runtime_endpoint", } ) diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 7550432fcc0..36c98815d52 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -1148,6 +1148,99 @@ def test_ProxyConfig__resolve_db_litellm_param_skips_non_string_values(monkeypat assert pc._resolve_db_litellm_param(key="tpm", value=100) == 100 +def test_ProxyConfig__add_deployment_resolves_env_refs_for_aws_bedrock_auth_params( + monkeypatch, +): + """Regression: DB-stored Bedrock/SageMaker auth params like + ``aws_role_name: os.environ/BEDROCK_ASSUME_ROLE_ARN`` must resolve at + DB-load time. PR #30867 removed request-time expansion in + ``BaseAWSLLM.get_credentials``; without DB-load resolution the literal + string reaches STS and fails with ``ValidationError: ... is invalid``.""" + aws_env = { + "aws_session_token": ("BEDROCK_SESSION_TOKEN", "resolved-session-token"), + "aws_region_name": ("BEDROCK_REGION", "us-east-1"), + "aws_session_name": ("BEDROCK_SESSION_NAME", "resolved-session"), + "aws_profile_name": ("BEDROCK_PROFILE", "resolved-profile"), + "aws_role_name": ( + "BEDROCK_ASSUME_ROLE_ARN", + "arn:aws:iam::123456789012:role/resolved", + ), + "aws_web_identity_token": ("BEDROCK_WEB_IDENTITY_TOKEN", "resolved-token"), + "aws_sts_endpoint": ( + "BEDROCK_STS_ENDPOINT", + "https://sts.us-east-1.amazonaws.com", + ), + "aws_external_id": ("BEDROCK_EXTERNAL_ID", "resolved-external-id"), + "aws_bedrock_runtime_endpoint": ( + "BEDROCK_RUNTIME_ENDPOINT", + "https://bedrock-runtime.us-east-1.amazonaws.com", + ), + } + for _, (env_name, env_value) in aws_env.items(): + monkeypatch.setenv(env_name, env_value) + monkeypatch.setattr( + "litellm.proxy.proxy_server.decrypt_value_helper", + lambda value, key, return_original_value: value, + ) + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + litellm_params: Dict[str, Any] = {"model": "bedrock/anthropic.claude-v2"} + for key, (env_name, _) in aws_env.items(): + litellm_params[key] = f"os.environ/{env_name}" + db_model = SimpleNamespace( + model_id="model-1", + model_name="bedrock-model", + model_info={"id": "model-1"}, + litellm_params=litellm_params, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model]) + deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] + + assert added == 1 + for key, (_, expected) in aws_env.items(): + assert getattr(deployment.litellm_params, key) == expected, key + + +def test_ProxyConfig__add_deployment_keeps_team_aws_env_refs_literal(monkeypatch): + """Team-scoped DB models must NOT resolve env refs even for AWS auth + params: this is the LIT-3831 defense-in-depth path where a team admin + could otherwise craft a DB entry that reads the process environment.""" + + def fail_on_call(secret_name, *args, **kwargs): + raise AssertionError("team DB models should not resolve env refs") + + monkeypatch.setenv("BEDROCK_ASSUME_ROLE_ARN", "arn:aws:iam::123:role/should-not-leak") + monkeypatch.setattr( + "litellm.proxy.proxy_server.decrypt_value_helper", + lambda value, key, return_original_value: value, + ) + monkeypatch.setattr("litellm.proxy.proxy_server.get_secret", fail_on_call) + fake_router = MagicMock() + fake_router.upsert_deployment = MagicMock(return_value=True) + monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router) + pc = ProxyConfig() + db_model = SimpleNamespace( + model_id="model-1", + model_name="model_name_team-1_bedrock", + model_info={"id": "model-1", "team_id": "team-1"}, + litellm_params={ + "model": "bedrock/anthropic.claude-v2", + "aws_role_name": "os.environ/BEDROCK_ASSUME_ROLE_ARN", + }, + blocked=False, + ) + + added = pc._add_deployment(db_models=[db_model]) + deployment = fake_router.upsert_deployment.call_args.kwargs["deployment"] + + assert added == 1 + assert deployment.litellm_params.aws_role_name == "os.environ/BEDROCK_ASSUME_ROLE_ARN" + + # --------------------------------------------------------------------------- # ProxyConfig.decrypt_model_list_from_db # --------------------------------------------------------------------------- From 29f0b02a83f8d36cfcc508b5de9d9105b3930afd Mon Sep 17 00:00:00 2001 From: yucheng Date: Mon, 6 Jul 2026 10:06:42 -0700 Subject: [PATCH 2/2] fix(proxy): also allow os.environ/ resolution for aws_bedrock_project_id, aws_batch_role_arn, aws_workspace_id Round out the AWS auth-field coverage of _DB_LITELLM_PARAM_ENV_REF_KEYS so every stringy aws_* field a deployment can pin in the DB resolves os.environ/ refs at load time: - aws_bedrock_project_id: Bedrock project/workspace association, banned from request bodies via _BANNED_REQUEST_BODY_PARAMS - aws_batch_role_arn: Bedrock batches role ARN (analog of aws_role_name) - aws_workspace_id: Claude Platform workspace ID Verified against three independent sources: - BaseAWSLLM.aws_authentication_params (all 11) - LiteLLM_Params-declared AWS fields (all 5) - every aws_* string read from litellm_params/kwargs/optional_params across litellm/ (all 14, excluding aws_bedrock_client which is a boto3 client object, not a string, and aws_polly which is a provider name) The regression test now pins all 12 newly-allowlisted fields. --- litellm/proxy/proxy_server.py | 3 +++ tests/test_litellm/proxy/proxy_server/test_proxy_config.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index b0fff2b63c3..1474c15e778 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -1134,6 +1134,9 @@ _DB_LITELLM_PARAM_ENV_REF_KEYS = frozenset( "aws_sts_endpoint", "aws_external_id", "aws_bedrock_runtime_endpoint", + "aws_bedrock_project_id", + "aws_batch_role_arn", + "aws_workspace_id", } ) diff --git a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py index 36c98815d52..9a687addbdd 100644 --- a/tests/test_litellm/proxy/proxy_server/test_proxy_config.py +++ b/tests/test_litellm/proxy/proxy_server/test_proxy_config.py @@ -1175,6 +1175,12 @@ def test_ProxyConfig__add_deployment_resolves_env_refs_for_aws_bedrock_auth_para "BEDROCK_RUNTIME_ENDPOINT", "https://bedrock-runtime.us-east-1.amazonaws.com", ), + "aws_bedrock_project_id": ("BEDROCK_PROJECT_ID", "resolved-project-id"), + "aws_batch_role_arn": ( + "BEDROCK_BATCH_ROLE_ARN", + "arn:aws:iam::123456789012:role/batch", + ), + "aws_workspace_id": ("BEDROCK_WORKSPACE_ID", "resolved-workspace-id"), } for _, (env_name, env_value) in aws_env.items(): monkeypatch.setenv(env_name, env_value)