Todos os produtos
Search
Central de documentação

Alibaba Cloud Service Mesh:Write a Wasm plug-in in Rust for an Envoy proxy in ASM

Última atualização: Jun 28, 2026

O Service Mesh (ASM) oferece suporte a plug-ins Wasm em proxies Envoy para processamento personalizado de requisições. Este tutorial demonstra como escrever um plug-in Wasm em Rust que inspeciona cabeçalhos de requisição HTTP e permite ou bloqueia requisições com base no resultado.

O plug-in verifica se uma requisição recebida contém o cabeçalho allow: true. Se o cabeçalho estiver ausente ou definido com qualquer outro valor, o plug-in retorna HTTP 403 com uma mensagem de erro personalizada. Caso o cabeçalho esteja presente e definido como true, a requisição segue para o serviço upstream.

Como funciona

Um plug-in Wasm é executado dentro do proxy Envoy como um módulo isolado. A especificação Proxy-Wasm define uma Interface Binária de Aplicação (ABI) padrão para comunicação entre o host do proxy e os módulos Wasm. Como o Proxy-Wasm é independente de proxy, os plug-ins desenvolvidos com essa ABI são portáteis para qualquer proxy que implemente a especificação.

O SDK do Proxy-Wasm para Rust organiza a lógica do plug-in em torno de três tipos de trait:

Trait

Função

RootContext

Gerencia o ciclo de vida e a configuração do plug-in. Cria novos contextos HTTP ou de stream para cada requisição.

HttpContext

Trata um único ciclo de requisição/resposta HTTP. Implementa callbacks como on_http_request_headers para inspecionar ou modificar o tráfego.

Context

Fornece funções utilitárias compartilhadas (acesso a propriedades, temporizadores, entre outras), herdadas pelos contextos raiz e HTTP.

Quando o Envoy recebe uma requisição HTTP, ele cria uma nova instância de HttpContext por meio do RootContext. Os callbacks do HttpContext são acionados em cada etapa do processamento da requisição, concedendo ao plug-in controle total sobre cabeçalhos, corpo e trailers.

Informações de fundo

O Wasm oferece desempenho de execução próximo ao nativo e roda em um sandbox seguro para memória, o que o torna ideal para estender o Envoy sem recompilar o binário do proxy. No entanto, linguagens com coleta de lixo integrada podem introduzir sobrecarga de desempenho no Wasm. Por esse motivo, linguagens com gerenciamento manual de memória — C++ e Rust — são as escolhas recomendadas.

Em comparação ao C++, o Rust oferece um fluxo de trabalho de compilação e build mais simples, embora tenha uma curva de aprendizado mais íngreme. Escolha com base na familiaridade da sua equipe com cada linguagem.

Pré-requisitos

Antes de começar, certifique-se de ter:

Visão geral do fluxo de trabalho

O processo completo consiste em cinco etapas:

  1. Configure o ambiente de desenvolvimento — Instale o toolchain do Rust e o alvo de compilação Wasm.

  2. Escreva o plug-in — Crie um projeto de biblioteca Rust, defina as dependências e implemente a lógica de inspeção de cabeçalhos de requisição.

  3. Compile e empacote — Compile o código Rust para um binário Wasm e empacote-o como uma imagem OCI.

  4. Implante — Envie a imagem para o Container Registry e aplique o recurso de plug-in Wasm no ASM.

  5. Verifique — Envie requisições de teste para confirmar se o plug-in bloqueia e permite o tráfego conforme esperado.

Etapa 1: Configurar o ambiente de desenvolvimento

  1. Instale o toolchain do Rust via rustup. Para instruções, consulte Instalar o Rust.

  2. Adicione o alvo de compilação wasm32-wasi. O alvo wasm32-wasi compila código Rust para WebAssembly com acesso à interface de sistema WASI, exigida pelo runtime Wasm do Envoy. Se o Rust já estiver instalado, atualize-o primeiro:

       rustup target add wasm32-wasi
       rustup update

Etapa 2: Escrever o plug-in

Esta etapa abrange três partes: criação do projeto, configuração de dependências e implementação da lógica do plug-in.

Criar o projeto

Crie um diretório chamado rust-example, acesse-o e inicialize uma nova biblioteca Rust:

mkdir rust-example && cd rust-example
cargo init --lib

Configurar dependências

Substitua o conteúdo de Cargo.toml pelo seguinte:

[package]
name = "rust-example"
version = "0.1.0"
edition = "2021"

[lib]
# Build as a C-compatible dynamic library so Envoy can load it
crate-type = ["cdylib"]

[dependencies]
log = "0.4.8"
proxy-wasm = "0.2.2"

O tipo de crate cdylib gera uma biblioteca dinâmica compatível com o runtime Wasm. As duas dependências são:

  • **log** — Fachada de logging padrão do Rust para saída de log estruturada.

  • **proxy-wasm** — O SDK do Proxy-Wasm para Rust, que fornece as definições de traits e bindings de funções do host.

Implementar a lógica do plug-in

O plug-in precisa:

  1. Registrar um RootContext que informe ao Envoy que este plug-in trata tráfego HTTP.

  2. Criar um HttpContext para cada requisição HTTP recebida, responsável por inspecionar os cabeçalhos da requisição.

  3. No callback on_http_request_headers, verificar a presença do cabeçalho allow: true. Bloqueie a requisição com HTTP 403 se o cabeçalho estiver ausente ou permita a passagem caso esteja presente.

Substitua o conteúdo de src/lib.rs por:

use log::info;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;

proxy_wasm::main! {{
    proxy_wasm::set_log_level(LogLevel::Trace);
    proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) });
}}

struct HttpHeadersRoot;

// Inherit shared utility functions (property access, timers, etc.)
impl Context for HttpHeadersRoot {}

