Skip to content

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 /metrics brut.
  • 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'EnvoyProxy du 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 CRD HTTPRouteFilter (comme #70).
  • Live (2026-06-16, critĂšres de l'issue #81) : curl https://api.devopsyouss.com/metrics → 404, et la target fastapi reste UP dans Prometheus (up{namespace="fastapi", endpoint="http"} == 1, scrape interne ClusterIP intact). ✅

Date : 2026-06-15 Sprint : 4 Issue : #81