API Troubleshooting by Code

Image: Infra as Code

Tenho participado de inúmeras sessões de “troubleshooting” de infraestruturas, algumas dessas sessões envolvem validar serviços de APIs e múltiplas camadas de comunicação que são empilhadas à frente de uma API por requisitos de arquitetura para expor o serviço ou por necessidades de segurança em proteger o serviço. Antes de colocar qualquer coisa em produção é necessário validar, validar, validar, validar e validar, garantindo que cada camada esteja fazendo a sua função conforme o planejado?

Como saber se cada pecinha desse quebra-cabeça está funcionando corretamente? Hummmm!!! É nesta hora que é preciso saber alguns macetes técnicos e conhecer alguns códigos importantes do protocolo HTTP.

Web Server Fingerprinting

Esse é um conhecimento fundamental, ele é o ponto de partida para qualquer iniciativa de “troubleshooting”. Por este ponto por aqui é que você vai conseguir reconhecer as características do componente que está enviando a resposta e conseguir responder a seguinte pergunta “é esse componente correto que deveria me enviar essa resposta?”.

É preciso saber que cada implementação de web server vai ser específica, cada serviço vai te dar um código de retorno respeitando a rfc2616 de implementação do protocolo HTTP, porém, a resposta vai vir com características específicas como: tipo de web server utilizado; se usa algum tipo de cache; se implementa headers específicos sim ou não; tipo de infraestrutura de cada empresa; Todas essas características presentes ou não numa resposta do web server chamamos de “Web Server Fingerprinting”. Em um processo de análise de problemas reconhecer o “Web Server Fingerprinting” ajuda a rastrear por onde o seu “request” está passando e onde ele está chegando.

Vamos fazer um teste simples e comparar o “Web Server Fingerprinting” de dois serviços famosos na Internet brasileira, o G1 e o UOL.

1$ curl  https://g1.globo.com -vs >g1.txt 2>&1
2$ curl  https://uol.com.br -vs >uol.txt 2>&1
3$ vim -O  uol.txt g1.txt 

Image: Infra as Code

Percebam no quadro verde, ambas as respostas são bem semelhantes até no formato mas diferem em:

  • Versão do protocolo utilizado
  • Código de retorno: o G1 responde com HTTP 200; o UOL responde com HTTP 301
  • Tipos de headers: o G1 responde com vários headers de controle

No quadro vermelho, as respostas são bem diferentes:

  • Tamanho, no G1 tem perto 20 linhas no UOL tem umas 10 linhas
  • O G1 implementa um age de 0s o UOL implementa um age de 60s

O que essas características nos dizem? Diz muita coisa, basta explorar linha por linha de cada resposta e descobrir #ficadica e fica o desafio !! ;). Mas o que dá para afirmar seguramente, ambas tem um “Web Server Fingerprinting” bem diferente uma da outra, para quem está fazendo algum “troubleshooting” em serviços web, saber coletar, conhecer e reconhecer essas características são habilidades que precisam fazer parte das suas ferramentas básicas de trabalho.

Identificando códigos de retorno

Toda transação do protocolo HTTP tem um código de retorno, conhecer as categorias de código de retorno é bastante importante, vejam alguns grupos de códigos de retorno:

  • 1xx - Informational
  • 2xx - Success
  • 3xx - Redirection
  • 4xx - Client Error
  • 5xx - Server Error

Nos exemplos anteriores vimos que o G1 respondeu 200 e o UOL respondeu 301, tá mas e daí e eu com isso?! Para uma pessoa leiga isso não quer dizer nada, mas para você meu jovem sysadmin, alguns códigos que precisam estar na sua mente tão fácil quanto quanto saber que 2+2 é igual 4, veja uma parte deles com um leve tempero de “troubleshooting”:

  • 200 - OK –> Funcionou, blz, tudo certo!!
  • 301 - No Content –> Seu request foi direcionado para outro lugar
  • 302 - Found –> Seu request foi direcionado para outro lugar
  • 400 - Bad Request –> Falta algo no request, melhor rever a URL, a URI, o Host
  • 401 - Unathorized –> Falhou a autenticação, rever mecanismos de autorização, login e senha
  • 403 - Forbidden –> Acesso negado, rever login e senha
  • 404 - Not Found –> Funcionou, mas o conteúdo não está lá
  • 408 - Request Timeout –> Demorou a responder, pode ser algum de configuração no webserver, erro de proxy ou erro do back-end
  • 500 - Internal Server Error –> Request deu certo até chegar no back-end, mas deu erro no backend
  • 503 - Service unavailable –> Request deu certo até chegar no front-end mas não encontrou o back-end

