From 2c117264a227ceaa0a0594a2d32463cb39ea7802 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 17 Jan 2025 19:27:56 -0800 Subject: [PATCH] [Hashicorp - secret manager] - use vault namespace for tls auth (#7834) * hcorp - use x-vault-namespace * _get_tls_cert_auth_body * HCP_VAULT_CERT_ROLE * test_hashicorp_secret_manager_tls_cert_auth * HCP_VAULT_CERT_ROLE --- docs/my-website/docs/proxy/config_settings.md | 1 + .../hashicorp_secret_manager.py | 18 ++++++++++++++---- tests/secret_manager_tests/test_hashicorp.py | 5 ++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index e9ff11440a..ff85349f3b 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -396,6 +396,7 @@ router_settings: | HCP_VAULT_CLIENT_KEY | Path to client key for [Hashicorp Vault Secret Manager](../secret.md#hashicorp-vault) | HCP_VAULT_NAMESPACE | Namespace for [Hashicorp Vault Secret Manager](../secret.md#hashicorp-vault) | HCP_VAULT_TOKEN | Token for [Hashicorp Vault Secret Manager](../secret.md#hashicorp-vault) +| HCP_VAULT_CERT_ROLE | Role for [Hashicorp Vault Secret Manager Auth](../secret.md#hashicorp-vault) | HELICONE_API_KEY | API key for Helicone service | HOSTNAME | Hostname for the server, this will be [emitted to `datadog` logs](https://docs.litellm.ai/docs/proxy/logging#datadog) | HUGGINGFACE_API_BASE | Base URL for Hugging Face API diff --git a/litellm/secret_managers/hashicorp_secret_manager.py b/litellm/secret_managers/hashicorp_secret_manager.py index bbfbf3e65d..a3d129f89c 100644 --- a/litellm/secret_managers/hashicorp_secret_manager.py +++ b/litellm/secret_managers/hashicorp_secret_manager.py @@ -29,6 +29,7 @@ class HashicorpSecretManager(BaseSecretManager): # Optional config for TLS cert auth self.tls_cert_path = os.getenv("HCP_VAULT_CLIENT_CERT", "") self.tls_key_path = os.getenv("HCP_VAULT_CLIENT_KEY", "") + self.vault_cert_role = os.getenv("HCP_VAULT_CERT_ROLE", None) # Validate environment if not self.vault_token: @@ -60,9 +61,9 @@ class HashicorpSecretManager(BaseSecretManager): --cacert vault-ca.pem \ --cert cert.pem \ --key key.pem \ - --data @payload.json \ + --header "X-Vault-Namespace: mynamespace/" \ + --data '{"name": "my-cert-role"}' \ https://127.0.0.1:8200/v1/auth/cert/login - ``` Response: @@ -75,19 +76,25 @@ class HashicorpSecretManager(BaseSecretManager): "renewable": true } } - ``` """ verbose_logger.debug("Using TLS cert auth for Hashicorp Vault") - # Vault endpoint for cert-based login, e.g. '/v1/auth/cert/login' login_url = f"{self.vault_addr}/v1/auth/cert/login" + # Include your Vault namespace in the header if you're using namespaces. + # E.g. self.vault_namespace = 'mynamespace/' + # If you only have root namespace, you can omit this header entirely. + headers = {} + if hasattr(self, "vault_namespace") and self.vault_namespace: + headers["X-Vault-Namespace"] = self.vault_namespace try: # We use the client cert and key for mutual TLS resp = httpx.post( login_url, cert=(self.tls_cert_path, self.tls_key_path), + headers=headers, + json=self._get_tls_cert_auth_body(), ) resp.raise_for_status() token = resp.json()["auth"]["client_token"] @@ -100,6 +107,9 @@ class HashicorpSecretManager(BaseSecretManager): except Exception as e: raise RuntimeError(f"Could not authenticate to Vault via TLS cert: {e}") + def _get_tls_cert_auth_body(self) -> dict: + return {"name": self.vault_cert_role} + def get_url(self, secret_name: str) -> str: _url = f"{self.vault_addr}/v1/" if self.vault_namespace: diff --git a/tests/secret_manager_tests/test_hashicorp.py b/tests/secret_manager_tests/test_hashicorp.py index 9e25d28d32..612af5a79c 100644 --- a/tests/secret_manager_tests/test_hashicorp.py +++ b/tests/secret_manager_tests/test_hashicorp.py @@ -183,7 +183,8 @@ def test_hashicorp_secret_manager_tls_cert_auth(): test_manager = HashicorpSecretManager() test_manager.tls_cert_path = "cert.pem" test_manager.tls_key_path = "key.pem" - + test_manager.vault_cert_role = "test-role" + test_manager.vault_namespace = "test-namespace" # Test the TLS auth method token = test_manager._auth_via_tls_cert() @@ -192,6 +193,8 @@ def test_hashicorp_secret_manager_tls_cert_auth(): mock_post.assert_called_once_with( f"{test_manager.vault_addr}/v1/auth/cert/login", cert=("cert.pem", "key.pem"), + headers={"X-Vault-Namespace": "test-namespace"}, + json={"name": "test-role"}, ) # Verify the token was cached