Writing MuleSoft Flex Gateway custom policies

Jose Ramon Huerga
Another Integration Blog
7 min readJan 12, 2023

Anypoint Flex Gateway provides several out-of-the-box policies that you can use to quick and easily apply a range of security, traffic management, and other controls to your APIs without having to write custom code. Some of those out-of-the-box policies include JWT validation, rate limiting, client credentials enforcement, etc.

However, sometimes those out-of-the-box policies do not meet exactly our requirements. Fortunately, for those scenarios it is possible to write our own custom policies, using the Rust programming language. Custom policies are based on proxy-wasm ABI, an event-driven, Envoy-agnostic, low-level interface for proxies. This interface specifies how a WASM (WebAssembly) extension and its host interact.

Configure a Rust development environment

To be able to write and compile a custom policy you will need a Rust development environment. Following steps describe how to download and configure Rust in Ubuntu.

Before running a Rust program, you must compile it. Build tools are required for compilation so make sure you have installed build-essential package in your system:

$ sudo apt-get update
$ sudo apt install build-essential

There are many ways to install Rust. The recommended way is to use the rustup shell script:

$ curl https://sh.rustup.rs -sSf | sh

Other alternative to rustup shell script is to use the official package manager for Ubuntu (sudo apt install rustc). In addition, another alternative is to use snap:

$ sudo snap install rustup –classic

Last step is to update to stable release:

$ rustup install stable
$ rustup default stable

Finally, you can use “cargo -V” and “rustup –V” to verify the installation:

Write the code

The first step to write a custom policy is to use this instruction to create a new Rust library:

$ cargo new custom_policy - lib

In this article, we are going to use an existing custom policy. That policy essentially parses the body response of the backend and checks if it contains a specific attribute; if that is the case, the policy masks the value of that attribute. You can find the source code in this GitHub repo.

That repo essentially contains five files (cargo.toml, README.md, definition.yaml, implementation.yaml, schema.json) and a folder “src”:

The folder “src” contains a file: lib.rs

TOML itself is a simple, ergonomic, and readable configuration format. The TOML format tends to be relatively common throughout the Rust community for configuration, notably being used by Cargo, Rust’s package manager. In this custom policy, the file cargo.toml contains dependencies to libraries such as proxy-wasm, serde_json, log, etc:

[package]
name = "flex_custom_policy_data_masking"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]
name="flex_custom_policy_data_masking"
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"
log = "0.4"

The definition metadata YAML file provides information about the policy and its requirements. In this custom policy, the file definition.yaml has this content:

#%Policy Definition 0.1
name: Custom Data Masking
description: Replaces an attribute in the JSON response body, masking its value.
category: Security
providedCharacteristics:
- Requires authentication
requiredCharacteristics: []
interfaceScope: ["api", "resource"]
interfaceTransformation: []
encryptionSupported: true
violationCategory: authentication

The implementation metadata YAML file provides details about the specific implementation of the policy definition. A single policy definition can have several implementations. Each an independent asset in Exchange or different versions of the same Exchange asset. In this example, the file implementation.yaml has this content:

#%Policy Implementation 1.0
minRuntimeVersion: 1.0.0
technology: flexGateway
name: Custom Data Masking Impl
releaseNotes: Initial version

The JSON schema file specifies the interface for the policy by identifying which properties are required or supported to configure the policy. The file also includes more annotations to refine the type for each property that enables some fields to perform a special behavior in the Anypoint platform. In this article, the file schema.json contains the definition of an attribute named “field-name” that will be used to identify the field in the JSON payload that is going to be masked:

{
"title": "Custom Data Masking",
"type": "object",
"description": "Replaces an attribute in the JSON response body, masking its value.",
"properties": {
"field-name": {
"title": "Custom Data Masking Field",
"type": "string"
}
},
"required": [
"field-name"
],
"unevaluatedProperties": false,
"@context": {
"@vocab": "anypoint://vocabulary/policy.yaml#",
"security": "anypoint://vocabulary/policy.yaml#"
},
"$id": "custom-data-masking-simple",
"$schema": "https://json-schema.org/draft/2019-09/schema"
}

Finally, the most important file is the implementation of the policy in Rust. In this case, the file lib.rs contains several functions and methods, for example “on_http_response_body” (that takes care of intercepting the response body and “transform” (that is responsible of converting the body payload to JSON and masking the desired field with the symbol #):

impl HttpContext for HttpConfigHeader {
fn on_http_response_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
info!("on_http_response_body");
if !_end_of_stream {
info!("on_http_response_body wait end of stream");
return Action::Pause;
}
if let Some(body_bytes) = self.get_http_response_body(0, _body_size) {
info!("on_http_response_body wait read body");
let body_str = String::from_utf8(body_bytes).unwrap();
let body_str_new = transform (body_str,String::from(self.field_name.as_mut()));
self.set_http_response_body(0, _body_size, &body_str_new.into_bytes());
}
Action::Continue
}
}
fn transform (input: String, field: String) -> String {
info!("transform function");
let mut v: Value = serde_json::from_str(input.as_str()).unwrap();
if let Some(_field_value) = v.get(field.as_str()) {
info!("transform function field found");
v[field] = serde_json::Value::String("############".to_owned());
}
return v.to_string();
}

Compile the code

The first step to compile the code is to download it using git:

$ git clone https://github.com/jrhuerga/mule-flex-data-masking.git

Rust supports a great number of platforms. To compile to other platforms you must install other target platforms. This is done with the rustup target add command. As an Anypoint Flex Gateway policy uses the WASM platform, we need to install using this command:

$ rustup target add wasm32-unknown-unknown

Finally, we will compile the code using cargo build:

$ cargo build - target wasm32-unknown-unknown - release

After the compilation is done, a file flex_custom_policy_data_masking.wasm will be generated under the folder target.

Publish policy

Once that the custom policy has been successfully compiled, we will upload it to Exchange, so it will be available to be applied to Anypoint Flex Gateways. The publishing to Exchange is done in two steps: 1) Publish the definition of the policy. 2) Publish the implementation of the policy.

This screenshot shows how to publish the definition of the policy. We will use the files schemas.json and definition.yaml.

Once that the definition of the policy has been published, we will add an implementation. This screenshot shows how to publish an implementation, using the file obtained after the compilation of the custom policy (flex_custom_policy_data_masking.wasm) and the file implementation.yaml.

Configure API

Once that we have published in Exchange the definition and the implementation of the custom policy, we are ready to apply it to APIs published in Anypoint Flex Gateway. In this case, the custom policy will need to be configured with the name of a JSON field that we want to be masked with # symbols. In this example, we are going to use the Star Wars API, masking the content of the field “gender”:

Test

Once that we have applied the custom policy, it is time to test it. This screenshot taken from Postman show the result of a call to the Star Wars API before the custom policy had been applied. Note the content of the field “gender”:

This screenshot has been taken after the custom policy has been applied. Now, the “gender” attribute content has been masked with “######”:

Summary

This article has explained how to use the Rust programming language to write a custom policy for Anypoint Flex Gateway. If you want to try these steps by yourself, you can find the source code of the policy in this link.

--

--