Wazuh Client for Rust

A comprehensive Rust client library for interacting with Wazuh API and Wazuh Indexer. This library provides a type-safe, async interface for managing Wazuh deployments, agents, rules, and security monitoring.
Features
- 🚀 Async/Await Support - Built on tokio for high-performance async operations
- 🔒 Type Safety - Strongly typed API with comprehensive error handling
- 🛡️ Security First - Support for TLS/SSL with certificate validation
- 📊 Comprehensive API Coverage - Full Wazuh Manager API support plus core Indexer operations
- 🔧 Flexible Configuration - Easy configuration with builder patterns
Supported Wazuh Components
Wazuh Manager API
- Agent Management - Add, remove, configure, and monitor agents
- Rule Management - Create, update, and manage detection rules
- Cluster Operations - Monitor and manage cluster nodes
- Configuration Management - Update and retrieve configurations
- Active Response - Trigger and manage active responses
- Log Analysis - Query and analyze security logs
Wazuh Indexer
- Alert Queries - Search and retrieve security alerts
Quick Start
Add this to your Cargo.toml:
[dependencies]
wazuh-client = "0.1.0"
tokio = { version = "1.0", features = ["full"] }
Basic Usage
use wazuh_client_rs::{WazuhClientFactory, WazuhClients}; use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string()) );
let mut clients: WazuhClients = factory.create_all_clients();
let summary = clients.agents.get_agents_summary().await?;
println!("Total agents: {}", summary.connection.total);
println!("Active agents: {}", summary.connection.active);
let rules = clients.rules.get_rules(Some(5), None, None, None, None).await?;
println!("Fetched {} rules. First rule ID (if any): {:?}", rules.len(), rules.first().map(|r| r.id));
Ok(())
}
Agent Management
use wazuh_client_rs::{WazuhClientFactory, WazuhClients, agents::AgentAddBody};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string())
);
let mut clients: WazuhClients = factory.create_all_clients();
match clients.agents.get_agent("001").await {
Ok(agent) => {
println!("Agent 001 Status: {}", agent.status);
println!("Agent 001 Name: {}", agent.name);
}
Err(e) => {
eprintln!("Error getting agent 001: {}", e);
}
}
Ok(())
}
Rule Management
use wazuh_client_rs::{WazuhClientFactory, WazuhClients};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string())
);
let mut clients: WazuhClients = factory.create_all_clients();
let rules = clients.rules.get_rules(Some(5), None, None, None, None).await?;
println!("Fetched {} rules.", rules.len());
if let Some(rule) = rules.first() {
println!("Example Rule ID: {}, Description: {}", rule.id, rule.description);
}
let ssh_rules = clients.rules.get_rules_by_group("ssh").await?;
println!("Found {} rules in the 'ssh' group.", ssh_rules.len());
if let Some(rule) = ssh_rules.first() {
println!("Example SSH Rule ID: {}, Description: {}", rule.id, rule.description);
}
Ok(())
}
Cluster Monitoring
use wazuh_client_rs::{WazuhClientFactory, WazuhClients};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string())
);
let mut clients: WazuhClients = factory.create_all_clients();
match clients.cluster.get_cluster_status().await {
Ok(status) => {
println!("Cluster enabled: {}", status.enabled);
println!("Cluster running: {}", status.running);
}
Err(e) => {
eprintln!("Error getting cluster status (this is expected in non-clustered environments): {}", e);
}
}
match clients.cluster.get_cluster_nodes(None, None, None).await {
Ok(nodes) => {
if nodes.is_empty() {
println!("No cluster nodes found (or single node deployment).");
} else {
println!("Cluster Nodes:");
for node in nodes {
println!(" Node: {}, Type: {}, Status: {}", node.name, node.node_type, node.status);
}
}
}
Err(e) => {
eprintln!("Error getting cluster nodes (this is expected in non-clustered environments): {}", e);
}
}
Ok(())
}
Log Analysis with Indexer
use wazuh_client_rs::{WazuhClientFactory, WazuhClients}; use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string())
);
let clients: WazuhClients = factory.create_all_clients();
match clients.indexer.get_alerts().await {
Ok(alerts) => {
println!("Retrieved {} alerts from the indexer.", alerts.len());
if let Some(alert) = alerts.first() {
println!("Example alert (first 50 chars): {:.50}", serde_json::to_string(alert)?);
}
}
Err(e) => {
eprintln!("Error getting alerts from indexer: {}", e);
}
}
Ok(())
}
Configuration
Environment Variables
You can configure the client using environment variables:
export WAZUH_HOST="https://your-wazuh-manager.com"
export WAZUH_PORT="55000"
export WAZUH_USERNAME="wazuh"
export WAZUH_PASSWORD="your-password"
export WAZUH_VERIFY_SSL="true"
export WAZUH_INDEXER_HOST="your-wazuh-indexer.com"
export WAZUH_INDEXER_PORT="9200"
export WAZUH_INDEXER_USERNAME="admin"
export WAZUH_INDEXER_PASSWORD="admin"
Client Factory Initialization
The WazuhClientFactory is used to configure and create clients.
use wazuh_client_rs::WazuhClientFactory;
use std::env;
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false), Some("https".to_string()) );
Error Handling
The library provides comprehensive error handling with detailed error types:
use wazuh_client_rs::{WazuhClientFactory, WazuhClients, WazuhApiError};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let factory = WazuhClientFactory::new(
env::var("WAZUH_API_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_API_PORT").unwrap_or_else(|_| "55000".to_string()).parse().unwrap_or(55000),
env::var("WAZUH_API_USERNAME").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_API_PASSWORD").unwrap_or_else(|_| "wazuh".to_string()),
env::var("WAZUH_INDEXER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
env::var("WAZUH_INDEXER_PORT").unwrap_or_else(|_| "9200".to_string()).parse().unwrap_or(9200),
env::var("WAZUH_INDEXER_USERNAME").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_INDEXER_PASSWORD").unwrap_or_else(|_| "admin".to_string()),
env::var("WAZUH_VERIFY_SSL").unwrap_or_else(|_| "false".to_string()).parse().unwrap_or(false),
Some("https".to_string())
);
let mut clients: WazuhClients = factory.create_all_clients();
match clients.agents.get_agent("invalid-id-123").await {
Ok(agent) => println!("Agent: {:?}", agent.name),
Err(WazuhApiError::HttpError { status, message, .. }) => {
eprintln!("HTTP Error {}: {}", status, message);
if status == reqwest::StatusCode::NOT_FOUND {
eprintln!("The agent 'invalid-id-123' was not found.");
} else if status == reqwest::StatusCode::UNAUTHORIZED {
eprintln!("Authentication failed. Check your API credentials.");
}
}
Err(WazuhApiError::AuthenticationError(msg)) => {
eprintln!("Wazuh API Authentication Error: {}", msg);
}
Err(WazuhApiError::ApiError(msg)) => {
eprintln!("Wazuh API Error: {}", msg);
}
Err(WazuhApiError::RequestError(reqwest_err)) => {
eprintln!("Network or Request Error: {}", reqwest_err);
}
Err(WazuhApiError::JsonError(json_err)) => {
eprintln!("JSON Parsing Error: {}", json_err);
}
Err(e) => eprintln!("An unexpected error occurred: {}", e),
}
Ok(())
}
Examples
The examples/ directory contains comprehensive examples:
Run examples with:
cargo run --example basic_usage
cargo run --example agent_management
Features
Default Features
tls - Enable TLS support using native TLS
Optional Features
rustls - Use rustls instead of native TLS
Enable features in your Cargo.toml:
[dependencies]
wazuh-client = { version = "0.1.0", features = ["rustls"] }
Compatibility
- Rust: 1.70.0 or later
- Wazuh: 4.12 or later
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch
- Make your changes
- Add test
- Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support