概要
WebAssembly (WASM) 拡張機能を使用して、Envoy 上でカスタム検索条件として機能するカスタムポリシーを実装できます。これらのポリシーは、 WebAssembly for Proxies (Rust SDK) を使用して実装します。以下の例では、Rust と関連ツールに関する知識があることを前提としています。
ライフサイクルイベント
カスタムポリシーは、Envoy に依存しないイベント駆動型の L4/L7 プロキシ用ローレベルインターフェースである proxy-wasm ABI に基づいています。このインターフェースは、WASM 拡張機能とホストがやり取りする方法を指定し、リスン可能なライフサイクルイベントである HTTP 要求ライフサイクルイベントに加えて WASM 検索条件ライフサイクルイベントも使用します。
SDK は、各ライフサイクルイベントに関連した情報を取得するためのメソッドを提供します。たとえば、on_http_request_headers イベントから取得されるデータを使用して、関連するカスタムポリシーロジックを実行でき、その結果によって、要求を処理する必要があるかどうかをランタイムに通知できます。
HTTP 要求ライフサイクルイベント
-
on_http_request_headersエンドポイントが完全な HTTP リクエストヘッダーのセットを受け取るとトリガーされます。
-
on_http_request_bodyエンドポイントが HTTP 要求の最初の数バイトを受け取るとトリガーされます。
-
on_http_response_headersエンドポイントが完全な HTTP レスポンスヘッダーのセットを受け取るとトリガーされます。
-
on_http_response_bodyエンドポイントが HTTP レスポンスボディの最初の数バイトを受け取るとトリガーされます。
ライフサイクルイベントの絞り込み
-
on_configureWASM 検索条件が使用可能な設定で開始されるとトリガーされます。
Flex Gateway は、ポリシー設定を JSON にシリアル化し、シリアル化された設定は Envoy WASM 検索条件の設定に使用されます。この JSON をカスタムポリシーで使用されるデータに非シリアル化して解析できます。
ライフサイクルイベントの実装例
次の Envoy 検索条件テンプレートは、ライフサイクルイベントの実装方法を示しています。
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(HttpConfigHeaderRoot {
header_content: String::new(),
})
});
}}
struct HttpConfigHeader {
header_content: String,
}
impl Context for HttpConfigHeader {}
impl HttpContext for HttpConfigHeader {
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
Action::Continue
}
fn on_http_request_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
Action::Continue
}
fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
Action::Continue
}
fn on_http_response_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
Action::Continue
}
}
struct HttpConfigHeaderRoot {
header_content: String,
}
impl Context for HttpConfigHeaderRoot {}
impl RootContext for HttpConfigHeaderRoot {
fn on_configure(&mut self, _: usize) -> bool {
if let Some(config_bytes) = self.get_plugin_configuration() {
self.header_content = String::from_utf8(config_bytes).unwrap()
}
true
}
fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(HttpConfigHeader {
header_content: self.header_content.clone(),
}))
}
fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
}
カスタム認証ヘッダーポリシーの例
次のチュートリアルでは、すでに Exchange にパブリッシュされているポリシー定義のポリシー実装の例を作成する方法を示しています。
このポリシー例では、x-custom-auth ヘッダーがユーザー設定の値と一致しない要求をブロックしています。
ポリシー開発プロセスは、以下のステップで行います。
-
ポリシー定義 JSON ファイルを作成する。
-
プロジェクトをセットアップする。
-
カスタムポリシーを開発する。
ポリシー定義 JSON ファイルの作成
ポリシー実装例では、ポリシー定義 JSON ファイルが必要です。Flex Gateway は、ポリシー設定をポリシー定義構造と一緒に JSON として Envoy WASM 検索条件に渡します。
認証ヘッダーポリシーの例には、ヘッダーで渡す必要のある値のパラメーターのみを使用しています。ポリシー定義 JSON ファイルは次の内容と一致している必要があります。
{
"title": "Custom Auth Header",
"type": "object",
"description": "Enforces HTTP authentication matching x-custom-auth value to what is configured in the policy.",
"properties": {
"secret-value": {
"title": "Custom Auth Header Password",
"type": "string",
"@context": {
"@characteristics": [
"security:sensitive"
]
}
}
},
"required": [
"secret-value"
],
"unevaluatedProperties": false,
"@context": {
"@vocab": "anypoint://vocabulary/policy.yaml#",
"security": "anypoint://vocabulary/policy.yaml#"
},
"$id": "custom-auth-header-simple",
"$schema": "https://json-schema.org/draft/2019-09/schema"
}
プロジェクトのセットアップ
-
次のコマンドで Rust プロジェクトを作成します。
cargo new flex_custom_policy_example --lib
これにより
flex_custom_policy_example ディレクトリが作成されます。 -
次のコンテンツを
flex_custom_policy_example ディレクトリの Cargo.toml にコピーします。[package] name = "flex_custom_policy_example" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] name="flex_custom_policy_example" path="src/lib.rs" [dependencies] proxy-wasm = { git = "https://github.com/proxy-wasm/proxy-wasm-rust-sdk.git", tag = "v0.2.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" [profile.release] opt-level = "z" lto = "fat"
カスタムポリシーの開発
-
次のポリシーブートストラップコードを新しい Rust ソースファイルに追加します。
use proxy_wasm::traits::*; use proxy_wasm::types::*; use serde::Deserialize; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(CustomAuthRootContext { config: CustomAuthConfig::default(), }) }); }}このコードは、WASM 検索条件を Envoy にデプロイするために必要です。
main ブロックは、ルートコンテキストを設定します。このルートコンテキストは、受け取った各 HTTP 要求に対して子コンテキストを生成するために使用する Envoy エンティティです。 -
次のルートコンテキスト実装コードを追加します。
struct CustomAuthRootContext { config: CustomAuthConfig, } impl Context for CustomAuthRootContext {} impl RootContext for CustomAuthRootContext { fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> { Some(Box::new(CustomAuthHttpContext { config: self.config.clone(), })) } fn get_type(&self) -> Option<ContextType> { Some(ContextType::HttpContext) } }各 Envoy 検索条件は、ルートコンテキスト実装を提供するために必要です。
RootContext trait には、実装可能な便利なメソッドが含まれています。この例では、Envoy が子コンテキストを生成できるように、HTTP 検索条件は create_http_context と get_type を実装しています。 -
次の
Struct を追加して、この JSON の解析を有効にします。#[derive(Default, Clone, Deserialize)] struct CustomAuthConfig { #[serde(alias = "secret-value")] secret_value: String, }Flex Gateway は、ポリシー定義で指定されている JSON を使用してポリシーを設定します。解析が必要な JSON には
secret-value という項目が 1 つあります。 -
設定を非シリアル化します。
基本的なポリシー設定構造を作成したら、逆シリアル化するために
RootContexton_configure メソッドを実装する必要があります。CustomAuthRootContext の RootContext の実装内で、次のスニペットのように on_configure メソッドを追加します。impl RootContext for CustomAuthRootContext { fn on_configure(&mut self, _: usize) -> bool { if let Some(config_bytes) = self.get_plugin_configuration() { self.config = serde_json::from_slice(config_bytes.as_slice()).unwrap(); } true } // Other implemented methods // ... } -
次の HTTP コンテキストコードを追加します。
struct CustomAuthHttpContext { pub config: CustomAuthConfig, } impl Context for CustomAuthHttpContext {} impl HttpContext for CustomAuthHttpContext {}受け取った各要求によって新しい
CustomAuthHttpContext が作成されます。この CustomAuthHttpContext は、HTTP 要求が有効である間は有効です。これにより (要求間の状態データではなく) 要求に関連した状態データを格納できます。 -
次のコアポリシー機能ロジックを追加します。
impl HttpContext for CustomAuthHttpContext { fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { if let Some(value) = self.get_http_request_header("x-custom-auth") { if self.config.secret_value == value { return Action::Continue; } } self.send_http_response(401, Vec::new(), None); Action::Pause } }proxy-wasm ABI は、受け取ったリクエストヘッダー値を取得して、値を secret-value と比較します。secret-value がヘッダーと一致すると、実装は Action::Continue を返します。そうでない場合は Action::Pause を返し、検索条件自身が send_http_response を使用して呼び出し側のクライアントに応答を返します。注意: Rust SDK には、
get_http_request_header を使用した非 UTF-8 値の読み込みの問題があります。この問題を回避するには、get_http_request_headers_bytes メソッドを使用し、from_utf8メソッドを使用してバイトを読み取ります。次に例を示します。let header = self.get_http_request_header_bytes("x-custom-auth").map(String::from_utf8).and_then(Result::ok); -
wasm32 を対象として追加して、コンパイルを有効にします。rustup target add wasm32-wasi
ポリシーに含まれるすべてのサードパーティライブラリは、
wasm32-wasi Rust コンパイルターゲットと互換性がある必要があります。一部のライブラリは wasm32-wasi ターゲットと適切に互換性があっても、Flex Gateway にデプロイすると適切に動作しないことがあります。この状況が発生した場合のデプロイメントエラーの例は次のとおりです。
-
Failed to load Wasm module due to a missing import (インポートが欠落しているため Wasm モジュールの読み込みに失敗しました) -
Wasm VM failed to initialize Wasm code (Wasm VM が Wasm コードの初期化に失敗しました) -
Plugin configured to fail closed failed to load (フェールクローズに設定されたプラグインの読み込みに失敗しました)
これらの問題を解決するには、サードパーティライブラリのオーナーに問い合わせるか、別のライブラリを使用してください。
-
-
次のコマンドでカスタムポリシーをコンパイルします。
cargo build --target wasm32-wasi --release
コンパイルにより、バイナリ
.wasm ファイルが ./target/wasm32-wasi/release ディレクトリに出力されます。Exchange へのカスタムポリシーのパブリッシュついての詳細は、「Flex Gateway または Mule 4 カスタムポリシーのパブリッシュ」を参照してください。