fix(proxy): never log billing credential values when they fail to resolve

Accepting inline PEM turned the cert env vars into secret-bearing values, but
the disable warning still echoed them. A value that is neither a readable path
nor `-----BEGIN`-prefixed PEM, for example a key with a preamble or a malformed
secret, fell through to the path branch and was written to the proxy logs
verbatim, exposing the client certificate or private key to anyone who can read
them.

The warning now names the offending environment variables and tells the operator
what a valid value looks like, without ever printing one
This commit is contained in:
Yassin Kortam 2026-07-09 20:32:30 +03:00
parent 37c6bad289
commit 269ee7e876
2 changed files with 40 additions and 5 deletions

View file

@ -229,14 +229,24 @@ def load_billing_metrics_config(
)
return None
required_paths = [paths.client_cert_path, paths.client_key_path] + (
[paths.ca_cert_path] if paths.ca_cert_path else []
)
unreadable = [path for path in required_paths if not os.path.isfile(path)]
# Report the variable names, never their values. A value that is neither a
# readable path nor recognizable PEM is still secret material, and this
# warning would otherwise copy a client key straight into the proxy logs.
unreadable = [
env_name
for env_name, path in (
(CLIENT_CERT_ENV, paths.client_cert_path),
(CLIENT_KEY_ENV, paths.client_key_path),
(CA_CERT_ENV, paths.ca_cert_path),
)
if path and not os.path.isfile(path)
]
if unreadable:
verbose_proxy_logger.warning(
"Enterprise billing metrics disabled: certificate file(s) not found: %s",
"Enterprise billing metrics disabled: %s did not resolve to a readable certificate file. "
"Set each to a file path, or to inline PEM content beginning with '%s'.",
", ".join(unreadable),
_PEM_PREFIX,
)
return None

View file

@ -240,6 +240,31 @@ def test_load_config_with_inline_pem_disabled_when_unwritable(monkeypatch):
assert bm.load_billing_metrics_config(license_data=None, litellm_version="1.0") is None
def test_load_config_never_logs_credential_values(monkeypatch):
"""
A value that is neither a readable path nor `-----BEGIN`-prefixed PEM is
still secret material. The disable warning must name the env vars, never
echo their contents, or a malformed key lands in the proxy logs.
"""
secret_material = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQ-not-pem-prefixed"
monkeypatch.setenv(bm.ENDPOINT_ENV, "https://collector.example:4317")
monkeypatch.setenv(bm.CLIENT_CERT_ENV, secret_material)
monkeypatch.setenv(bm.CLIENT_KEY_ENV, secret_material)
logged: List[str] = []
def _capture(msg, *args):
logged.append(msg % args if args else msg)
monkeypatch.setattr(bm.verbose_proxy_logger, "warning", _capture)
assert bm.load_billing_metrics_config(license_data=None, litellm_version="1.0") is None
joined = "\n".join(logged)
assert secret_material not in joined
assert bm.CLIENT_CERT_ENV in joined and bm.CLIENT_KEY_ENV in joined
def test_load_config_with_empty_pem_env_is_disabled(monkeypatch):
"""Empty stays empty: an unset secret must not be mistaken for inline PEM."""
monkeypatch.setenv(bm.ENDPOINT_ENV, "https://collector.example:4317")