[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
This commit is contained in:
Ishaan Jaff
2025-01-17 19:27:56 -08:00
committed by GitHub
parent 69d876f4c7
commit 2c117264a2
3 changed files with 19 additions and 5 deletions
@@ -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
@@ -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:
+4 -1
View File
@@ -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