Dalla vita con Kubernetes: come il server HTTP non ha favorito gli spagnoli

Dalla vita con Kubernetes: come il server HTTP non ha favorito gli spagnoli

Un rappresentante del nostro cliente, il cui stack di applicazioni risiede nel cloud Microsoft (Azure), ha affrontato un problema: recentemente, alcune richieste di alcuni clienti dall'Europa hanno iniziato a terminare con l'errore 400 (Richiesta non valida). Tutte le applicazioni sono scritte in .NET, distribuite in Kubernetes...

Una delle applicazioni è l'API, attraverso la quale alla fine arriva tutto il traffico. Questo traffico viene ascoltato dal server HTTP Gheppio, configurato dal client .NET e ospitato in un pod. Con il debug siamo stati fortunati nel senso che c'era un utente specifico che riproduceva costantemente il problema. Tuttavia, tutto è stato complicato dalla catena del traffico:

Dalla vita con Kubernetes: come il server HTTP non ha favorito gli spagnoli

L'errore in Ingress era simile al seguente:

{
   "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":{}
}

Allo stesso tempo, Kestrel ha dato:

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

Anche con la massima verbosità, l'errore Kestrel conteneva estremamente poche informazioni utili:

{
   "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":{}
}

Sembrerebbe che solo tcpdump possa aiutare a risolvere questo problema... ma lo ripeterò per quanto riguarda la catena del traffico:

Dalla vita con Kubernetes: come il server HTTP non ha favorito gli spagnoli

indagine

Ovviamente è meglio ascoltare il traffico su quello specifico nodo, dove Kubernetes ha implementato un pod: il volume del dump sarà tale che sarà possibile trovare almeno qualcosa abbastanza rapidamente. E infatti, esaminandolo, è stato notato il seguente frame:

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

Dopo un'ispezione più attenta della discarica, la parola è stata notata M.laga. È facile intuire che non esiste una città di M.laga in Spagna (ma c’è Malaga). Cogliendo questa idea, abbiamo guardato le configurazioni di Ingress, dove abbiamo visto quella inserita un mese fa (su richiesta del cliente) frammento "innocuo".:

    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;

Dopo aver disabilitato l'inoltro di queste intestazioni, tutto è andato bene! (Divenne presto chiaro che l'applicazione stessa non aveva più bisogno di queste intestazioni.)

Ora esaminiamo il problema più generalmente. Può essere facilmente riprodotto all'interno dell'applicazione effettuando una richiesta telnet a 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

... ritorna 401 Unauthorized, come previsto. Cosa succede se facciamo:

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

?

Sarà di ritorno 400 Bad request — nel registro dell'applicazione riceveremo un errore che ci è già familiare:

{
   "@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
}

Risultati di

Nello specifico Gheppio non si può elaborare correttamente le intestazioni HTTP con i caratteri corretti in UTF-8, che sono contenute nei nomi di un numero abbastanza elevato di città.

Un ulteriore fattore nel nostro caso è che il cliente attualmente non prevede di modificare l'implementazione di Kestrel nell'applicazione. Tuttavia, i problemi nello stesso AspNetCore (№ 4318, № 7707) dicono che questo non aiuterà...

Riassumendo: la nota non riguarda più i problemi specifici di Kestrel o UTF-8 (nel 2019?!), ma il fatto che consapevolezza e studio coerente Ogni passo che fai durante la ricerca dei problemi prima o poi darà i suoi frutti. Buona fortuna!

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento