fix(examples): make the metrics scrape example actually reachable

The VMServiceScrape example scraped Traefik's pods directly, so the
request carried Host: <pod-ip>:<port>. Traefik evaluates a router's
match rule before any middleware runs, and the only router in this
file requires Host(`llm.example.com`). The scrape request never
matched it, the llmgateway middleware never ran, and /metrics 404ed
before the plugin ever saw the request.

Add a second, Host-free IngressRoute matching PathPrefix(`/metrics`)
and carrying the same middleware, so an in-cluster scraper reaches the
route regardless of what Host header it sends. Document the
reachability trade-off this introduces (this route is not
Host-restricted, though /metrics stays gated by its own auth inside
the plugin) and name the alternative fix the review also offered:
relabelConfigs rewriting __address__ to the ingress hostname before
the request is sent, keeping one Host-scoped router instead.
This commit is contained in:
2026-08-23 01:56:44 +01:00
parent 47e22e627c
commit 95039d96c9
+45 -1
View File
@@ -210,6 +210,48 @@ spec:
- name: llmgateway-dummy-backend
port: 80
---
# A SEPARATE, Host-free IngressRoute for the metrics scrape (review fix,
# adversarial verification 2026-08-23): Traefik evaluates a router's
# match rule BEFORE any middleware runs, and the ONLY router above
# requires Host(`llm.example.com`). A VMServiceScrape/ServiceMonitor (below)
# reaches this Service through its cluster-internal DNS name or the pod
# IP directly, so the scrape request's Host header is never
# llm.example.com — it never matches that rule, the llmgateway
# middleware (and therefore /metrics itself) never runs, and the scrape
# 404s. PathPrefix, not Host, is deliberate here: this router exists
# specifically so an in-cluster scraper, addressing this Service by
# whatever internal name/IP it resolves to, still reaches the metrics
# route regardless of Host. /metrics is already gated by its own auth
# inside the plugin (an admin key, or metrics.allowedCIDRs — see
# ../README.md#metrics), so this being reachable without a Host match is
# not a second, unguarded door — but if your own Traefik instance fronts
# other Host-restricted routers on the SAME entryPoint, this route is
# reachable via any of THEIR hostnames too, not only in-cluster; narrow
# entryPoints below to an internal-only one, or add your own Host()
# match for your cluster's internal DNS name, if that reachability is
# broader than you want. The alternative the review that flagged this
# also named — keeping one Host-scoped router and instead using
# VMServiceScrape's own relabelConfigs to rewrite __address__ to
# llm.example.com before the request is sent — works too; this file uses
# the simpler of the two.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: llmgateway-metrics
namespace: traefik
spec:
entryPoints:
- websecure
routes:
- match: PathPrefix(`/metrics`)
kind: Rule
middlewares:
- name: llmgateway
services:
- name: llmgateway-dummy-backend
port: 80
---
# Scraping /metrics (../README.md#metrics): this cluster runs
# VictoriaMetrics, not Prometheus, so the CRD is VMServiceScrape
@@ -222,7 +264,9 @@ spec:
# IMPORTANT: /metrics is served by TRAEFIK ITSELF, not by
# llmgateway-dummy-backend above — this middleware intercepts the path
# before any backend service is ever reached (same as every other route
# this plugin owns). Point this scrape at YOUR Traefik installation's own
# this plugin owns), which is exactly why the Host-free IngressRoute
# above exists: without it this scrape 404s before the middleware ever
# sees the request. Point this scrape at YOUR Traefik installation's own
# Service (commonly named "traefik" — verify against your own Helm
# release), never at a backend service this file defines. Adjust
# selector/namespaceSelector/port to match your Traefik Service's real