ADR 018 â Exclure /metrics de l'exposition publique (2026-06-15)
Statut
AcceptĂ© (2026-06-15). Issue #81. ValidĂ© live le 2026-06-16 (/metrics â 404
depuis l'extérieur, target fastapi toujours UP dans Prometheus).
Contexte
Depuis #73, l'application expose /metrics (format Prometheus) via
prometheus-fastapi-instrumentator. L'endpoint est branché sur l'app FastAPI
principale, donc routé par l'HTTPRoute public (api.devopsyouss.com/metrics) par
effet de bord : la rule existante matche PathPrefix: / et attrape tout.
Or /metrics n'a aucune raison d'ĂȘtre public :
- Prometheus le scrape en interne via le Service ClusterIP (
ServiceMonitor, #74), jamais via le Gateway. - La vitrine humaine du monitoring est Grafana (#76), pas
/metricsbrut. - Exposé publiquement, il divulgue routes, latences, volumétrie et codes d'erreur, utiles à un attaquant pour la reconnaissance.
DĂ©cision 1 â Couper au niveau du Gateway, pas dans l'app
On bloque /metrics au niveau du Gateway (lĂ oĂč il fuit), sans toucher Ă
l'app ni au scrape interne. Une rule HTTPRoute dédiée matche PathPrefix: /metrics
et renvoie une réponse directe 404 via un HTTPRouteFilter (CRD Envoy Gateway,
gateway.envoyproxy.io/v1alpha1), référencé par un filtre ExtensionRef :
# k8s/base/httproutefilter-deny-metrics.yaml
kind: HTTPRouteFilter
spec:
directResponse:
statusCode: 404
body:
type: Inline
inline: "Not Found"
La rule / existante (backend) reste inchangée. Gateway API choisit le match le
plus spĂ©cifique : une requĂȘte /metrics tombe sur la rĂ©ponse 404 et n'atteint
jamais le backend ; tout le reste du trafic passe normalement vers l'app. Le
ServiceMonitor tape le Service ClusterIP en direct, hors Gateway : le scrape
interne n'est pas affecté.
DĂ©cision 2 â 404, pas 403
On renvoie 404 Not Found plutÎt que 403 Forbidden : du point de vue d'un client externe, l'endpoint « n'existe pas ». Un 403 confirmerait au contraire la présence de l'endpoint (information de reconnaissance). Le 404 ne divulgue rien.
Alternatives écartées
| Option | Pourquoi écartée |
|---|---|
| SecurityPolicy Envoy Gateway (authz deny) | Conçue pour de l'authentification / autorisation fine (JWT, OIDC, API key). Overkill pour couper sÚchement un chemin ; ajoute une ressource et une logique d'authz inutiles ici. |
Exposer /metrics sur un port séparé cÎté app |
Invasif : modif de l'app, du Service et du ServiceMonitor. Déborde du périmÚtre sécurité (refonte applicative) alors que le problÚme est purement un routage public. |
Retirer .expose(app) de l'instrumentator |
Casserait le scrape interne (plus d'endpoint du tout). Le besoin est de garder /metrics interne, pas de le supprimer. |
Conséquences
- Aucun impact sur le scrape interne : Prometheus continue de scraper le
ClusterIP
fastapi(ServiceMonitor, path/metrics), indépendant du Gateway. - Pattern réutilisable : tout endpoint interne exposé par effet de bord (debug,
admin) peut ĂȘtre coupĂ© de la mĂȘme façon (rule spĂ©cifique +
directResponse). - Cohérent avec ADR 010/017 : on reste dans le modÚle Envoy Gateway déclaratif,
sans contrĂŽleur supplĂ©mentaire (mĂȘme famille de CRD que l'
EnvoyProxydu NLB).
Validation
- Hors infra :
kustomize build k8s/base(exit 0), YAML conforme Ă l'API Envoy Gateway, cohĂ©rence de la rĂ©fĂ©rence HTTPRoute â HTTPRouteFilter (deny-metrics). Pas d'outil hors cluster pour valider le CRDHTTPRouteFilter(comme #70). - Live (2026-06-16, critĂšres de l'issue #81) :
curl https://api.devopsyouss.com/metricsâ 404, et la targetfastapireste UP dans Prometheus (up{namespace="fastapi", endpoint="http"}== 1, scrape interne ClusterIP intact). â
Date : 2026-06-15 Sprint : 4 Issue : #81