Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Dissecando mensagens gRPC e Google PubSub no Attribute™ Sensor: uma jornada pela pilha de rede

By Hili ParyentiOct 9, 20245 min read

Esta página também está disponível em English, Deutsch, Español, Français, Italiano e 日本語.

Construir um sensor avançado para monitorar e dissecar mensagens de rede não é tarefa fácil, ainda mais quando o assunto são serviços de nuvem modernos e complexos.

No Attribute™, temos um orgulho especial do nosso Sensor. Ele nos permite enxergar o tráfego e extrair insights sobre o uso de recursos de nuvem e sobre os fluxos das aplicações — por isso investimos e evoluímos as capacidades do sensor do Attribute o tempo todo.

Neste post, vamos compartilhar o que aprendemos ao adicionar novos recursos de dissecação para dar suporte ao Google Pub/Sub.

O desafio: dissecar o protocolo do Google Pub/Sub

Nosso desafio era claro: como adicionar suporte para dissecar mensagens do serviço Google Cloud Pub/Sub, que opera sobre o protocolo gRPC, cobrindo também outros serviços do GCP e comunicações gRPC em Python?

No centro desse desafio estava o PubSub, o serviço de fila de mensagens do Google, que se comunica via gRPC — um protocolo baseado em HTTP/2 que serializa mensagens em Protobuf. Precisávamos entender como capturar e decodificar essas mensagens em nível granular, lidando ao mesmo tempo com dados criptografados e comprimidos trafegando pela rede.

O primeiro passo: preparando o ambiente do PubSub

Começamos criando um ambiente controlado no Google Cloud para gerar mensagens do PubSub. Para isso, configuramos um tópico no PubSub, criamos um canal de subscription e escrevemos scripts em Python para simular eventos reais de publicação e assinatura.

Em seguida, escrevemos um script publisher simples para enviar mensagens ao canal e outro script que atua como servidor subscriber, aguardando mensagens do canal de subscription criado.

Veja a seguir um trecho da lógica do script publisher:

from``google.cloud``import``pubsub_v1
from``google.oauth2``import``service_account
def``test_pubsub_v1_publisher_client():
# Create a Pub/Sub publisher client with the credentials`<br>credentials=service_account.Credentials.from_service_account_file(<br>```TEST_CONFIG[``"service_account_json_file_path"``]
)`<br>publisher=pubsub_v1.PublisherClient(credentials=credentials)<br>```# Publish the message
message_data``=``"Hello, Pub/Sub!"`<br>future=publisher.publish(<br>```topic``=``TEST_CONFIG[``"topic_path"``], data``=``message_data.encode(``"utf-8"``)
)`<br>logger.info(f"Published message: {future.result()}")`

Com tudo pronto, a ideia era que nosso sensor capturasse as mensagens do PubSub à medida que trafegassem pela rede. Só que o sensor não detectou nenhum tráfego de rede nem atividade da biblioteca SSL. Essa falha inicial nos empurrou a investigar a pilha de rede em mais detalhes.

Cavando mais fundo: entendendo a pilha de rede do PubSub

Depois de alguma pesquisa, descobrimos que o Google PubSub se comunica via gRPC, que usa HTTP/2 como camada de transporte e Protobuf para serialização das mensagens. A biblioteca `grpcio`, que dá base ao gRPC em Python, adiciona ainda mais complexidade ao embutir partes do OpenSSL dentro dos seus próprios módulos Cython customizados para criptografia e segurança de transporte.

Rastreamos o funcionamento interno da biblioteca `grpcio` e, no fim, identificamos o uso de um módulo C-Extension (`cygrpc.cpython-x86_64-linux-gnu.so`) responsável por tratar as mensagens gRPC criptografadas. Essa descoberta foi decisiva: agora tínhamos um alvo para o nosso sensor — interceptar essas funções gRPC de baixo nível para capturar as mensagens do PubSub.

O grande avanço: interceptando mensagens gRPC

O grande avanço veio quando focamos na Transport Security Interface (TSI) dentro do `grpcio`. Esse módulo cuida da criptografia e da descriptografia das mensagens gRPC, e percebemos que, ao posicionar probes em funções específicas da TSI, dava para capturar os dados criptografados no exato momento em que eram processados.

