La télémétrie est un canal de fuite : tokens, mots de passe, emails s’y retrouvent trop facilement — et un backend d’observabilité est rarement protégé comme la base de production. Dans ce lab, vous constatez une fuite réelle (déjà dans le code de review-service…), puis vous la neutralisez à deux niveaux : dans le SDK de l’application et, en filet de sécurité, dans le collecteur.
🇪🇺 RGPD : email, nom, téléphone sont des données personnelles. Leur présence dans les traces/logs crée les mêmes obligations (droit à l’effacement, rétention…) que dans une base — dans un système conçu pour tout garder.
Prérequis
Labs 1 à 7 terminés.
D’abord les variables de la formation chargées dans votre shell : . ./scripts/env.sh. Elles donnent le port du review-service ($APP_PORT, accès direct au service, pas via le frontend-proxy) ainsi que $PF_ADDR et $PF_HOST, l’adresse sur laquelle vos port-forward écoutent.
Ensuite seulement le port-forward des UIs, plus celui d’OpenSearch, qui s’appuient sur ces variables : kubectl port-forward -n otel-demo --address $PF_ADDR svc/opensearch $OS_PORT:9200 &
Étapes
Partie 1 — Constater la fuite
Relire le code du POST dans ReviewController.java — trois fautes y sont plantées volontairement :
span.setAttribute("user.email", ...); // PII dans un attribut de spanspan.setAttribute("http.request.header.authorization", authorization); // credentials dans un span !logger.info("Creating review for product {} by {} <{}>", ...); // PII dans un log
Déployer avec le Starter (nécessaire pour la partie SDK) et générer une requête « authentifiée » :
. ./scripts/env.sh # si ce n'est pas déjà fait dans ce terminal./scripts/deploy.sh -p starter
kubectl port-forward -n otel-demo --address $PF_ADDR svc/review-service $APP_PORT:8080 &
curl -X POST http://$PF_HOST:$APP_PORT/api/reviews \
-H "Content-Type: application/json"\
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.SECRET-JWT-TOKEN"\
-d '{"productId": "OLJCESPC7Z", "rating": 5, "comment": "fuite", "userEmail": "leak@example.com", "userName": "Leaky User"}'
Chercher la fuite. Dans Jaeger, ouvrez la trace POST /api/reviews : que voyez-vous dans les attributs ? Dans Grafana/OpenSearch, cherchez leak@example.com.
Réponse
Span POST /api/reviews → attributs user.email = leak@example.com et http.request.header.authorization = Bearer eyJ... : le token JWT complet est dans Jaeger. Quiconque accède à l’UI peut le rejouer.
OpenSearch → le log Creating review for product ... by Leaky User <leak@example.com> : PII indexée, requêtable, sauvegardée.
Fuites typiques du même genre : payloads complets en attribut, URLs avec ?token=... (url.full), headers Cookie, corps d’exceptions avec mots de passe.
Partie 2 — Masquer dans le SDK (au plus près de la source)
Lire le mécanisme dans PiiMaskingConfiguration.java et PiiRedactingLogRecordExporter.java : un décorateur d’exporter qui remplace les emails du body par ***@***, branché sur le SDK du Starter via AutoConfigurationCustomizerProvider, activé par la variable MASK_PII.
Pourquoi pas un LogRecordProcessor ? Dans l’API stable, onEmit peut modifier les attributs (setAttribute) mais pas le body — d’où le wrapper d’exporter. Un SpanProcessor a la même limite en onEnd (span en lecture seule) : côté spans, le SDK filtre à la source (Sampler, ne pas poser l’attribut !) et la réécriture se fait au collecteur.
Activer le masquage SDK et rejouer :
kubectl set env -n otel-demo deployment/review-service MASK_PII=true
kubectl rollout status -n otel-demo deployment/review-service
# relancer le port-forward puis :curl -X POST http://$PF_HOST:$APP_PORT/api/reviews \
-H "Content-Type: application/json"\
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.SECRET-JWT-TOKEN"\
-d '{"productId": "OLJCESPC7Z", "rating": 5, "comment": "sdk-mask", "userEmail": "sdk-mask@example.com", "userName": "Masked User"}'
Vérifiez : le log est masqué (***@*** dans OpenSearch)… mais les attributs du span fuient toujours dans Jaeger !
Partie 3 — Le filet de sécurité : masquer au collecteur
Écrire la règle OTTL dans manifests/80-otel-security-values.yaml : elle supprime user.email et http.request.header.authorization de tous les spans, et masque les emails dans tous les bodies de logs — quel que soit le service, corrigé ou non.
Alternatives : le processor redaction (approche allowlist : seuls les attributs autorisés passent — plus sûr qu’une denylist) et replace_pattern(..., hash=...) pour pseudonymiser (hachage) au lieu de supprimer, quand on veut garder la capacité de corréler.
Appliquer et vérifier l’avant/après :
helm upgrade otel-demo open-telemetry/opentelemetry-demo \
--version 0.40.9 -n otel-demo \
-f manifests/values-training.yaml \
-f manifests/30-otel-collector-values.yaml \
-f manifests/60-otel-metrics-values.yaml \
-f manifests/70-otel-traces-values.yaml \
-f manifests/80-otel-security-values.yaml
kubectl rollout status daemonset/otel-collector-agent -n otel-demo
# on peut même désactiver le masquage SDK : le collecteur protège seulkubectl set env -n otel-demo deployment/review-service MASK_PII-
kubectl rollout status -n otel-demo deployment/review-service
# relancer le port-forward puis rejouer un POST avec un email marqueur :curl -X POST http://$PF_HOST:$APP_PORT/api/reviews \
-H "Content-Type: application/json"\
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.SECRET-JWT-TOKEN"\
-d '{"productId": "OLJCESPC7Z", "rating": 5, "comment": "collector-mask", "userEmail": "collector-mask@example.com", "userName": "Safe User"}'
Dans Jaeger : la nouvelle trace POST /api/reviews n’a plus ni user.email ni le header Authorization. Dans OpenSearch : collector-mask@example.com est introuvable, le log montre ***@***.
La vraie correction : le masquage en aval est un filet, pas une excuse — la faute reste dans le code. Supprimez les trois lignes fautives de ReviewController.java (masquage applicatif, niveau 1 de la défense en profondeur) et gardez les règles collecteur pour les services que vous ne contrôlez pas.
Livrable
La preuve avant / après : capture de la trace avec JWT + email (partie 1) et de la même requête après masquage (partie 3), plus la recherche OpenSearch vide sur l’email marqueur.