Tous les produits
Search
Centre de documentation

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

Dernière mise à jour :Aug 11, 2026

Service Mesh (ASM) prend en charge les plug-ins Wasm dans les proxys Envoy pour le traitement personnalisé des requêtes. Ce tutoriel vous guide pas à pas dans la rédaction d'un plug-in Wasm en Rust qui inspecte les en-têtes de requête HTTP et autorise ou bloque les requêtes selon le résultat.

Le plug-in vérifie si une requête entrante contient l'en-tête allow: true. Si l'en-tête est absent ou défini sur une autre valeur, le plug-in renvoie un code HTTP 403 accompagné d'un message d'erreur personnalisé. Si l'en-tête est présent et défini sur true, la requête est transmise au service amont.

Fonctionnement

Un plug-in Wasm s'exécute au sein du proxy Envoy sous forme de module isolé. La spécification Proxy-Wasm définit une interface binaire d'application (ABI) standard pour la communication entre l'hôte du proxy et les modules Wasm. Étant donné que Proxy-Wasm est indépendant du proxy, les plug-ins construits sur cette ABI sont portables sur tout proxy implémentant la spécification.

Le SDK Rust Proxy-Wasm organise la logique du plug-in autour de trois types de traits :

Trait Rôle
RootContext Gère le cycle de vie et la configuration du plug-in. Crée de nouveaux contextes HTTP ou de flux pour chaque requête.
HttpContext Gère un seul cycle de requête/réponse HTTP. Implémentez des rappels tels que on_http_request_headers pour inspecter ou modifier le trafic.
Context Fournit des fonctions utilitaires partagées (accès aux propriétés, minuteries, etc.) héritées par les contextes racine et HTTP.

Lorsqu'Envoy reçoit une requête HTTP, il crée une nouvelle instance de HttpContext via le RootContext. Les rappels de HttpContext se déclenchent à chaque étape du traitement de la requête, offrant au plug-in un contrôle total sur les en-têtes, le corps et les trailers.

Informations contextuelles

Wasm offre des performances d'exécution proches du natif et s'exécute dans un sandbox sécurisé au niveau de la mémoire, ce qui le rend idéal pour étendre Envoy sans recompilation du binaire du proxy. Toutefois, les langages dotés d'un ramasse-miettes intégré peuvent engendrer une surcharge de performance dans Wasm. Pour cette raison, les langages avec gestion manuelle de la mémoire — C++ et Rust — sont recommandés.

Par rapport au C++, Rust propose un flux de compilation et de construction plus simple, bien que sa courbe d'apprentissage soit plus raide. Faites votre choix en fonction de la maîtrise de chaque langage au sein de votre équipe.

Prérequis

Avant de commencer, assurez-vous de disposer des éléments suivants :

Vue d'ensemble du workflow

Le processus de bout en bout comprend cinq étapes :

  1. Configurer l'environnement de développement — Installez la chaîne d'outils Rust et la cible de compilation Wasm.

  2. Rédiger le plug-in — Créez un projet de bibliothèque Rust, définissez les dépendances et implémentez la logique d'inspection des en-têtes de requête.

  3. Construire et empaqueter — Compilez le code Rust en binaire Wasm et empaquetez-le sous forme d'image OCI.

  4. Déployer — Poussez l'image vers Container Registry et appliquez la ressource de plug-in Wasm dans ASM.

  5. Vérifier — Envoyez des requêtes de test pour confirmer que le plug-in bloque et autorise le trafic comme prévu.

Étape 1 : Configurer l'environnement de développement

  1. Installez la chaîne d'outils Rust via rustup. Pour obtenir des instructions, consultez la page Install Rust.

  2. Ajoutez la cible de compilation wasm32-wasi : La cible wasm32-wasi compile le code Rust en WebAssembly avec accès à l'interface système WASI, attendue par le runtime Wasm d'Envoy. Si Rust est déjà installé, commencez par le mettre à jour :

       rustup target add wasm32-wasi
       rustup update

Étape 2 : Rédiger le plug-in

Cette étape couvre trois parties : création du projet, configuration des dépendances et implémentation de la logique du plug-in.

Créer le projet

Créez un répertoire nommé rust-example, accédez-y et initialisez une nouvelle bibliothèque Rust :

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

Configurer les dépendances

Remplacez le contenu de Cargo.toml par ce qui suit :