Instalando probes eBPF (extended Berkeley Packet Filter) em funções críticas da TSI, como `protect` e `unprotect`, conseguimos interceptar quadros HTTP/2 brutos — na prática, as mensagens gRPC encapsuladas em Protobuf.

A seguir está a struct "tsi_frame_protector_vtable", usada internamente na biblioteca para criptografia/descriptografia de texto claro/texto cifrado:

struct``tsi_frame_protector_vtable {
tsi_result (*protect)(tsi_frame_protector* self,`<br>constunsignedchar* unprotected_bytes,`<br>```size_t* unprotected_bytes_size,<br>```unsigned``char``* protected_output_frames,
size_t``* protected_output_frames_size);`<br>tsi_result (protect_flush)(tsi_frame_protector self,<br>```unsigned``char``* protected_output_frames,
size_t``* protected_output_frames_size,`<br>size_t* still_pending_size);`<br>```tsi_result (*unprotect)(tsi_frame_protector* self,`<br>```constunsignedchar* protected_frames_bytes,<br>```size_t``* protected_frames_bytes_size,
unsigned``char``* unprotected_bytes,`<br>size_t* unprotected_bytes_size);`<br>```void(destroy)(tsi_frame_protector self);<br>};`

Decodificando as mensagens do PubSub

Depois de capturar os quadros HTTP/2 brutos, o próximo passo era decodificá-los. Isso passava por identificar os cabeçalhos gRPC, que vêm precedidos por um campo de tamanho e uma flag de compressão, seguidos pela mensagem codificada em Protobuf. Estendemos nosso decodificador HTTP/2 para dar conta disso.

Veja um resumo simplificado do processo:

1. Interceptar o tráfego gRPC: nosso probe eBPF captura os dados em texto claro a partir das funções TSI do gRPC.

2. Decodificar os quadros HTTP/2: extraímos os cabeçalhos das mensagens gRPC e as mensagens Protobuf que vêm junto.

3. Fazer o parsing dos dados Protobuf: usando o protocolo do PubSub, decodificamos mensagens como `PublishRequest` e `AcknowledgeRequest`.

Isso nos permitiu dissecar mensagens do Google PubSub em tempo real, transformando nosso sensor em uma ferramenta poderosa para monitorar o tráfego de rede em ambientes GCP.

Ampliando o suporte para outras versões do gRPC

Um dos últimos obstáculos foi garantir compatibilidade com várias versões da biblioteca `grpcio` em Python. Implementamos uma lógica para localizar e analisar dinamicamente os símbolos usados nas diferentes releases do `grpcio`. Isso exigiu mergulhar em análise binária, e usamos métodos como parsing de símbolos de debug do DWARF, análise binária (contando, entre outros, com o framework Zanalysis e com pre-script/post-script do Ghidra etc.), engenharia reversa automatizada via scripting e extração de símbolos — tudo para automatizar o processo de identificar as funções relevantes em cada versão da biblioteca.

O futuro: escalando para além do Python

Nosso foco principal foi o Python, mas o framework gRPC é usado em várias linguagens de programação, incluindo C++, Java e Go. Ao conseguir dissecar mensagens gRPC baseadas em Python, criamos a base para estender esse suporte a outras linguagens com esforço relativamente pequeno. Cada nova linguagem vai exigir mais pesquisa sobre sua implementação de gRPC, mas os princípios centrais são os mesmos.

Conclusão: um novo nível de visibilidade

Com muita pesquisa, tentativa e erro e um entendimento profundo da pilha do Google PubSub e do gRPC, conseguimos construir um sistema robusto para interceptar e dissecar mensagens do PubSub. Essa solução não só amplia a visibilidade sobre os serviços do GCP como também prepara o terreno para um suporte mais amplo ao gRPC em diferentes linguagens e ambientes.

Com esses recursos em mãos, estamos mais bem preparados para monitorar, resolver problemas e otimizar o desempenho de serviços de nuvem que usam gRPC e PubSub. E, conforme seguimos ampliando o suporte para novos serviços, as possibilidades desse sensor só crescem.

Esperamos que este post ajude você na sua jornada de dissecar protocolos baseados em gRPC. Se quiser saber mais, é só chamar a gente!

E aqui vão algumas leituras complementares recomendadas:

GRPC Core Concepts GRPC Stacks

Interceptors Guide