A middleware plugin for Traefik that integrates Treblle's API monitoring and logging services. Built in Rust and compiled to WebAssembly (WASM) for high performance and compatibility with Traefik v3.1 or newer, using either wasm-wasi
or wasm-wasi1p
as the target.
The plugin collects data from Traefik's request/response lifecycle, masks sensitive information, and sends the sanitized data to Treblle's API for monitoring. This plugin is designed to be lightweight, efficient, and easy to install through the Traefik catalog.
sequenceDiagram
participant Client
participant Traefik
participant WASM Plugin
participant Backend Service
participant Treblle API
Client->>Traefik: HTTP Request
Traefik->>Backend Service: Forward Request
Backend Service->>WASM Plugin: handle_request()
WASM Plugin->>WASM Plugin: Check blacklist & content type
alt Route not blacklisted & JSON content
WASM Plugin->>WASM Plugin: Extract & mask request data
WASM Plugin->>Treblle API: Send request data (async)
end
WASM Plugin-->>Backend Service: Continue processing
Backend Service->>WASM Plugin: handle_response()
WASM Plugin->>WASM Plugin: Extract & mask response data
WASM Plugin->>Treblle API: Send response data (async)
WASM Plugin-->>Backend Service: Finish processing
Backend Service-->>Traefik: HTTP Response
Traefik-->>Client: Forward Response
- Data Ingestion: Captures request and response data from Traefik and sends it to Treblle via a POST request in JSON format.
- Sensitive Data Masking: Automatically masks sensitive data such as passwords, credit card numbers, and other user-defined fields before sending data to Treblle.
- Customizable Masking: Users can define additional custom keywords for masking sensitive data.
- Route Blacklisting: Allows users to define specific routes or regex patterns to exclude from data collection and reporting.
- Silent Error Handling: Ensures that any errors in the plugin do not interfere with the host application's functionality.
- Fire-and-Forget: The plugin sends data without waiting for a response, ensuring minimal impact on performance.
- WASM-WASI1P Compatible: Built using WebAssembly (WASM) for high performance and compatibility with support for outgoing HTTP requests.
The project consists of several components:
- Traefik Middleware Plugin (
treblle-wasm-plugin
): A Rust-based WASM module that integrates with Traefik v3.1 or newer.- This version is important because Traefik v3.1 introduced enhanced support for WASM plugins required by this project.
- Producer Service (
producer
): Generates various HTTP requests, including JSON, plain text, and XML. - Consumer Service (
consumer
): Receives and processes the requests, including handling different routes. - Treblle API (
treblle-api
): A mock API that receives and logs the processed data.
- Docker and Docker Compose
- Rust (version specified in
.env
file) rustup
- Homebrew (if on macOS)
make
-
Clone this repository:
git clone [email protected]:momo-gg/traefik-rust-http-wasm-middleware-treblle.git cd traefik-rust-http-wasm-middleware-treblle
-
Install required tools:
-
If you're on macOS and don't have Homebrew, install it from brew.sh.
-
Install CMake and Ninja (required for building wabt):
brew install cmake ninja
-
Optionally install pthread-stubs for threading support:
brew install pthread-stubs
-
-
Install Rust and required components:
- The project uses a specific Rust version defined in the
.env
file. - The
rust-toolchain.toml
file in the root of the repository helps manage the Rust version and components. - If you don't have rustup, install it from rustup.rs.
- The project uses a specific Rust version defined in the
-
Install WASM tools:
./wasm-tools-install.sh
-
Build and run the project:
make all
The project uses a Makefile to manage building and running. Here are the main targets:
make all
: (or simplymake
) Default target. Checks Rust version, builds the plugin, and runs the services.make run
: Runs the Docker Compose services.make stop
: Stops the Docker Compose services.make check-rust-version
: Verifies the installed Rust version matches the required version.make generate-rust-toolchain
: Generates therust-toolchain.toml
file.make build-plugin
: Builds the WASM plugin.make validate-plugin
: Validates the WASM plugin exports.make build-run
: Builds and runs the Docker Compose services.make clean
: Cleans up Docker resources and build artifacts.make restart
: Cleans up and rebuilds everything.
The Traefik configuration is defined in traefik.yml
and traefik_dynamic.yml
. You can modify these files to adjust Traefik's behavior and the plugin settings.
The plugin configuration is located in traefik_dynamic.yml
under the http.middlewares.treblle-middleware.plugin.treblle
section. You can adjust the following settings:
treblleApiUrls
: List of URLs of the Treblle API to be used in round-robin fashion.apiKey
: Your Treblle API keyprojectId
: Your Treblle project IDrouteBlacklist
: List of routes to exclude from processing (e.g., ["/blacklisted-example"])sensitiveKeysRegex
: Regex pattern for masking sensitive data
http:
middlewares:
my-treblle-middleware:
plugin:
treblle:
apiKey: "your_api_key_here"
projectId: "your_project_id_here"
routeBlacklist:
["/ping", "/healthcheck", "/blacklisted-example"]
sensitiveKeysRegex: "(?i)(password|pwd|secret|password_confirmation|cc|card_number|ccv|ssn|credit_score)"
Once the services are running:
-
The Producer service will start generating random HTTP requests to the Consumer service, including:
- JSON requests (with and without sensitive payloads) to
/consume
- Plain text requests to
/consume
- XML requests to
/consume
- JSON requests to
/blacklisted-example
(which should be ignored by the middleware)
- JSON requests (with and without sensitive payloads) to
-
The Traefik middleware will intercept these requests, process them, and forward the data to the Treblle API if:
- The request is not to a blacklisted route
- The content type is JSON
-
The Consumer service will log all received requests, regardless of their processing by the middleware.
-
The Treblle API will receive and log the processed data from valid requests, as well as prepare Prometheus metrics for ingestion.
To update the Rust version used in the project:
- Modify the
RUST_VERSION
in the.env
file. - Run
make generate-rust-toolchain
to update therust-toolchain.toml
file. - Run
make check-rust-version
to verify the installed Rust version.
The WASM plugin source code is located in the treblle-wasm-plugin
directory. After making changes:
- Run
make build-plugin
to rebuild the plugin. - Run
make validate-plugin
to ensure the required exports are present. - Use
make restart
to apply the changes to the running services.
The middleware plugin implements a specific pattern for handling request bodies, which is important to understand:
-
Reading the Request Body: The middleware reads the request body using the
host_read_request_body
function. This operation "consumes" the body, meaning it's no longer available for subsequent middleware or the final handler (Consumer service). -
Processing the Body: After reading, the body is processed (e.g., sent to the Treblle API for analysis).
-
Writing the Body Back: To ensure the original request body is available for the rest of the request processing pipeline, the middleware writes the body back using the
host_write_request_body
function.
-
Body Consumption: In many HTTP proxy systems, reading the body often involves buffering the entire content, which can be resource-intensive for large payloads. Once read, the original stream is typically closed to free up resources.
-
WASM Module Isolation: The WASM module runs in an isolated environment. Reading the body copies the data into the module's memory, triggering the consumption in the host environment (Traefik).
-
Preserving Original Behavior: By writing the body back, we ensure that subsequent middleware and the final handler can access the body content, maintaining the expected behavior of the HTTP request.
The relevant code for this process is in the handle_request
function:
let body = match host_read_request_body() {
Ok(body) => {
host_log(
LOG_LEVEL_INFO,
&format!("Successfully read body: {} bytes", body.len()),
);
body
}
Err(e) => {
host_log(
LOG_LEVEL_ERROR,
&format!("Failed to read request body: {}", e),
);
return Err(TreblleError::HostFunction(format!(
"Failed to read request body: {}",
e
)));
}
};
// ... process the body ...
if let Err(e) = host_write_request_body(body.as_bytes()) {
host_log(
LOG_LEVEL_ERROR,
&format!("Failed to write request body back: {}", e),
);
return Err(TreblleError::HostFunction(format!(
"Failed to write request body back: {}",
e
)));
}
This pattern allows the middleware to inspect and potentially modify the body while ensuring that the original (or modified) body is still available for the rest of the request processing pipeline.
The project includes several test scenarios:
- JSON Requests: Sent to
/consume
, these should be processed by the middleware and forwarded to the Treblle API. - Non-JSON Requests: Sent to
/consume
, these should be ignored by the middleware but still reach the Consumer. - Blacklisted Route: Requests to
/blacklisted-example
should be ignored by the middleware but reach the Consumer. - Sensitive Data: JSON requests containing sensitive information (like passwords or credit card numbers) should be masked before being sent to the Treblle API.
To verify these scenarios, check the logs of the Consumer service and the Treblle API after running the system for a while.
If you encounter any issues:
- Check the logs of each service using
docker compose logs [service_name]
. - Ensure all services are running with
docker compose ps
. - Verify the Traefik configuration in
traefik.yml
andtraefik_dynamic.yml
. - Check the Rust code in the middleware and Treblle API for any errors.
- Run
make check-rust-version
to ensure you're using the correct Rust version. - If you've modified the WASM plugin, run
make validate-plugin
to check for required exports.
Contributions are welcome! Please fork the repository and submit a pull request with your changes.