ΠžΡ‚ ΠΆΠΈΠ²ΠΎΡ‚Π° с Kubernetes: Как HTTP ΡΡŠΡ€Π²ΡŠΡ€ΡŠΡ‚ Π½Π΅ облагодСтСлства испанцитС

ΠžΡ‚ ΠΆΠΈΠ²ΠΎΡ‚Π° с Kubernetes: Как HTTP ΡΡŠΡ€Π²ΡŠΡ€ΡŠΡ‚ Π½Π΅ облагодСтСлства испанцитС

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΡ‚Π΅Π» Π½Π° нашия ΠΊΠ»ΠΈΠ΅Π½Ρ‚, Ρ‡ΠΈΠΉΡ‚ΠΎ стСк ΠΎΡ‚ прилоТСния сС Π½Π°ΠΌΠΈΡ€Π° Π² ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° Microsoft (Azure), адрСсира ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ: наскоро някои заявки ΠΎΡ‚ някои ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΈ ΠΎΡ‚ Π•Π²Ρ€ΠΎΠΏΠ° Π·Π°ΠΏΠΎΡ‡Π½Π°Ρ…Π° Π΄Π° Π·Π°Π²ΡŠΡ€ΡˆΠ²Π°Ρ‚ с Π³Ρ€Π΅ΡˆΠΊΠ° 400 (НСправилна заявка). Всички прилоТСния са написани Π½Π° .NET, Π²Π½Π΅Π΄Ρ€Π΅Π½ΠΈ Π² Kubernetes...

Π•Π΄Π½ΠΎ ΠΎΡ‚ прилоТСнията Π΅ API, ΠΏΡ€Π΅Π· ΠΊΠΎΠ΅Ρ‚ΠΎ Π² ΠΊΡ€Π°ΠΉΠ½Π° смСтка ΠΈΠ΄Π²Π° цСлият Ρ‚Ρ€Π°Ρ„ΠΈΠΊ. Π’ΠΎΠ·ΠΈ Ρ‚Ρ€Π°Ρ„ΠΈΠΊ сС ΠΏΡ€ΠΎΡΠ»ΡƒΡˆΠ²Π° ΠΎΡ‚ HTTP ΡΡŠΡ€Π²ΡŠΡ€Π° Π²Π΅Ρ‚Ρ€ΡƒΡˆΠΊΠ°, ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½ ΠΎΡ‚ .NET ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ хостван Π² ΠΏΠΎΠ΄. Π‘ отстраняванСто Π½Π° Π³Ρ€Π΅ΡˆΠΊΠΈ ΠΈΠΌΠ°Ρ…ΠΌΠ΅ ΠΊΡŠΡΠΌΠ΅Ρ‚ Π² смисъл, Ρ‡Π΅ имашС ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π΅Π½ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π», ΠΊΠΎΠΉΡ‚ΠΎ постоянно Π²ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅ΠΆΠ΄Π°ΡˆΠ΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°. Всичко ΠΎΠ±Π°Ρ‡Π΅ бСшС услоТнСно ΠΎΡ‚ Π²Π΅Ρ€ΠΈΠ³Π°Ρ‚Π° Π½Π° Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°:

ΠžΡ‚ ΠΆΠΈΠ²ΠΎΡ‚Π° с Kubernetes: Как 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":{}
}

Π’ ΡΡŠΡ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Kestrel Π΄Π°Π΄Π΅:

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

Π”ΠΎΡ€ΠΈ ΠΈ с максимално многословиС, Π³Ρ€Π΅ΡˆΠΊΠ°Ρ‚Π° Kestrel ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°ΡˆΠ΅ ΠΈΠ·ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎΠ»Π΅Π·Π½Π° информация:

{
   "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 Ρ‰Π΅ ΠΏΠΎΠΌΠΎΠ³Π½Π΅ Π·Π° Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚ΠΎΠ·ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ ... Π½ΠΎ Ρ‰Π΅ повторя Π·Π° Π²Π΅Ρ€ΠΈΠ³Π°Ρ‚Π° Π½Π° Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°:

ΠžΡ‚ ΠΆΠΈΠ²ΠΎΡ‚Π° с Kubernetes: Как 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. ЛСсно Π΅ Π΄Π° сС досСтитС, Ρ‡Π΅ Π² Испания няма Π³Ρ€Π°Π΄ 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;

Π‘Π»Π΅Π΄ Π΄Π΅Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€Π°Π½Π΅ Π½Π° ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‰Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚Π΅Π·ΠΈ Ρ…Π΅Π΄ΡŠΡ€ΠΈ всичко сС ΠΎΠΏΡ€Π°Π²ΠΈ! (Π‘ΠΊΠΎΡ€ΠΎ стана ясно, Ρ‡Π΅ самото ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π²Π΅Ρ‡Π΅ Π½Π΅ сС Π½ΡƒΠΆΠ΄Π°Π΅ ΠΎΡ‚ Ρ‚Π΅Π·ΠΈ Π·Π°Π³Π»Π°Π²ΠΊΠΈ.)

Π‘Π΅Π³Π° Π½Π΅ΠΊΠ° Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° ΠΏΠΎ-ΠΎΠ±Ρ‰ΠΎ. МоТС лСсно Π΄Π° сС Π²ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅ Π²ΡŠΡ‚Ρ€Π΅ Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, ΠΊΠ°Ρ‚ΠΎ сС Π½Π°ΠΏΡ€Π°Π²ΠΈ 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

... сС Π·Π°Π²Ρ€ΡŠΡ‰Π° 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) ΠΊΠ°Π·Π²Π°Ρ‚, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° няма Π΄Π° ΠΏΠΎΠΌΠΎΠ³Π½Π΅...

Π—Π° Π΄Π° ΠΎΠ±ΠΎΠ±Ρ‰ΠΈΠΌ: Π±Π΅Π»Π΅ΠΆΠΊΠ°Ρ‚Π° Π²Π΅Ρ‡Π΅ Π½Π΅ Π΅ Π·Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ Π½Π° Kestrel ΠΈΠ»ΠΈ UTF-8 (ΠΏΡ€Π΅Π· 2019?!), Π° Π·Π° Ρ„Π°ΠΊΡ‚Π°, Ρ‡Π΅ вниматСлност ΠΈ послСдоватСлно ΠΈΠ·ΡƒΡ‡Π°Π²Π°Π½Π΅ Всяка ΡΡ‚ΡŠΠΏΠΊΠ°, която ΠΏΡ€Π°Π²ΠΈΡ‚Π΅, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ Ρ‚ΡŠΡ€ΡΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ, Ρ€Π°Π½ΠΎ ΠΈΠ»ΠΈ късно Ρ‰Π΅ Π΄Π°Π΄Π΅ ΠΏΠ»ΠΎΠ΄. ΠšΡŠΡΠΌΠ΅Ρ‚!

PS

ΠŸΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ ΡΡŠΡ‰ΠΎ Π² нашия Π±Π»ΠΎΠ³:

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: www.habr.com

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€