Từ cuộc sống với Kubernetes: Máy chủ HTTP không ưa chuộng người Tây Ban Nha như thế nào

Từ cuộc sống với Kubernetes: Máy chủ HTTP không ưa chuộng người Tây Ban Nha như thế nào

Đại diện khách hàng của chúng tôi, có ngăn xếp ứng dụng nằm trên đám mây Microsoft (Azure), đã giải quyết một vấn đề: gần đây, một số yêu cầu từ một số khách hàng từ Châu Âu bắt đầu kết thúc với lỗi 400 (Yêu cầu xấu). Tất cả các ứng dụng đều được viết bằng .NET, được triển khai trong Kubernetes...

Một trong những ứng dụng là API, nơi cuối cùng tất cả lưu lượng truy cập đều đến. Lưu lượng truy cập này được máy chủ HTTP lắng nghe Kestrel, được cấu hình bởi máy khách .NET và được lưu trữ trong một nhóm. Với việc gỡ lỗi, chúng tôi thật may mắn khi có một người dùng cụ thể luôn tái tạo lại sự cố. Tuy nhiên, mọi thứ đều phức tạp bởi chuỗi giao thông:

Từ cuộc sống với Kubernetes: Máy chủ HTTP không ưa chuộng người Tây Ban Nha như thế nào

Lỗi trong Ingress trông như thế này:

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

Đồng thời, Kestrel đưa ra:

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

Ngay cả với mức độ chi tiết tối đa, lỗi Kestrel chứa đựng rất nhiều ít thông tin hữu ích:

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

Có vẻ như chỉ tcpdump mới giúp giải quyết vấn đề này... nhưng tôi sẽ nhắc lại về chuỗi lưu lượng:

Từ cuộc sống với Kubernetes: Máy chủ HTTP không ưa chuộng người Tây Ban Nha như thế nào

Điều tra

Rõ ràng, tốt hơn là nên lắng nghe giao thông trên nút cụ thể đó, trong đó Kubernetes đã triển khai một nhóm: khối lượng kết xuất sẽ ở mức có thể tìm thấy ít nhất thứ gì đó khá nhanh chóng. Và thực sự, khi kiểm tra nó, người ta nhận thấy khung sau:

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

Khi kiểm tra kỹ hơn bãi rác, người ta nhận thấy dòng chữ M.laga. Thật dễ dàng để đoán rằng không có thành phố M.laga ở Tây Ban Nha (nhưng có Malaga). Nắm bắt ý tưởng này, chúng tôi đã xem xét các cấu hình Ingress, nơi chúng tôi thấy cấu hình được chèn cách đây một tháng (theo yêu cầu của khách hàng) đoạn "vô hại":

    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;

Sau khi vô hiệu hóa tính năng chuyển tiếp các tiêu đề này, mọi thứ đều ổn! (Rõ ràng là bản thân ứng dụng không còn cần những tiêu đề này nữa.)

Bây giờ chúng ta hãy nhìn vào vấn đề tổng quát hơn. Nó có thể được sao chép dễ dàng bên trong ứng dụng bằng cách thực hiện yêu cầu telnet tới 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

... trả về 401 Unauthorized, như mong đợi. Điều gì xảy ra nếu chúng ta làm:

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

?

Sẽ trở lại 400 Bad request — trong nhật ký ứng dụng, chúng tôi sẽ nhận được một lỗi quen thuộc với chúng tôi:

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

Kết quả

Cụ thể là Kestrel không thể xử lý chính xác các tiêu đề HTTP với các ký tự chính xác trong UTF-8, được chứa trong tên của một số lượng khá lớn các thành phố.

Một yếu tố bổ sung trong trường hợp của chúng tôi là khách hàng hiện không có kế hoạch thay đổi cách triển khai Kestrel trong ứng dụng. Tuy nhiên, vấn đề nằm ở chính AspNetCore (№ 4318, № 7707) họ nói rằng điều này sẽ không giúp ích gì...

Tóm lại: ghi chú không còn về các vấn đề cụ thể của Kestrel hoặc UTF-8 (năm 2019?!), mà là về thực tế là chánh niệm và học tập nhất quán Mỗi bước bạn thực hiện khi tìm kiếm vấn đề sớm muộn gì cũng sẽ có kết quả. Chúc may mắn!

PS

Đọc thêm trên blog của chúng tôi:

Nguồn: www.habr.com

Thêm một lời nhận xét