diff --git a/docs/my-website/docs/secret_managers/cyberark.md b/docs/my-website/docs/secret_managers/cyberark.md index 37aa108669..c33aa28670 100644 --- a/docs/my-website/docs/secret_managers/cyberark.md +++ b/docs/my-website/docs/secret_managers/cyberark.md @@ -41,6 +41,7 @@ CYBERARK_CLIENT_KEY="path/to/client.key" # OPTIONAL CYBERARK_REFRESH_INTERVAL="300" # defaults to 300 seconds (5 minutes), frequency of token refresh +CYBERARK_SSL_VERIFY="true" # defaults to true, set to "false" to disable SSL verification (for self-signed certificates) ``` **Step 2.** Add to proxy config.yaml @@ -172,6 +173,24 @@ If these commands work successfully against your CyberArk instance, then CyberAr - The `CYBERARK_API_BASE` URL is accessible from your LiteLLM instance - Your API key or certificates have the necessary permissions in CyberArk +### SSL Certificate Errors + +If you encounter SSL certificate verification errors like: + +``` +RuntimeError: Could not authenticate to CyberArk Conjur: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain +``` + +This typically occurs when your CyberArk Conjur instance uses a self-signed certificate. You can disable SSL verification by setting: + +```bash +CYBERARK_SSL_VERIFY="false" +``` + +:::warning +Disabling SSL verification is insecure and should only be used for testing or development environments with self-signed certificates. For production, configure your certificate chain properly or use certificate-based authentication with `CYBERARK_CLIENT_CERT` and `CYBERARK_CLIENT_KEY`. +::: + ## Video Walkthrough This video walks through using CyberArk Conjur as a secret manager with LiteLLM. We create a virtual key in the LiteLLM Admin UI and verify it exists in CyberArk. Then we rotate the secret key and verify it exists in CyberArk. diff --git a/litellm/secret_managers/cyberark_secret_manager.py b/litellm/secret_managers/cyberark_secret_manager.py index 5f1e8b5cd0..2745df0077 100644 --- a/litellm/secret_managers/cyberark_secret_manager.py +++ b/litellm/secret_managers/cyberark_secret_manager.py @@ -16,6 +16,7 @@ from litellm.llms.custom_httpx.http_handler import ( from litellm.proxy._types import KeyManagementSystem from .base_secret_manager import BaseSecretManager +from .main import str_to_bool class CyberArkSecretManager(BaseSecretManager): @@ -32,6 +33,11 @@ class CyberArkSecretManager(BaseSecretManager): self.tls_cert_path = os.getenv("CYBERARK_CLIENT_CERT", "") self.tls_key_path = os.getenv("CYBERARK_CLIENT_KEY", "") + # SSL verification - can be disabled for self-signed certificates + # Set CYBERARK_SSL_VERIFY=false to disable SSL verification + ssl_verify_env = str_to_bool(os.getenv("CYBERARK_SSL_VERIFY")) + self.ssl_verify: bool = ssl_verify_env if ssl_verify_env is not None else True + # Validate environment if not self.conjur_api_key and not ( self.tls_cert_path and self.tls_key_path @@ -52,6 +58,11 @@ class CyberArkSecretManager(BaseSecretManager): f"CyberArk secret manager is only available for premium users. {CommonProxyErrors.not_premium_user.value}" ) + if not self.ssl_verify: + verbose_logger.warning( + "CyberArk SSL verification is disabled. This is insecure and should only be used for testing with self-signed certificates." + ) + def _authenticate(self) -> str: """ Authenticate with CyberArk Conjur and get a session token. @@ -71,13 +82,16 @@ class CyberArkSecretManager(BaseSecretManager): try: if self.tls_cert_path and self.tls_key_path: - # Certificate-based authentication - http_client = httpx.Client(cert=(self.tls_cert_path, self.tls_key_path)) + # Certificate-based authentication - need custom client for cert + http_client = httpx.Client( + cert=(self.tls_cert_path, self.tls_key_path), + verify=self.ssl_verify, + ) resp = http_client.post(auth_url, content=self.conjur_api_key) else: # API key authentication - http_handler = _get_httpx_client() - resp = http_handler.post(auth_url, content=self.conjur_api_key) + http_handler = _get_httpx_client(params={"ssl_verify": self.ssl_verify}) + resp = http_handler.client.post(auth_url, content=self.conjur_api_key) resp.raise_for_status() @@ -117,8 +131,8 @@ class CyberArkSecretManager(BaseSecretManager): policy_yaml = f"- !variable {secret_name}\n" try: - client = _get_httpx_client() - resp = client.post( + client = _get_httpx_client(params={"ssl_verify": self.ssl_verify}) + resp = client.client.post( policy_url, headers={ **self._get_request_headers(), @@ -180,6 +194,7 @@ class CyberArkSecretManager(BaseSecretManager): async_client = get_async_httpx_client( llm_provider=httpxSpecialProvider.SecretManager, + params={"ssl_verify": self.ssl_verify}, ) try: @@ -227,11 +242,11 @@ class CyberArkSecretManager(BaseSecretManager): if self.cache.get_cache(secret_name) is not None: return self.cache.get_cache(secret_name) - sync_client = _get_httpx_client() + sync_client = _get_httpx_client(params={"ssl_verify": self.ssl_verify}) try: url = self.get_url(secret_name) - response = sync_client.get(url, headers=self._get_request_headers()) + response = sync_client.client.get(url, headers=self._get_request_headers()) response.raise_for_status() # CyberArk Conjur returns the raw secret value as text @@ -278,7 +293,7 @@ class CyberArkSecretManager(BaseSecretManager): """ async_client = get_async_httpx_client( llm_provider=httpxSpecialProvider.SecretManager, - params={"timeout": timeout}, + params={"ssl_verify": self.ssl_verify}, ) try: