From 6e14f023ea6f2265844bd2ee69ce239aeef716a8 Mon Sep 17 00:00:00 2001 From: Iaagl Date: Mon, 17 Aug 2026 22:45:13 +0300 Subject: [PATCH 1/3] fix(helm): support authenticated ServiceMonitor scrapes --- helm/litellm-helm/Chart.yaml | 2 +- helm/litellm-helm/README.md | 19 +++++++ .../templates/servicemonitor.yaml | 6 +++ .../tests/servicemonitor_tests.yaml | 51 +++++++++++++++++++ helm/litellm-helm/values.yaml | 4 ++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 helm/litellm-helm/tests/servicemonitor_tests.yaml diff --git a/helm/litellm-helm/Chart.yaml b/helm/litellm-helm/Chart.yaml index a3a94241057..228101e2470 100644 --- a/helm/litellm-helm/Chart.yaml +++ b/helm/litellm-helm/Chart.yaml @@ -18,7 +18,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.3 +version: 1.1.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/helm/litellm-helm/README.md b/helm/litellm-helm/README.md index e059d45e037..aa5749c9ca9 100644 --- a/helm/litellm-helm/README.md +++ b/helm/litellm-helm/README.md @@ -53,6 +53,10 @@ If `db.useStackgresOperator` is used (not yet implemented): | `pdb.maxUnavailable` | Maximum number/percentage of pods that can be unavailable during **voluntary** disruptions (choose **one** of minAvailable/maxUnavailable) | `null` | | `pdb.annotations` | Extra metadata annotations to add to the PDB | `{}` | | `pdb.labels` | Extra metadata labels to add to the PDB | `{}` | +| `serviceMonitor.enabled` | Create a Prometheus Operator ServiceMonitor | `false` | +| `serviceMonitor.authSecret.enabled` | Configure bearer authorization for metrics scraping from an existing Kubernetes Secret | `false` | +| `serviceMonitor.authSecret.name` | Name of the existing Secret containing a valid LiteLLM key | `""` | +| `serviceMonitor.authSecret.key` | Key within the existing Secret that contains the LiteLLM key | `""` | | `billingMetrics.enabled` | Enable enterprise billable-request metering. Requires an enterprise license. | `false` | | `billingMetrics.endpoint` | Collector that the billable-request counter is pushed to. | `https://telemetry.litellm.ai` | @@ -107,6 +111,21 @@ data: type: Opaque ``` +#### Authenticated Prometheus scraping + +LiteLLM requires authentication on `/metrics` by default. Store a valid LiteLLM key in a Kubernetes Secret in the ServiceMonitor namespace, then reference it from the chart: + +``` +serviceMonitor: + enabled: true + authSecret: + enabled: true + name: litellm-prometheus-key + key: token +``` + +Use a dedicated restricted key instead of the LiteLLM master key. The Prometheus Operator must be able to read the Secret. If unauthenticated metrics are acceptable for your deployment, leave `authSecret.enabled` set to `false` and set `litellm_settings.require_auth_for_metrics_endpoint: false` in `proxy_config` + #### Enterprise billable-request metering Enterprise licenses meter billable requests by pushing a counter to LiteLLM's collector over mutual TLS. The chart does not create the client certificate; it mounts one you already hold, read-only, so the private key is never exposed through the environment. Create the Secret under the name the chart expects, then turn the block on: diff --git a/helm/litellm-helm/templates/servicemonitor.yaml b/helm/litellm-helm/templates/servicemonitor.yaml index 68083d0da61..d463f784e5f 100644 --- a/helm/litellm-helm/templates/servicemonitor.yaml +++ b/helm/litellm-helm/templates/servicemonitor.yaml @@ -31,6 +31,12 @@ spec: interval: {{ .interval }} scrapeTimeout: {{ .scrapeTimeout }} scheme: http + {{- if .authSecret.enabled }} + authorization: + credentials: + name: {{ required "serviceMonitor.authSecret.name is required when serviceMonitor.authSecret.enabled is true" .authSecret.name | quote }} + key: {{ required "serviceMonitor.authSecret.key is required when serviceMonitor.authSecret.enabled is true" .authSecret.key | quote }} + {{- end }} {{- if .relabelings }} relabelings: {{- toYaml .relabelings | nindent 4 }} diff --git a/helm/litellm-helm/tests/servicemonitor_tests.yaml b/helm/litellm-helm/tests/servicemonitor_tests.yaml new file mode 100644 index 00000000000..0ddc47881e4 --- /dev/null +++ b/helm/litellm-helm/tests/servicemonitor_tests.yaml @@ -0,0 +1,51 @@ +suite: ServiceMonitor Configuration Tests +templates: + - servicemonitor.yaml +tests: + - it: should not create a ServiceMonitor by default + asserts: + - hasDocuments: + count: 0 + + - it: should create a ServiceMonitor without authorization when no secret is enabled + set: + serviceMonitor.enabled: true + asserts: + - isKind: + of: ServiceMonitor + - isNull: + path: spec.endpoints[0].authorization + + - it: should configure bearer authorization from an existing secret + set: + serviceMonitor.enabled: true + serviceMonitor.authSecret.enabled: true + serviceMonitor.authSecret.name: prometheus-litellm-key + serviceMonitor.authSecret.key: token + asserts: + - equal: + path: spec.endpoints[0].authorization + value: + credentials: + name: prometheus-litellm-key + key: token + - isNull: + path: spec.endpoints[0].bearerTokenSecret + + - it: should reject an enabled authorization secret without a name + set: + serviceMonitor.enabled: true + serviceMonitor.authSecret.enabled: true + serviceMonitor.authSecret.key: token + asserts: + - failedTemplate: + errorMessage: serviceMonitor.authSecret.name is required when serviceMonitor.authSecret.enabled is true + + - it: should reject an enabled authorization secret without a key + set: + serviceMonitor.enabled: true + serviceMonitor.authSecret.enabled: true + serviceMonitor.authSecret.name: prometheus-litellm-key + asserts: + - failedTemplate: + errorMessage: serviceMonitor.authSecret.key is required when serviceMonitor.authSecret.enabled is true diff --git a/helm/litellm-helm/values.yaml b/helm/litellm-helm/values.yaml index 8450c5f2840..a84d4ebc901 100644 --- a/helm/litellm-helm/values.yaml +++ b/helm/litellm-helm/values.yaml @@ -659,6 +659,10 @@ serviceMonitor: # kubernetes.io/test: test interval: 15s scrapeTimeout: 10s + authSecret: + enabled: false + name: "" + key: "" relabelings: [] # - targetLabel: __meta_kubernetes_pod_node_name # replacement: $1 From 7e4cca3f5e4e15b2bc722d2f1931ca634f08c594 Mon Sep 17 00:00:00 2001 From: brtydse100 Date: Tue, 27 Oct 2026 08:14:02 +0200 Subject: [PATCH 2/3] docs(helm): explain restricted metrics credentials --- helm/litellm-helm/README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/helm/litellm-helm/README.md b/helm/litellm-helm/README.md index aa5749c9ca9..3dd80553366 100644 --- a/helm/litellm-helm/README.md +++ b/helm/litellm-helm/README.md @@ -113,7 +113,28 @@ type: Opaque #### Authenticated Prometheus scraping -LiteLLM requires authentication on `/metrics` by default. Store a valid LiteLLM key in a Kubernetes Secret in the ServiceMonitor namespace, then reference it from the chart: +The proxy's `/metrics/` endpoint requires authentication by default when proxy authentication is configured. This option is for `metricsServer.enabled: false`, the default. The separate metrics server does not use virtual-key authentication + +As a proxy administrator, create a dedicated internal user and a key restricted to the metrics routes. Set `LITELLM_URL` to your proxy URL and `LITELLM_MASTER_KEY` to its administrator credential, then run: + +```bash +curl --fail --silent --show-error "$LITELLM_URL/user/new" \ + -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ + -H 'Content-Type: application/json' \ + -d '{"user_id":"prometheus-metrics","user_role":"internal_user","auto_create_key":false}' + +METRICS_KEY="$(curl --fail --silent --show-error "$LITELLM_URL/key/generate" \ + -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ + -H 'Content-Type: application/json' \ + -d '{"user_id":"prometheus-metrics","key_alias":"prometheus","allowed_routes":["/metrics","/metrics/"]}' \ + | jq --exit-status --raw-output .key)" + +printf '%s' "$METRICS_KEY" | kubectl --namespace YOUR_RELEASE_NAMESPACE \ + create secret generic litellm-prometheus-key --from-file=token=/dev/stdin +unset METRICS_KEY +``` + +The Secret must be in the ServiceMonitor namespace and contain the raw key, without a `Bearer ` prefix. The Prometheus Operator must have permission to read it. Reference that Secret in the chart: ``` serviceMonitor: @@ -124,7 +145,7 @@ serviceMonitor: key: token ``` -Use a dedicated restricted key instead of the LiteLLM master key. The Prometheus Operator must be able to read the Secret. If unauthenticated metrics are acceptable for your deployment, leave `authSecret.enabled` set to `false` and set `litellm_settings.require_auth_for_metrics_endpoint: false` in `proxy_config` +The chart does not generate or rotate the LiteLLM key. Keep the key valid and update the Secret when rotating it. If unauthenticated metrics are acceptable for your deployment, leave `authSecret.enabled` set to `false` and set `litellm_settings.require_auth_for_metrics_endpoint: false` in `proxy_config` #### Enterprise billable-request metering From ca8a32e3d3a97f42a45771f92aa30eec68bd9740 Mon Sep 17 00:00:00 2001 From: brtydse100 Date: Tue, 27 Oct 2026 08:18:23 +0200 Subject: [PATCH 3/3] fix(deps): patch MSAL in extension packaging --- vscode-extension/package-lock.json | 18 ++++++++++++++---- vscode-extension/package.json | 5 +++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json index 81e9425ef4e..536b48502fd 100644 --- a/vscode-extension/package-lock.json +++ b/vscode-extension/package-lock.json @@ -189,19 +189,29 @@ } }, "node_modules/@azure/msal-node": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz", - "integrity": "sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-7.1.0.tgz", + "integrity": "sha512-s1O6hSY/C/dmqykTmmeHY6GhSkDuY8N1gxyhuLyaCwbY7N+UBvU2Wu4flas7+NxZiAPcXNPVpgt3n/HP/5yL+w==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "16.14.1", + "@azure/msal-common": "16.14.2", "jsonwebtoken": "^9.0.0" }, "engines": { "node": ">=20" } }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.2.tgz", + "integrity": "sha512-HOBR0Gfo39TMrkxNG8k6cfjRkrRjXZc3JQs3ebMWQc9S4cihuknMYra3sgmzbZB752oQBqBN9+WF7c826p2vIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", diff --git a/vscode-extension/package.json b/vscode-extension/package.json index a3cf2dc7a5f..63d6e6aef44 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -76,6 +76,11 @@ "dependencies": { "openai": "^7.18.0" }, + "overrides": { + "@azure/identity": { + "@azure/msal-node": "7.1.0" + } + }, "devDependencies": { "@types/node": "^22.20.3", "@types/vscode": "1.115.0",