Од животот со Кубернетес: Како серверот HTTP не ги фаворизираше Шпанците

Од животот со Кубернетес: Како серверот HTTP не ги фаворизираше Шпанците

Претставник на нашиот клиент, чиј стек апликации се наоѓа во облакот на Microsoft (Azure), реши проблем: неодамна, некои барања од некои клиенти од Европа почнаа да завршуваат со грешка 400 (Лошо барање). Сите апликации се напишани во .NET, распоредени во Kubernetes...

Една од апликациите е API, преку кој на крајот доаѓа целиот сообраќај. Овој сообраќај го слуша серверот HTTP Китрел, конфигуриран од клиентот .NET и хостиран во pod. Со дебагирање, имавме среќа во смисла дека имаше конкретен корисник кој постојано го репродуцираше проблемот. Сепак, сè беше комплицирано од сообраќајниот синџир:

Од животот со Кубернетес: Како серверот HTTP не ги фаворизираше Шпанците

Грешката во Ingress изгледаше вака:

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

Во исто време, Кестрал даде:

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

Дури и со максимална говорност, грешката Кестрел содржеше исклучително малку корисни информации:

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

Се чини дека само tcpdump ќе помогне да се реши овој проблем ... но ќе повторам за сообраќајниот синџир:

Од животот со Кубернетес: Како серверот HTTP не ги фаворизираше Шпанците

Истрага

Очигледно, подобро е да се слуша сообраќајот на тој специфичен јазол, каде што Kubernetes има распоредено подлога: волуменот на депонијата ќе биде таков што ќе биде можно да се најде барем нешто прилично брзо. И навистина при неговото испитување беше забележана следната рамка:

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

При поблиска проверка на депонијата, зборот бил забележан M.laga. Лесно е да се погоди дека не постои град М.Лага во Шпанија (но постои Малага). Искористувајќи ја оваа идеја, ги погледнавме конфигурациите на Ingress, каде што ја видовме онаа вметната пред еден месец (на барање на клиентот) „безопасен“ фрагмент:

    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;

По оневозможувањето на препраќањето на овие заглавија, сè стана во ред! (Наскоро стана јасно дека на самата апликација повеќе не и се потребни овие заглавија.)

Сега да го погледнеме проблемот поопшто. Може лесно да се репродуцира внатре во апликацијата со барање телнет до 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, како што се очекуваше. Што ќе се случи ако направиме:

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 — во дневникот на апликацијата ќе добиеме грешка што ни е веќе позната:

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

Резултатите од

Поточно кестрал не може правилно обработувајте ги заглавијата на HTTP со точните знаци во UTF-8, кои се содржани во имињата на прилично голем број градови.

Дополнителен фактор во нашиот случај е тоа што клиентот моментално не планира да ја промени имплементацијата на Kestrel во апликацијата. Сепак, проблемите во самиот AspNetCore (№ 4318, № 7707) велат дека ова нема да помогне...

Да резимираме: белешката повеќе не е за конкретните проблеми на Кестрел или UTF-8 (во 2019 година?!), туку за фактот дека внимателност и доследно проучување Секој чекор што го правите додека барате проблеми, порано или подоцна ќе вроди со плод. Со среќа!

PS

Прочитајте и на нашиот блог:

Извор: www.habr.com

Додадете коментар