
O representante do nosso cliente, cuja pilha de aplicativos vive na nuvem da Microsoft (Azure), enfrentou um problema: recentemente, parte das solicitações de alguns clientes da Europa começou a terminar com o erro 400 (
Solicitação incorreta). Todos os aplicativos são escritos em .NET, implantados no Kubernetes ...
Um aplicativo é a API através da qual, em última análise, todo o tráfego chega. Esse tráfego é escutado pelo servidor HTTP
Kestrel configurado pelo cliente .NET e hospedado no pod. Com a depuração, tivemos sorte no sentido de que havia um usuário específico que tinha um problema reproduzido estável. No entanto, tudo foi complicado pela cadeia de tráfego:

O erro no Ingress era assim:
{ "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":{} }
Ao mesmo tempo, Kestrel deu:
HTTP/1.1 400 Bad Request Connection: close Date: Wed, 06 Mar 2019 12:34:20 GMT Server: Kestrel Content-Length: 0
Mesmo com a máxima verbosidade, o erro do Kestrel continha extremamente
pouca informação útil :
{ "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":{} }
Parece que apenas o tcpdump ajudará a resolver esse problema ... mas vou repetir sobre a cadeia de tráfego:

Investigação
Obviamente, é melhor ouvir o tráfego
nesse nó específico em que o Kubernetes implantou o pod: o volume de despejo será tal que será possível encontrar pelo menos algo rapidamente. De fato, ao considerá-lo, esse quadro foi percebido:
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
Após um exame cuidadoso do depósito, a palavra
M.laga
foi notada. É fácil adivinhar que na Espanha não há cidade M.laga (mas há
Málaga ). Entendendo essa idéia, vimos as configurações do Ingress, nas quais vimos um
snippet "inofensivo" inserido há um mês (a pedido do cliente):
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;
Quando você desabilita o encaminhamento desses cabeçalhos, tudo fica bom! (Logo ficou claro que o aplicativo em si não precisava mais desses cabeçalhos.)
Agora vamos ver o problema
de uma maneira mais geral . Pode ser facilmente reproduzido dentro do aplicativo se você fizer uma solicitação de telnet para o
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
...
401 Unauthorized
Retornos
401 Unauthorized
, conforme o esperado. E o que acontece se fizermos:
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
?
400 Bad request
incorreta retornará - no log do aplicativo, obteremos o erro que já sabemos:
{ "@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 }
Sumário
Especificamente, o Kestrel
não pode manipular corretamente os cabeçalhos HTTP com os caracteres corretos em UTF-8, que estão contidos nos nomes de um número bastante grande de cidades.
Um fator adicional em nosso caso é que o cliente atualmente não planeja alterar a implementação do Kestrel no aplicativo. No entanto, problemas no próprio AspNetCore (
nº 4318 ,
nº 7707 ) dizem que isso não ajudará ...
Para resumir: a nota não é mais sobre os problemas específicos do Kestrel ou UTF-8 (em 2019, o ano?!), Mas que a
atenção e o estudo consistente de cada etapa durante a busca de um problema, mais cedo ou mais tarde, darão frutos. Boa sorte
PS
Leia também em nosso blog: