De la vie avec Kubernetes : comment le serveur HTTP n'a pas favorisé les Espagnols

De la vie avec Kubernetes : comment le serveur HTTP n'a pas favorisé les Espagnols

Un représentant de notre client, dont la pile d'applications réside dans le cloud Microsoft (Azure), a résolu un problème : récemment, certaines requêtes de certains clients européens ont commencé à se terminer par l'erreur 400 (Bad Request). Toutes les applications sont écrites en .NET, déployées dans Kubernetes...

L’une des applications est l’API, par laquelle passe finalement tout le trafic. Ce trafic est écouté par le serveur HTTP Crécerelle, configuré par le client .NET et hébergé dans un pod. Avec le débogage, nous avons eu de la chance dans le sens où il y avait un utilisateur spécifique qui reproduisait systématiquement le problème. Cependant, tout était compliqué par la chaîne du trafic :

De la vie avec Kubernetes : comment le serveur HTTP n'a pas favorisé les Espagnols

L'erreur dans Ingress ressemblait à ceci :

{
   "number_fields":{
      "status":400,
      "request_time":0.001,
      "bytes_sent":465,
      "upstream_response_time":0,
      "upstream_retries":0,
      "bytes_received":2328
   },
   "stream":"stdout",
   "string_fields":{
      "ingress":"app",
      "protocol":"HTTP/1.1",
      "request_id":"f9ab8540407208a119463975afda90bc",
      "path":"/api/sign-in",
      "nginx_upstream_status":"400",
      "service":"app",
      "namespace":"production",
      "location":"/front",
      "scheme":"https",
      "method":"POST",
      "nginx_upstream_response_time":"0.000",
      "nginx_upstream_bytes_received":"120",
      "vhost":"api.app.example.com",
      "host":"api.app.example.com",
      "user":"",
      "address":"83.41.81.250",
      "nginx_upstream_addr":"10.240.0.110:80",
      "referrer":"https://api.app.example.com/auth/login?long_encrypted_header",
      "service_port":"http",
      "user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36",
      "time":"2019-03-06T18:29:16+00:00",
      "content_kind":"cache-headers-not-present",
      "request_query":""
   },
   "timestamp":"2019-03-06 18:29:16",
   "labels":{
      "app":"nginx",
      "pod-template-generation":"6",
      "controller-revision-hash":"1682636041"
   },
   "namespace":"kube-nginx-ingress",
   "nsec":6726612,
   "source":"kubernetes",
   "host":"k8s-node-55555-0",
   "pod_name":"nginx-v2hcb",
   "container_name":"nginx",
   "boolean_fields":{}
}

Au même moment, Kestrel a donné :

HTTP/1.1 400 Bad Request
Connection: close
Date: Wed, 06 Mar 2019 12:34:20 GMT
Server: Kestrel
Content-Length: 0

Même avec une verbosité maximale, l'erreur Kestrel contenait extrêmement peu d'informations utiles:

{
   "number_fields":{"ThreadId":76},
   "stream":"stdout",
   "string_fields":{
      "EventId":"{"Id"=>17, "Name"=>"ConnectionBadRequest"}",
      "SourceContext":"Microsoft.AspNetCore.Server.Kestrel",
      "ConnectionId":"0HLL2VJSST5KV",
      "@mt":"Connection id "{ConnectionId}" bad request data: "{message}"",
      "@t":"2019-03-07T13:06:48.1449083Z",
      "@x":"Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Malformed request: invalid headers.n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<ProcessRequestsAsync>d__185`1.MoveNext()",
      "message":"Malformed request: invalid headers."
   },
   "timestamp":"2019-03-07 13:06:48",
   "labels":{
      "pod-template-hash":"2368795483",
      "service":"app"
   },
   "namespace":"production",
   "nsec":145341848,
   "source":"kubernetes",
   "host":"k8s-node-55555-1",
   "pod_name":"app-67bdcf98d7-mhktx",
   "container_name":"app",
   "boolean_fields":{}
}

Il semblerait que seul tcpdump aidera à résoudre ce problème... mais je vais répéter à propos de la chaîne de trafic :

De la vie avec Kubernetes : comment le serveur HTTP n'a pas favorisé les Espagnols

Enquête

Évidemment, il vaut mieux écouter le trafic sur ce nœud spécifique, où Kubernetes a déployé un pod : le volume du dump sera tel qu'il sera possible de trouver au moins quelque chose assez rapidement. Et en effet, en l’examinant, on a remarqué le cadre suivant :

GET /back/user HTTP/1.1
Host: api.app.example.com
X-Request-ID: 27ceb14972da8c21a8f92904b3eff1e5
X-Real-IP: 83.41.81.250
X-Forwarded-For: 83.41.81.250
X-Forwarded-Host: api.app.example.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-URI: /front/back/user
X-Scheme: https
X-Original-Forwarded-For: 83.41.81.250
X-Nginx-Geo-Client-Country: Spain
X-Nginx-Geo-Client-City: M.laga
Accept-Encoding: gzip
CF-IPCountry: ES
CF-RAY: 4b345cfd1c4ac691-MAD
CF-Visitor: {"scheme":"https"}
pragma: no-cache
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
referer: https://app.example.com/auth/login
accept-language: en-US,en;q=0.9,en-GB;q=0.8,pl;q=0.7
cookie: many_encrypted_cookies; .AspNetCore.Identity.Application=something_encrypted; 
CF-Connecting-IP: 83.41.81.250
True-Client-IP: 83.41.81.250
CDN-Loop: cloudflare

HTTP/1.1 400 Bad Request
Connection: close
Date: Wed, 06 Mar 2019 12:34:20 GMT
Server: Kestrel
Content-Length: 0

En inspectant de plus près la décharge, le mot a été remarqué M.laga. Il est facile de deviner qu’il n’y a pas de ville de M.laga en Espagne (mais il y en a Málaga). Saisissant cette idée, nous avons regardé les configs Ingress, où nous avons vu celle insérée il y a un mois (à la demande du client) extrait "inoffensif":

    ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Nginx-Geo-Client-Country $geoip_country_name;
      proxy_set_header X-Nginx-Geo-Client-City $geoip_city;

Après avoir désactivé le transfert de ces en-têtes, tout s'est bien passé ! (Il est vite devenu évident que l’application elle-même n’avait plus besoin de ces en-têtes.)

Voyons maintenant le problème plus généralement. Il peut être facilement reproduit dans l'application en effectuant une requête telnet à localhost:80:

GET /back/user HTTP/1.1
Host: api.app.example.com
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
Cookie: test=Desiree

... Retour 401 Unauthorized, comme prévu. Que se passe-t-il si nous faisons :

GET /back/user HTTP/1.1
Host: api.app.example.com
cache-control: no-cache
accept: application/json, text/plain, */*
origin: https://app.example.com
Cookie: test=Désirée

?

Reviendra 400 Bad request — dans le journal des applications, nous recevrons une erreur qui nous est déjà familière :

{
   "@t":"2019-03-31T12:59:54.3746446Z",
   "@mt":"Connection id "{ConnectionId}" bad request data: "{message}"",
   "@x":"Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Malformed request: invalid headers.n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.TryParseRequest(ReadResult result, Boolean& endConnection)n   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.<ProcessRequestsAsync>d__185`1.MoveNext()",
   "ConnectionId":"0HLLLR1J974L9",
   "message":"Malformed request: invalid headers.",
   "EventId":{
      "Id":17,
      "Name":"ConnectionBadRequest"
   },
   "SourceContext":"Microsoft.AspNetCore.Server.Kestrel",
   "ThreadId":71
}

Les résultats de

Plus précisément crécerelle ne peut pas traiter correctement les en-têtes HTTP avec les caractères corrects en UTF-8, qui sont contenus dans les noms d'un assez grand nombre de villes.

Un facteur supplémentaire dans notre cas est que le client n'envisage pas actuellement de modifier l'implémentation de Kestrel dans l'application. Cependant, des problèmes dans AspNetCore lui-même (№ 4318, № 7707) ils disent que cela ne servira à rien...

Pour résumer : la note ne porte plus sur les problèmes spécifiques du Kestrel ou de l'UTF-8 (en 2019 ?!), mais sur le fait que pleine conscience et étude cohérente Chaque pas que vous faites dans la recherche de problèmes portera tôt ou tard ses fruits. Bonne chance!

PS

A lire aussi sur notre blog :

Source: habr.com

Ajouter un commentaire