""" This is a file for the AWS Secret Manager Integration Relevant issue: https://github.com/BerriAI/litellm/issues/1883 Requires: * `os.environ["AWS_REGION_NAME"], * `pip install boto3>=1.28.57` """ import ast import base64 import os import re from typing import Any, Dict, Optional import litellm from litellm.proxy._types import KeyManagementSystem def validate_environment(): if "AWS_REGION_NAME" not in os.environ: raise ValueError("Missing required environment variable - AWS_REGION_NAME") def load_aws_kms(use_aws_kms: Optional[bool]): if use_aws_kms is None or use_aws_kms is False: return try: import boto3 validate_environment() # Create a Secrets Manager client kms_client = boto3.client("kms", region_name=os.getenv("AWS_REGION_NAME")) litellm.secret_manager_client = kms_client litellm._key_management_system = KeyManagementSystem.AWS_KMS except Exception as e: raise e class AWSKeyManagementService_V2: """ V2 Clean Class for decrypting keys from AWS KeyManagementService """ def __init__(self) -> None: self.validate_environment() self.kms_client = self.load_aws_kms(use_aws_kms=True) def validate_environment( self, ): if "AWS_REGION_NAME" not in os.environ: raise ValueError("Missing required environment variable - AWS_REGION_NAME") ## CHECK IF LICENSE IN ENV ## - premium feature is_litellm_license_in_env: bool = False if os.getenv("LITELLM_LICENSE", None) is not None: is_litellm_license_in_env = True elif os.getenv("LITELLM_SECRET_AWS_KMS_LITELLM_LICENSE", None) is not None: is_litellm_license_in_env = True if is_litellm_license_in_env is False: raise ValueError( "AWSKeyManagementService V2 is an Enterprise Feature. Please add a valid LITELLM_LICENSE to your envionment." ) def load_aws_kms(self, use_aws_kms: Optional[bool]): if use_aws_kms is None or use_aws_kms is False: return try: import boto3 validate_environment() # Create a Secrets Manager client kms_client = boto3.client("kms", region_name=os.getenv("AWS_REGION_NAME")) return kms_client except Exception as e: raise e def decrypt_value(self, secret_name: str) -> Any: if self.kms_client is None: raise ValueError("kms_client is None") encrypted_value = os.getenv(secret_name, None) if encrypted_value is None: raise Exception( "AWS KMS - Encrypted Value of Key={} is None".format(secret_name) ) if isinstance(encrypted_value, str) and encrypted_value.startswith("aws_kms/"): encrypted_value = encrypted_value.replace("aws_kms/", "") # Decode the base64 encoded ciphertext ciphertext_blob = base64.b64decode(encrypted_value) # Set up the parameters for the decrypt call params = {"CiphertextBlob": ciphertext_blob} # Perform the decryption response = self.kms_client.decrypt(**params) # Extract and decode the plaintext plaintext = response["Plaintext"] secret = plaintext.decode("utf-8") if isinstance(secret, str): secret = secret.strip() try: secret_value_as_bool = ast.literal_eval(secret) if isinstance(secret_value_as_bool, bool): return secret_value_as_bool except Exception: pass return secret """ - look for all values in the env with `aws_kms/` - decrypt keys - rewrite env var with decrypted key (). Note: this environment variable will only be available to the current process and any child processes spawned from it. Once the Python script ends, the environment variable will not persist. """ def decrypt_env_var() -> Dict[str, Any]: # setup client class aws_kms = AWSKeyManagementService_V2() # iterate through env - for `aws_kms/` new_values = {} for k, v in os.environ.items(): if ( k is not None and isinstance(k, str) and k.lower().startswith("litellm_secret_aws_kms") ) or (v is not None and isinstance(v, str) and v.startswith("aws_kms/")): decrypted_value = aws_kms.decrypt_value(secret_name=k) # reset env var k = re.sub("litellm_secret_aws_kms_", "", k, flags=re.IGNORECASE) new_values[k] = decrypted_value return new_values