Safe Rust bindings to OWASP Coraza Web Application Firewall, based on the official C bindings.
Coraza is a Go-based WAF compatible with ModSecurity's SecLang. This repository provides Rust crates to embed Coraza into Rust applications.
| Crate | Description |
|---|---|
coraza |
Safe, idiomatic Rust API |
coraza-sys |
Raw FFI bindings (auto-generated from C header) |
examples |
Usage examples |
use coraza::WafConfig;
fn main() {
let waf = WafConfig::new()
.unwrap()
.with_directives("SecRuleEngine DetectionOnly")
.with_directives("SecRequestBodyAccess On")
.build()
.unwrap();
let mut tx = waf.new_transaction();
tx.process_connection("127.0.0.1", 8080, "localhost", 80).unwrap();
tx.process_uri("/path", "GET", "HTTP/1.1").unwrap();
tx.add_request_header("Host", "localhost");
tx.process_request_headers().unwrap();
tx.process_logging();
if let Some(intervention) = tx.intervention() {
eprintln!("Blocked with status {}", intervention.status);
}
}- Rust 1.75+ (edition 2021)
- Go 1.21+ (for compiling the Coraza WAF engine)
- C compiler (gcc/clang on Unix, MSVC on Windows)
- libclang (for bindgen)
On Ubuntu/Debian:
sudo apt install golang-go libclang-devOn macOS:
brew install go llvmcargo buildThe first build compiles the Go WAF engine into a static library and generates Rust FFI bindings. Subsequent builds are fast.
cargo run --bin simple_get
cargo run --bin deny_rule
cargo run --bin with_callbacksMinimal WAF usage with DetectionOnly mode — processes a GET request and checks for interventions.
WAF with a deny rule that blocks requests from specific IPs — demonstrates handling a 403 intervention.
Registers debug log and error callbacks — shows how to capture matched rule details.
use coraza::{WafConfig, LogLevel};
let waf = WafConfig::new()?
.with_directives("SecRuleEngine On")
.with_directives("SecRequestBodyAccess On")
.with_debug_log_callback(|level, msg, fields| {
eprintln!("[{level}] {msg} {fields}");
})
.with_error_callback(|rule| {
eprintln!("Rule {} matched: {}", rule.rule_id, rule.message);
})
.build()?;Transactions follow Coraza's phase-based lifecycle:
let mut tx = waf.new_transaction();
// Phase 0: Connection & URI
tx.process_connection("127.0.0.1", 8080, "localhost", 80)?;
tx.process_uri("/path", "GET", "HTTP/1.1")?;
// Phase 1: Request headers
tx.add_request_header("Host", "localhost");
tx.process_request_headers()?; // may return Err(Intervention)
// Phase 2: Request body (if SecRequestBodyAccess On)
tx.append_request_body(b"hello=world")?;
tx.process_request_body()?;
// Phase 3: Response headers
tx.process_response_headers(200, "HTTP/1.1")?;
// Phase 4: Response body (if SecResponseBodyAccess On)
tx.append_response_body(b"<html>...</html>")?;
tx.process_response_body()?;
// Phase 5: Logging (always run, even on interruption)
tx.process_logging();
// Check for rule matches
if let Some(intervention) = tx.intervention() {
eprintln!("Blocked: {}", intervention.status);
}| Type | Send |
Sync |
Notes |
|---|---|---|---|
Waf |
Yes | Yes | Immutable after creation; safe to share for concurrent transaction creation |
WafConfig |
Yes | No | Builder consumed by build() |
Transaction |
Yes | No | Mutable state; must not be shared across threads |
Licensed under the Apache License, Version 2.0.