fix(proxy_server.py): re-encrypt env var on config save + use original value on decrypt error (#15671)

* fix(proxy_server.py): re-encrypt env var on config save + use original value on decrypt error

Closes https://github.com/BerriAI/litellm/issues/14854

Fixes https://github.com/BerriAI/litellm/issues/13406

* docs: email.md

document PROXY_BASE_URL param

* fix(proxy_server.py): pop model list before writing to db
This commit is contained in:
Krish Dholakia 2025-10-18 13:39:25 -07:00 committed by GitHub
parent a91e3f1873
commit c1355e92dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 14 deletions

View File

@ -141,6 +141,7 @@ LiteLLM allows you to customize various aspects of your email notifications. Bel
| Email Signature | `EMAIL_SIGNATURE` | string (HTML) | Standard LiteLLM footer | `"<p>Best regards,<br/>Your Team</p><p><a href='https://your-company.com'>Visit us</a></p>"` | HTML-formatted footer for all emails |
| Invitation Subject | `EMAIL_SUBJECT_INVITATION` | string | "LiteLLM: New User Invitation" | `"Welcome to Your Company!"` | Subject line for invitation emails |
| Key Creation Subject | `EMAIL_SUBJECT_KEY_CREATED` | string | "LiteLLM: API Key Created" | `"Your New API Key is Ready"` | Subject line for key creation emails |
| Proxy Base URL | `PROXY_BASE_URL` | string | http://0.0.0.0:4000 | `"https://proxy.your-company.com"` | Base URL for the LiteLLM Proxy (used in email links) |
## HTML Support in Email Signature
@ -180,6 +181,9 @@ EMAIL_SIGNATURE="<p>Best regards,<br/>Your Company Team</p><p><a href='https://y
# Email Subject Lines
EMAIL_SUBJECT_INVITATION="Welcome to Your Company!" # Subject for invitation emails
EMAIL_SUBJECT_KEY_CREATED="Your API Key is Ready" # Subject for key creation emails
# Proxy Configuration
PROXY_BASE_URL="https://proxy.your-company.com" # Base URL for the LiteLLM Proxy (used in email links)
```
## HTML Support in Email Signature
@ -225,3 +229,17 @@ EMAIL_SUBJECT_KEY_CREATED="Your \{company_name\} API Key"
```
The system will automatically replace `\{event_message\}` and other template variables with their actual values when sending emails.
## FAQ
### Why do I see "http://0.0.0.0:4000" in the email links?
The `PROXY_BASE_URL` environment variable is used to construct email links. If you are using the LiteLLM Proxy in a local environment, you will see "http://0.0.0.0:4000" in the email links.
If you are using the LiteLLM Proxy in a production environment, you will see the actual base URL of the LiteLLM Proxy.
You can set the `PROXY_BASE_URL` environment variable to the actual base URL of the LiteLLM Proxy.
```bash
PROXY_BASE_URL="https://proxy.your-company.com"
```

View File

@ -61,9 +61,12 @@ def decrypt_value_helper(
verbose_proxy_logger.debug(
f"Unable to decrypt value={value} for key: {key}, returning None"
)
verbose_proxy_logger.exception(error_message)
# [Non-Blocking Exception. - this should not block decrypting other values]
return value if return_original_value else None
if return_original_value:
return value
else:
verbose_proxy_logger.exception(error_message)
# [Non-Blocking Exception. - this should not block decrypting other values]
return None
def encrypt_value(value: str, signing_key: str):

View File

@ -262,9 +262,7 @@ from litellm.proxy.management_endpoints.customer_endpoints import (
from litellm.proxy.management_endpoints.internal_user_endpoints import (
router as internal_user_router,
)
from litellm.proxy.management_endpoints.internal_user_endpoints import (
user_update,
)
from litellm.proxy.management_endpoints.internal_user_endpoints import user_update
from litellm.proxy.management_endpoints.key_management_endpoints import (
delete_verification_tokens,
duration_in_seconds,
@ -312,9 +310,7 @@ from litellm.proxy.ocr_endpoints.endpoints import router as ocr_router
from litellm.proxy.openai_files_endpoints.files_endpoints import (
router as openai_files_router,
)
from litellm.proxy.openai_files_endpoints.files_endpoints import (
set_files_config,
)
from litellm.proxy.openai_files_endpoints.files_endpoints import set_files_config
from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
passthrough_endpoint_router,
)
@ -1669,8 +1665,28 @@ class ProxyConfig:
):
# if using - db for config - models are in ModelTable
new_config.pop("model_list", None)
await prisma_client.insert_data(data=new_config, table_name="config")
# Make a copy to avoid mutating the original config
config_to_save = new_config.copy()
# SECURITY: Always encrypt environment_variables before DB write
if (
"environment_variables" in config_to_save
and config_to_save["environment_variables"]
):
# decrypt the environment_variables - in case a caller function has already encrypted the environment_variables
decrypted_env_vars = self._decrypt_and_set_db_env_variables(
environment_variables=config_to_save["environment_variables"],
return_original_value=True,
)
# encrypt the environment_variables,
config_to_save["environment_variables"] = self._encrypt_env_variables(
environment_variables=decrypted_env_vars
)
config_to_save.pop("model_list", None)
await prisma_client.insert_data(data=config_to_save, table_name="config")
else:
# Save the updated config - if user is not using a dB
## YAML
@ -2847,7 +2863,9 @@ class ProxyConfig:
encrypted_env_vars[k] = encrypted_value
return encrypted_env_vars
def _decrypt_and_set_db_env_variables(self, environment_variables: dict) -> dict:
def _decrypt_and_set_db_env_variables(
self, environment_variables: dict, return_original_value: bool = False
) -> dict:
"""
Decrypts a dictionary of environment variables and then sets them in the environment
@ -2858,7 +2876,9 @@ class ProxyConfig:
decrypted_env_vars = {}
for k, v in environment_variables.items():
try:
decrypted_value = decrypt_value_helper(value=v, key=k)
decrypted_value = decrypt_value_helper(
value=v, key=k, return_original_value=return_original_value
)
if decrypted_value is not None:
os.environ[k] = decrypted_value
decrypted_env_vars[k] = decrypted_value
@ -3056,7 +3076,9 @@ class ProxyConfig:
d[k] = v
if param_name == "environment_variables":
decrypted_env_vars = self._decrypt_and_set_db_env_variables(db_param_value)
decrypted_env_vars = self._decrypt_and_set_db_env_variables(
db_param_value, return_original_value=True
)
current_config.setdefault("environment_variables", {}).update(
decrypted_env_vars
)