[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"

Le type de crate cdylib produit une bibliothèque dynamique compatible avec le runtime Wasm. Les deux dépendances sont :

  • **log** — Interface de journalisation Rust standard pour une sortie de journal structurée.

  • **proxy-wasm** — Le SDK Rust Proxy-Wasm, qui fournit les définitions de traits et les liaisons de fonctions hôte.

Implémenter la logique du plug-in

Le plug-in doit :

  1. Enregistrer un RootContext qui indique à Envoy que ce plug-in gère le trafic HTTP.

  2. Pour chaque requête HTTP entrante, créer un HttpContext qui inspecte les en-têtes de la requête.

  3. Dans le rappel on_http_request_headers, vérifier la présence de l'en-tête allow: true. Bloquer la requête avec un code HTTP 403 si l'en-tête est absent, ou la laisser passer s'il est présent.

Remplacez le contenu de src/lib.rs par :

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
            }
        }
    }
}

Le code se décompose comme suit :

Section Objectif
proxy_wasm::main! Macro de point d'entrée. Définit le niveau de journalisation sur Trace et enregistre HttpHeadersRoot en tant que contexte racine.
HttpHeadersRoot + RootContext Renvoie ContextType::HttpContext pour indiquer à Envoy que ce plug-in traite le trafic HTTP. Crée une nouvelle instance de HttpHeaders pour chaque requête.
HttpHeaders + HttpContext Implémente on_http_request_headers. Lit l'en-tête allow : renvoie Action::Continue si la valeur est "true", sinon envoie une réponse HTTP 403 et renvoie Action::Pause pour arrêter la requête.

Compiler le plug-in

Compilez le plug-in avec la cible wasm32-wasi :

cargo build --target wasm32-wasi --release

Après une compilation réussie, le binaire Wasm se trouve à l'emplacement suivant :

target/wasm32-wasi/release/rust_example.wasm

Étape 3 : Construire une image OCI et la pousser vers Container Registry

Empaquetez le binaire Wasm sous forme d'image OCI afin qu'ASM puisse l'extraire et la charger.

  1. Créez un Dockerfile à la racine du projet :

       FROM scratch
       # Copy the compiled Wasm binary into the image as plugin.wasm
       ADD target/wasm32-wasi/release/rust_example.wasm ./plugin.wasm
  2. Construisez et poussez l'image. Pour connaître la procédure détaillée, consultez la rubrique Créer une image OCI du plug-in Wasm et la pousser vers l'instance Container Registry Enterprise Edition. Remplacez <your-registry> et <tag> par l'adresse réelle de votre registre et le tag de votre image :

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

Étape 4 : Appliquer le plug-in Wasm à la passerelle ingress

Configurez la ressource WasmPlugin dans ASM pour charger le plug-in dans le proxy Envoy de la passerelle ingress.

Pour la procédure complète, consultez la rubrique Appliquer le plug-in Wasm à la passerelle ingress. Assurez-vous que le champ url pointe vers l'image que vous avez poussée à l'étape 3.

Étape 5 : Vérifier le plug-in

Après le déploiement du plug-in, testez-le en envoyant des requêtes avec et sans l'en-tête allow: true.

Activer la journalisation de débogage

Utilisez le kubeconfig du cluster du plan de données pour activer les journaux de débogage Wasm sur le Pod de la passerelle :

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

Remplacez <gateway-pod-name> par le nom réel de votre Pod de passerelle.

Tester sans l'en-tête allow

Envoyez une requête sans l'en-tête allow :

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

Résultat attendu :

Forbidden by ASM Wasm Plugin, rust version

Le plug-in bloque la requête et renvoie un code HTTP 403.

Consulter les journaux de la passerelle

Examinez les journaux du Pod de la passerelle pour repérer des entrées similaires à celles-ci :

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"}

Ces lignes de journal confirment que le plug-in s'est exécuté et a refusé la requête.

Tester avec l'en-tête allow

Envoyez une requête avec allow: true :

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

Résultat attendu :

    -=[ teapot ]=-

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

La requête est transmise à HTTPBin, qui renvoie la réponse standard HTTP 418 teapot. Le plug-in fonctionne correctement.

Étapes suivantes

  • Pour rédiger un plug-in Wasm en Go plutôt qu'en Rust, consultez la version Go de ce tutoriel.

  • Explorez davantage d'exemples de plug-ins dans le dépôt du SDK Rust proxy-wasm, y compris la modification du corps de la réponse HTTP, le chargement de la configuration et l'authentification gRPC.