mirror of
https://github.com/gbrigandi/mcp-server-wazuh.git
synced 2025-07-13 15:14:48 -06:00
297 lines
15 KiB
Markdown
297 lines
15 KiB
Markdown
# Wazuh MCP Server
|
|
|
|
A Rust-based server designed to bridge the gap between a Wazuh Security Information and Event Management (SIEM) system and applications requiring contextual security data, specifically tailored for the Claude Desktop Integration using the Model Context Protocol (MCP).
|
|
|
|
## Overview
|
|
|
|
Modern AI assistants like Claude can benefit significantly from real-time context about the user's environment. For security operations, this means providing relevant security alerts and events. Wazuh is a popular open-source SIEM, but its API output isn't directly consumable by systems expecting MCP format.
|
|
|
|
This server acts as a middleware:
|
|
1. It connects to the Wazuh API.
|
|
2. Authenticates using credentials (fetching a JWT token).
|
|
3. Periodically fetches security alerts from Wazuh.
|
|
4. Transforms these alerts from Wazuh's native format into the standardized MCP JSON format.
|
|
5. Exposes a simple HTTP endpoint (`/mcp`) where clients like Claude Desktop can poll for the latest transformed security context.
|
|
|
|
## Architecture
|
|
|
|
The server facilitates communication between Claude Desktop (or any MCP client) and the Wazuh API.
|
|
|
|
```ascii
|
|
```ascii
|
|
+-----------------+ +--------------------+ +-----------------+
|
|
| Claude Desktop | | Wazuh MCP Server | | Wazuh |
|
|
| (MCP Client) | | (This Application) | | (SIEM) |
|
|
+-----------------+ +--------------------+ +-----------------+
|
|
| | |
|
|
| 1. GET /mcp Request | |
|
|
|------------------------->| |
|
|
| | 2. Check/Refresh JWT |
|
|
| |------------------------->| 3. Authenticate (if needed via API)
|
|
| | |
|
|
| | 4. JWT Response (if auth)|
|
|
| |<-------------------------|
|
|
| | |
|
|
| | 5. GET /wazuh-alerts-*_search (with JWT)
|
|
| |------------------------->| 6. Fetch Alerts (via API)
|
|
| | |
|
|
| | 7. Wazuh Alert Data |
|
|
| |<-------------------------|
|
|
| | |
|
|
| | 8. Transform Data to MCP|
|
|
| | (Internal Logic) |
|
|
| | |
|
|
| 9. MCP JSON Response | |
|
|
|<-------------------------| |
|
|
| | |
|
|
|
|
```
|
|
|
|
**Data Flow:**
|
|
|
|
1. An MCP client (e.g., Claude Desktop) sends an HTTP GET request to the `/mcp` endpoint of this server.
|
|
2. The server checks if it has a valid JWT for the Wazuh API.
|
|
3. If the JWT is missing or expired, it authenticates with the Wazuh API using configured credentials (`WAZUH_USER`, `WAZUH_PASS`) via the `/security/user/authenticate` endpoint.
|
|
4. The Wazuh API returns a JWT.
|
|
5. The server uses the valid JWT to make a request to the Wazuh alerts search endpoint (e.g., `/wazuh-alerts-*_search`).
|
|
6. The Wazuh API executes the search (currently fetches all recent alerts).
|
|
7. The Wazuh API returns the alert data in its native JSON format.
|
|
8. The server's transformation logic (`src/mcp/transform.rs`) processes each alert, mapping Wazuh fields (like `rule.level`, `rule.description`, `agent.name`, `data`, `timestamp`) to the corresponding MCP fields (`severity`, `description`, `agent`, `data`, `timestamp`). It also sets default values for missing fields.
|
|
9. The server responds to the MCP client with a JSON array of transformed alerts in the MCP format.
|
|
|
|
## Features
|
|
|
|
- **Wazuh API Integration:** Connects securely to the Wazuh API.
|
|
- **JWT Authentication:** Handles authentication with Wazuh using username/password to obtain a JWT.
|
|
- **Automatic Token Refresh:** Monitors JWT validity and automatically re-authenticates when the token expires or is close to expiring. Retries API calls once upon receiving a 401 Unauthorized response.
|
|
- **Alert Retrieval:** Fetches alerts from the Wazuh API (currently configured to retrieve all recent alerts via a `match_all` query).
|
|
- **MCP Transformation:** Converts Wazuh alert JSON objects into MCP v1.0 compliant JSON messages. This includes:
|
|
- Mapping Wazuh `rule.level` to MCP `severity` (e.g., 0-3 -> "low", 8-11 -> "high").
|
|
- Extracting `rule.description`, `id`, `timestamp`, `agent` details, and the `data` payload.
|
|
- Taking the first group from `rule.groups` as the MCP `category`.
|
|
- Handling potential differences in Wazuh response structure (e.g., presence or absence of `_source` nesting).
|
|
- Providing default values (e.g., "unknown_severity", "unknown_category", current time for invalid timestamps).
|
|
- **HTTP Server:** Exposes endpoints using the Axum web framework.
|
|
- `/mcp`: Serves the transformed MCP messages.
|
|
- `/health`: Provides a simple health check.
|
|
- **Configuration:** Easily configurable via environment variables or a `.env` file.
|
|
- **Containerization:** Includes a `Dockerfile` and `docker-compose.yml` for easy deployment.
|
|
- **Logging:** Uses the `tracing` library for request and application logging (configurable via `RUST_LOG`).
|
|
|
|
## Requirements
|
|
|
|
- Rust (latest stable recommended, see `Cargo.toml` for specific dependencies)
|
|
- A running Wazuh server (v4.x recommended) with the API enabled and accessible.
|
|
- Network connectivity between this server and the Wazuh API.
|
|
|
|
## Configuration
|
|
|
|
Configuration is managed through environment variables. A `.env` file can be placed in the project root for local development.
|
|
|
|
| Variable | Description | Default | Required |
|
|
| ----------------- | ------------------------------------------------- | ----------- | -------- |
|
|
| `WAZUH_HOST` | Hostname or IP address of the Wazuh API server. | `localhost` | Yes |
|
|
| `WAZUH_PORT` | Port number for the Wazuh API. | `55000` | Yes |
|
|
| `WAZUH_USER` | Username for Wazuh API authentication. | `admin` | Yes |
|
|
| `WAZUH_PASS` | Password for Wazuh API authentication. | `admin` | Yes |
|
|
| `VERIFY_SSL` | Set to `true` to verify the Wazuh API's SSL cert. | `false` | No |
|
|
| `MCP_SERVER_PORT` | Port for this MCP server to listen on. | `8000` | No |
|
|
| `RUST_LOG` | Log level (e.g., `info`, `debug`, `trace`). | `info` | No |
|
|
|
|
**Note on `VERIFY_SSL`:** For production environments, it is strongly recommended to set `VERIFY_SSL=true` and ensure proper certificate validation. Setting it to `false` disables certificate checks, which is insecure.
|
|
|
|
## Building and Running
|
|
|
|
### Prerequisites
|
|
|
|
- Install Rust: [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install)
|
|
- Install Docker and Docker Compose (optional, for containerized deployment): [https://docs.docker.com/get-docker/](https://docs.docker.com/get-docker/)
|
|
|
|
### Local Development
|
|
|
|
1. **Clone the repository:**
|
|
```bash
|
|
git clone https://github.com/yourusername/mcp-server-wazuh.git # Replace with your repo URL
|
|
cd mcp-server-wazuh
|
|
```
|
|
2. **Configure:**
|
|
- Copy the example environment file: `cp .env.example .env`
|
|
- Edit the `.env` file with your specific Wazuh API details (`WAZUH_HOST`, `WAZUH_PORT`, `WAZUH_USER`, `WAZUH_PASS`).
|
|
3. **Build:**
|
|
```bash
|
|
cargo build
|
|
```
|
|
4. **Run:**
|
|
```bash
|
|
cargo run
|
|
# Or use the run script:
|
|
# ./run.sh
|
|
```
|
|
The server will start listening on the port specified by `MCP_SERVER_PORT` (default 8000).
|
|
|
|
### Docker Deployment
|
|
|
|
1. **Clone the repository** (if not already done).
|
|
2. **Configure:** Ensure you have a `.env` file with your Wazuh credentials in the project root, or set the environment variables directly in the `docker-compose.yml` or your deployment environment.
|
|
3. **Build and Run:**
|
|
```bash
|
|
docker-compose up --build -d
|
|
```
|
|
This will build the Docker image and start the container in detached mode.
|
|
|
|
## API Endpoints
|
|
|
|
### `GET /mcp`
|
|
|
|
Fetches the latest alerts from the configured Wazuh API, transforms them into MCP format, and returns them as a JSON array.
|
|
|
|
- **Method:** `GET`
|
|
- **Success Response:** `200 OK`
|
|
- **Body:** `application/json` - An array of MCP message objects.
|
|
```json
|
|
[
|
|
{
|
|
"protocol_version": "1.0",
|
|
"source": "Wazuh",
|
|
"timestamp": "2023-10-27T10:30:00Z", // ISO 8601 format (RFC3339)
|
|
"event_type": "alert",
|
|
"context": {
|
|
"id": "wazuh_alert_id_1", // Wazuh alert ID
|
|
"category": "gdpr", // Derived from rule groups
|
|
"severity": "high", // Derived from rule level
|
|
"description": "High severity rule triggered",
|
|
"agent": { // Wazuh agent details
|
|
"id": "001",
|
|
"name": "server-db"
|
|
},
|
|
"data": { // Original Wazuh alert data field
|
|
"srcip": "1.2.3.4",
|
|
"dstport": "22"
|
|
}
|
|
// Other relevant context fields might be added here
|
|
},
|
|
"metadata": {
|
|
"integration": "Wazuh-MCP",
|
|
"notes": "Data fetched via Wazuh API"
|
|
// Other metadata like Wazuh rule ID could be added
|
|
// "rule_id": "1002"
|
|
}
|
|
},
|
|
// ... more MCP messages
|
|
]
|
|
```
|
|
- **Error Responses:**
|
|
- `401 Unauthorized`: If Wazuh authentication fails persistently.
|
|
- `500 Internal Server Error`: If there's an issue fetching/parsing data from Wazuh, or an internal server problem.
|
|
- `502 Bad Gateway`: If the server cannot connect to the Wazuh API or the API returns an unexpected error.
|
|
|
|
### `GET /health`
|
|
|
|
A simple health check endpoint.
|
|
|
|
- **Method:** `GET`
|
|
- **Success Response:** `200 OK`
|
|
- **Body:** `application/json`
|
|
```json
|
|
{
|
|
"status": "ok",
|
|
"service": "wazuh-mcp-server",
|
|
"timestamp": "2023-10-27T12:00:00Z" // Current server time in ISO 8601
|
|
}
|
|
```
|
|
- **Error Responses:** None expected for this endpoint itself, but the server might be unreachable if down.
|
|
|
|
## Running the All-in-One Demo (Wazuh + MCP Server)
|
|
|
|
For a complete local demo environment that includes Wazuh (Indexer, Manager, Dashboard) and the Wazuh MCP Server pre-configured to connect to it, you can use the `docker-compose.all-in-one.yml` file.
|
|
|
|
This setup is ideal for testing the end-to-end flow from Wazuh alerts to MCP messages.
|
|
|
|
**1. Launch the Environment:**
|
|
|
|
Navigate to the project root directory in your terminal and run:
|
|
|
|
```bash
|
|
docker-compose -f docker-compose.all-in-one.yml up -d
|
|
|
|
This command will:
|
|
- Download the necessary Wazuh and OpenSearch images (if not already present).
|
|
- Start the Wazuh Indexer, Wazuh Manager, and Wazuh Dashboard services.
|
|
- Build and start the Wazuh MCP Server.
|
|
- All services are configured to communicate with each other on an internal Docker network.
|
|
|
|
**2. Accessing Services:**
|
|
|
|
* **Wazuh Dashboard:**
|
|
* URL: `https://localhost:8443` (Note: Uses HTTPS with a self-signed certificate, so your browser will likely show a warning).
|
|
* Default Username: `admin`
|
|
* Default Password: `AdminPassword123!` (This is set by `WAZUH_INITIAL_PASSWORD` in the `wazuh-indexer` service).
|
|
|
|
* **Wazuh MCP Server:**
|
|
* The MCP server will be running and accessible on port `8000` by default (or the port specified by `MCP_SERVER_PORT` if you've set it as an environment variable on your host machine before running docker-compose).
|
|
* Example MCP endpoint: `http://localhost:8000/mcp`
|
|
* Example Health endpoint: `http://localhost:8000/health`
|
|
* **Configuration:** The `mcp-server` service within `docker-compose.all-in-one.yml` is already configured with the necessary environment variables to connect to the `wazuh-manager` service:
|
|
* `WAZUH_HOST=wazuh-manager`
|
|
* `WAZUH_PORT=55000`
|
|
* `WAZUH_USER=wazuh_user_demo`
|
|
* `WAZUH_PASS=wazuh_password_demo`
|
|
* `VERIFY_SSL=false`
|
|
You do not need to set these in a separate `.env` file when using this all-in-one compose file, as they are defined directly in the service's environment.
|
|
|
|
**3. Stopping the Environment:**
|
|
|
|
To stop all services, run:
|
|
|
|
```bash
|
|
docker-compose -f docker-compose.all-in-one.yml down
|
|
```
|
|
|
|
To stop and remove volumes (deleting Wazuh data):
|
|
|
|
```bash
|
|
docker-compose -f docker-compose.all-in-one.yml down -v
|
|
```
|
|
|
|
This approach simplifies setup by bundling all necessary components and their configurations.
|
|
|
|
## Claude Desktop Integration
|
|
|
|
To use this Wazuh MCP Server with Claude Desktop (or any other MCP-compatible client), you need to configure the client to poll the `/mcp` endpoint exposed by this server.
|
|
|
|
1. **Ensure the Wazuh MCP Server is running** and accessible from the machine where Claude Desktop is operating. This might involve:
|
|
* Running the server locally (e.g., `cargo run` or via Docker, including the all-in-one setup described above).
|
|
* Deploying the server to a reachable host.
|
|
2. **Identify the server's address and port.**
|
|
* If using the all-in-one demo: `http://localhost:8000/mcp` (or your `MCP_SERVER_PORT`).
|
|
* If running `mcp-server` standalone: `http://localhost:8000` by default, or `http://<your-server-ip-or-hostname>:<MCP_SERVER_PORT>` if deployed elsewhere. The `MCP_SERVER_PORT` is configurable via environment variables (defaults to `8000`).
|
|
3. **Configure Claude Desktop:**
|
|
* In Claude Desktop's settings or configuration area for external context sources, add a new MCP endpoint.
|
|
* Set the URL to `http://<server_address>:<port>/mcp`. For example:
|
|
* If running the all-in-one demo or locally: `http://localhost:8000/mcp`
|
|
* If running on a remote server `192.168.1.100` on port `8080`: `http://192.168.1.100:8080/mcp`
|
|
4. **Verify Firewall Rules:** Ensure that any firewalls between Claude Desktop and the Wazuh MCP Server allow traffic on the configured `MCP_SERVER_PORT`.
|
|
|
|
Once configured, Claude Desktop should start polling the `/mcp` endpoint periodically to fetch the latest Wazuh security alerts in MCP format.
|
|
`
|
|
## Development & Testing
|
|
|
|
- **Code Style:** Uses standard Rust formatting (`cargo fmt`).
|
|
- **Linting:** Uses Clippy (`cargo clippy`).
|
|
- **Testing:** Contains unit tests for transformation logic and integration tests using a mock Wazuh API server (`httpmock`) and a test MCP client.
|
|
```bash
|
|
# Run all tests
|
|
cargo test
|
|
|
|
# Run specific integration test
|
|
cargo test --test integration_test
|
|
|
|
# Run tests with detailed logging
|
|
RUST_LOG=debug cargo test
|
|
```
|
|
- See `tests/README.md` for more details on running tests and using the test client CLI.
|
|
|
|
## License
|
|
|
|
This project is licensed under the [MIT License](LICENSE).
|