Sacou?!? Você pode conhecer mais alguns códigos de retorno na documentação para desenvolvedores da Mozila

Testando APIs

 1$ curl -I  https://uol.com.br 
 2HTTP/1.0 301 Moved Permanently
 3Location: https://www.uol.com.br/
 4Cache-Control: max-age=60
 5Server: BigIP
 6Connection: Keep-Alive
 7Content-Length: 0
 8
 9$ curl -vv  https://uol.com.br 
10*   Trying 200.147.3.157:443...
11* TCP_NODELAY set
12* Connected to uol.com.br (200.147.3.157) port 443 (#0)
13* ALPN, offering h2
14* ALPN, offering http/1.1
15* successfully set certificate verify locations:
16*   CAfile: /etc/ssl/certs/ca-certificates.crt
17  CApath: /etc/ssl/certs
18* TLSv1.3 (OUT), TLS handshake, Client hello (1):
19* TLSv1.3 (IN), TLS handshake, Server hello (2):
20* TLSv1.2 (IN), TLS handshake, Certificate (11):
21* TLSv1.2 (IN), TLS handshake, Server finished (14):
22* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
23* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
24* TLSv1.2 (OUT), TLS handshake, Finished (20):
25* TLSv1.2 (IN), TLS handshake, Finished (20):
26* SSL connection using TLSv1.2 / AES256-SHA256
27* ALPN, server did not agree to a protocol
28* Server certificate:
29*  subject: CN=uol.com.br
30*  start date: Feb 24 00:00:00 2023 GMT
31*  expire date: Mar  9 23:59:59 2024 GMT
32*  subjectAltName: host "uol.com.br" matched cert's "uol.com.br"
33*  issuer: C=US; O=DigiCert, Inc.; CN=RapidSSL Global TLS RSA4096 SHA256 2022 CA1
34*  SSL certificate verify ok.
35> GET / HTTP/1.1
36> Host: uol.com.br
37> User-Agent: curl/7.68.0
38> Accept: */*
39> 
40* Mark bundle as not supporting multiuse
41* HTTP 1.0, assume close after body
42< HTTP/1.0 301 Moved Permanently
43< Location: https://www.uol.com.br/
44< Cache-Control: max-age=60
45< Server: BigIP
46* HTTP/1.0 connection set to keep alive!
47< Connection: Keep-Alive
48< Content-Length: 0
49< 
50* Connection #0 to host uol.com.br left intact
  1curl -I  https://g1.globo.com
  2HTTP/2 200 
  3date: Thu, 30 Mar 2023 09:44:02 GMT
  4content-type: text/html; charset=UTF-8
  5content-length: 873058
  6show-page-version: 0
  7x-request-id: 75875626-e08c-462b-a4cc-b8b8d0a73c58
  8x-mobile: desktop
  9x-served-from: rpaas-router-gcp-g1-prod, Show Services GCP
 10content-security-policy: upgrade-insecure-requests
 11x-content-type-options: nosniff
 12x-xss-protection: 1; mode=block
 13expires: Thu, 30 Mar 2023 09:44:12 GMT
 14cache-control: max-age=10
 15x-location-rule: equal-barra
 16age: 0
 17vary: X-Forwarded-Proto, User-Agent, Accept-Encoding
 18x-bip: 875445117 asra01lx36ca01.globoi.com
 19via: 2.0 CachOS
 20accept-ranges: bytes
 21x-thanos: 0AB0D063
 22
 23$ curl -I -vv  https://g1.globo.com
 24*   Trying 186.192.81.31:443...
 25* TCP_NODELAY set
 26* Connected to g1.globo.com (186.192.81.31) port 443 (#0)
 27* ALPN, offering h2
 28* ALPN, offering http/1.1
 29* successfully set certificate verify locations:
 30*   CAfile: /etc/ssl/certs/ca-certificates.crt
 31  CApath: /etc/ssl/certs
 32* TLSv1.3 (OUT), TLS handshake, Client hello (1):
 33* TLSv1.3 (IN), TLS handshake, Server hello (2):
 34* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
 35* TLSv1.3 (IN), TLS handshake, Certificate (11):
 36* TLSv1.3 (IN), TLS handshake, CERT verify (15):
 37* TLSv1.3 (IN), TLS handshake, Finished (20):
 38* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
 39* TLSv1.3 (OUT), TLS handshake, Finished (20):
 40* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
 41* ALPN, server accepted to use h2
 42* Server certificate:
 43*  subject: CN=g1.globo.com
 44*  start date: Sep 20 00:00:00 2022 GMT
 45*  expire date: Sep 20 23:59:59 2023 GMT
 46*  subjectAltName: host "g1.globo.com" matched cert's "g1.globo.com"
 47*  issuer: C=US; O=DigiCert, Inc.; CN=RapidSSL Global TLS RSA4096 SHA256 2022 CA1
 48*  SSL certificate verify ok.
 49* Using HTTP2, server supports multi-use
 50* Connection state changed (HTTP/2 confirmed)
 51* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
 52* Using Stream ID: 1 (easy handle 0x55bbe3be0320)
 53> HEAD / HTTP/2
 54> Host: g1.globo.com
 55> user-agent: curl/7.68.0
 56> accept: */*
 57> 
 58* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
 59* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
 60* old SSL session ID is stale, removing
 61* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
 62< HTTP/2 200 
 63HTTP/2 200 
 64< date: Thu, 30 Mar 2023 09:44:09 GMT
 65date: Thu, 30 Mar 2023 09:44:09 GMT
 66< content-type: text/html; charset=UTF-8
 67content-type: text/html; charset=UTF-8
 68< content-length: 873058
 69content-length: 873058
 70< show-page-version: 0
 71show-page-version: 0
 72< x-request-id: e04dda4f-0c43-4f6f-a630-60e405121b11
 73x-request-id: e04dda4f-0c43-4f6f-a630-60e405121b11
 74< x-mobile: desktop
 75x-mobile: desktop
 76< x-served-from: rpaas-router-gcp-g1-prod, Show Services GCP
 77x-served-from: rpaas-router-gcp-g1-prod, Show Services GCP
 78< content-security-policy: upgrade-insecure-requests
 79content-security-policy: upgrade-insecure-requests
 80< x-content-type-options: nosniff
 81x-content-type-options: nosniff
 82< x-xss-protection: 1; mode=block
 83x-xss-protection: 1; mode=block
 84< expires: Thu, 30 Mar 2023 09:44:12 GMT
 85expires: Thu, 30 Mar 2023 09:44:12 GMT
 86< cache-control: max-age=10
 87cache-control: max-age=10
 88< x-location-rule: equal-barra
 89x-location-rule: equal-barra
 90< age: 6
 91age: 6
 92< vary: X-Forwarded-Proto, User-Agent, Accept-Encoding
 93vary: X-Forwarded-Proto, User-Agent, Accept-Encoding
 94< x-bip: 751853216 asra04lx33ca01.globoi.com
 95x-bip: 751853216 asra04lx33ca01.globoi.com
 96< via: 2.0 CachOS
 97via: 2.0 CachOS
 98< accept-ranges: bytes
 99accept-ranges: bytes
100< x-thanos: 0AB25047
101x-thanos: 0AB25047
102
103< 
104* Connection #0 to host g1.globo.com left intact

Testando APIs like a BOSS 😎

A ferramenta Ansible tem um módulo chamado URI, com ele é possível fazermos inúmeros testes, veja algumas possibilidade no código abaixo:

 1---
 2- hosts: localhost 
 3
 4  tasks:
 5
 6  - name: Confirma que G1 vai retornar o código HTTP 200
 7    uri:
 8      url: "https://g1.globo.com"
 9      status_code: 200
10
11  - name: Confirma que G1 vai retornar o código HTTP 400
12    uri:
13      url: "https://g1.globo.com"
14      status_code: 400
15      return_content: no
16    ignore_errors: true
17
18  - name: Confirma que UOL vai retornar o código HTTP 200
19    uri:
20      url: "https://uol.com.br"
21      return_content: no
22      status_code: 200
23
24  - name: Confirma que UOL vai retornar o código HTTP 301
25    uri:
26      url: "https://uol.com.br"
27      return_content: no
28      status_code: 301
29
30  - name: Confirma que págin contém a string leo ( Esse é ótimo para fazer HelthCheck)
31    uri:
32      url: "https://api.nationalize.io/?name=leo"
33      return_content: yes
34    register: this
35    failed_when: "'leo' not in this.content"

Executando o comando

1  $ ansible-playbook  api-test.yaml

Veja os resultados!!

 1PLAY [localhost] ***********************************************************************************************************************************************************************************
 2 
 3TASK [Gathering Facts] *****************************************************************************************************************************************************************************
 4ok: [localhost]
 5 
 6TASK [Confirma que G1 vai retornar o código HTTP 200] **********************************************************************************************************************************************
 7ok: [localhost]
 8 
 9TASK [Confirma que G1 vai retornar o código HTTP 400] **********************************************************************************************************************************************
10fatal: [localhost]: FAILED! => {"accept_ranges": "bytes", "age": "0", "cache_control": "max-age=10", "changed": false, "connection": "close", "content": "<!DOCTYPE HTML><html lang=\"pt-br\" class>
11...ignoring
12 
13TASK [Confirma que UOL vai retornar o código HTTP 200] *********************************************************************************************************************************************
14ok: [localhost]
15 
16TASK [Confirma que UOL vai retornar o código HTTP 301] *********************************************************************************************************************************************
17fatal: [localhost]: FAILED! => {"age": "5", "cache_control": "no-transform, max-age=10, must-revalidate, proxy-revalidate", "changed": false, "connection": "close", "content": "<!DOCTYPE html><htm
18
19TASK [Confirma que págin contém a string leo ( Esse é ótimo para fazer HelthCheck)] ****************************************************************************************************************
20ok: [localhost]
21
22PLAY RECAP *****************************************************************************************************************************************************************************************
23localhost                  : ok=4    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=1

Seéloucccccco isso é lindo d+!!! 😎😎 Pode mandar cinco mil URLs de APIs que eu testo like a BOSS 😎

Conclusão

Esse é um tipo de validação funcional, ideal para ser feito antes de colocar a infraestrutura em produção. Aumentaria muito a confiabilidade da sua infra configurar o pipeline para realizar algumas dessas validações após cada deploy. As possibilidades não se limitam aos exemplos dados. É muito recomendado que se faça outros tipos de validações, como: validações de segurança; validação de input de dados, validação de origem das conexões e todas outras validações necessárias.

Abraços!

Vida longa e próspera a todos!!

Referências

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
  2. https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
  3. https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html
  4. https://www.redhat.com/sysadmin/ansible-web-endpoints
  5. https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html


Eu adoraria ouvir suas outras histórias e situações semelhantes ao que acabei de escrever neste post, você pode me encontrar em @infraascode_br ou linkedin.com/in/leonardoml/ .

Te convido a ver os outros posts do blog Infra-as-Code garanto que tem coisas legais lá!!


--- --- IMPORTANTE --- ---
As opiniões aqui expressas são pessoais e de responsabilidade única e exclusiva do autor, elas não refletem necessariamente a posição das empresas que eu trabalho(ei) e/ou presto(ei) serviço.