API Troubleshooting by 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
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
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
- https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
- https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html
- https://www.redhat.com/sysadmin/ansible-web-endpoints
- https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
MENTORIA
Curtiu o blog? Quer trocar uma ideia comigo sobre algum post?
Marca Aqui! É um papo gratuito
oferecido para quem é leitor do blog, podemos falar de temas como: DevOps, SRE e carreira em TI.
Te convido a ver os outros posts do blog Infra-as-Code garanto que tem coisas legais lá!!
|