impl RootContext for HttpHeadersRoot {
    fn get_type(&self) -> Option<ContextType> {
        Some(ContextType::HttpContext)
    }

    fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
        Some(Box::new(HttpHeaders { context_id }))
    }
}

struct HttpHeaders {
    context_id: u32,
}

impl Context for HttpHeaders {}

impl HttpContext for HttpHeaders {
    fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
        info!("#{} wasm-rust: on_http_request_headers", self.context_id);

        match self.get_http_request_header("allow") {
            Some(allow) if allow == "true" => {
                Action::Continue
            }
            _ => {
                info!("#{} wasm-rust: allow header not found or is not true, deny by default", self.context_id);
                self.send_http_response(
                    403,
                    vec![("Content-Type", "text/plain")],
                    Some(b"Forbidden by ASM Wasm Plugin, rust version\n"),
                );
                Action::Pause
            }
        }
    }
}

O código divide-se da seguinte forma:

Seção

Propósito

proxy_wasm::main!

Macro de ponto de entrada. Define o nível de log como Trace e registra HttpHeadersRoot como o contexto raiz.

HttpHeadersRoot + RootContext

Retorna ContextType::HttpContext para indicar ao Envoy que este plug-in processa tráfego HTTP. Cria uma nova instância de HttpHeaders para cada requisição.

HttpHeaders + HttpContext

Implementa on_http_request_headers. Lê o cabeçalho allow: retorna Action::Continue se o valor for "true"; caso contrário, envia uma resposta HTTP 403 e retorna Action::Pause para interromper a requisição.

Compilar o plug-in

Compile o plug-in com o alvo wasm32-wasi:

cargo build --target wasm32-wasi --release

Após uma compilação bem-sucedida, o binário Wasm estará em:

target/wasm32-wasi/release/rust_example.wasm

Etapa 3: Criar uma imagem OCI e enviá-la ao Container Registry

Empacote o binário Wasm como uma imagem OCI para que o ASM possa baixá-la e carregá-la.

  1. Crie um Dockerfile na raiz do projeto:

       FROM scratch
       # Copy the compiled Wasm binary into the image as plugin.wasm
       ADD target/wasm32-wasi/release/rust_example.wasm ./plugin.wasm
  2. Compile e envie a imagem. Para etapas detalhadas, consulte Criar uma imagem OCI do plug-in Wasm e enviá-la à instância do Container Registry Enterprise Edition. Substitua <your-registry> e <tag> pelo endereço real do seu registry e pela tag da imagem:

       docker build -t <your-registry>/wasm-rust-example:<tag> .
       docker push <your-registry>/wasm-rust-example:<tag>

Etapa 4: Aplicar o plug-in Wasm ao gateway de entrada

Configure o recurso WasmPlugin no ASM para carregar o plug-in no proxy Envoy do gateway de entrada.

Para o procedimento completo, consulte Aplicar o plug-in Wasm ao gateway de entrada. Certifique-se de que o campo url aponte para a imagem enviada na Etapa 3.

Etapa 5: Verificar o plug-in

Após implantar o plug-in, teste-o enviando requisições com e sem o cabeçalho allow: true.

Ativar logs de depuração

Use o kubeconfig do cluster do plano de dados para ativar logs de depuração Wasm no Pod do gateway:

kubectl -n istio-system exec <gateway-pod-name> -c istio-proxy -- \
  curl -XPOST "localhost:15000/logging?wasm=debug"

Substitua <gateway-pod-name> pelo nome real do seu Pod de gateway.

Testar sem o cabeçalho allow

Envie uma requisição sem o cabeçalho allow:

curl <ASM-gateway-IP>/status/418

Saída esperada:

Forbidden by ASM Wasm Plugin, rust version

O plug-in bloqueia a requisição e retorna HTTP 403.

Verificar logs do gateway

Inspecione os logs do Pod do gateway em busca de entradas semelhantes às seguintes:

2024-09-05T08:33:31.079869Z  info  envoy wasm  wasm log istio-system.header-authorization: #2 wasm-rust: on_http_request_headers
2024-09-05T08:33:31.079943Z  info  envoy wasm  wasm log istio-system.header-authorization: #2 wasm-rust: allow header not found or is not true, deny by default
{"authority_for":"xx.xx.xx.xx","bytes_received":"0","bytes_sent":"43","downstream_local_address":"xx.xx.xx.xx:80","downstream_remote_address":"xx.xx.xx.xx:xxxxx","duration":"0","istio_policy_status":"-","method":"GET","path":"/status/418","protocol":"HTTP/1.1","request_id":"d5250d1a-54b3-406d-8bea-5a51b617b579","requested_server_name":"-","response_code":"403","response_flags":"-","route_name":"httpbin","start_time":"2024-09-05T08:33:31.079Z","trace_id":"-","upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local","upstream_host":"-","upstream_local_address":"-","upstream_response_time":"-","upstream_service_time":"-","upstream_transport_failure_reason":"-","user_agent":"curl/8.9.0-DEV","x_forwarded_for":"xx.xx.xx.xx"}

Essas linhas de log confirmam que o plug-in foi executado e negou a requisição.

Testar com o cabeçalho allow

Envie uma requisição com allow: true:

curl <ASM-gateway-IP>/status/418 -H "allow: true"

Saída esperada:

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

A requisição passa pelo plug-in e chega ao HTTPBin, que retorna a resposta padrão HTTP 418 teapot. O plug-in está funcionando corretamente.

Próximos passos

  • Para escrever um plug-in Wasm em Go, consulte a versão deste tutorial em Go.

  • Explore mais exemplos de plug-ins no repositório do SDK proxy-wasm para Rust, incluindo modificação de corpo de resposta HTTP, carregamento de configuração e autenticação gRPC.