Skip to content

Commit 5351943

Browse files
committed
initial import
1 parent aa77291 commit 5351943

26 files changed

+4794
-2
lines changed

README.md

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,80 @@
1-
# crowdsec-local-mcp
2-
An MCP exposing prompts and tools to help users write WAF rules, scenarios etc.
1+
<p align="center">
2+
<img src="https://github.com/crowdsecurity/crowdsec-docs/blob/main/crowdsec-docs/static/img/crowdsec_logo.png" alt="CrowdSec" title="CrowdSec" width="400" height="260"/>
3+
</p>
4+
5+
6+
**Life is too short to write YAML, just ask nicely!**
7+
8+
> A Model Context Protocol (MCP) server to generate, validate, and deploy CrowdSec WAF rules & Scenarios.
9+
10+
11+
## Features
12+
13+
### WAF Rules Features
14+
15+
- **WAF Rule Generation**: Generate CrowdSec WAF rules from user input or a CVE reference
16+
- **Validation**: Validate syntaxical correctness of WAF rules
17+
- **Linting**: Get warnings and hints to improve your WAF rules
18+
- **Deployment Guide**: Step-by-step deployment instructions
19+
- **Docker Test Harness**: Spin up CrowdSec + nginx + bouncer to exercise rules for false positives/negatives
20+
- **Nuclei Lookup**: Quickly jump to existing templates in the official `projectdiscovery/nuclei-templates` repository for a given CVE
21+
22+
### Scenarios Features
23+
24+
- **CrowdSec Scenarios Generation**: Generate CrowdSec scenarios
25+
- **Validation**: Validate syntaxical correctness of scenarios
26+
- **Linting**: Get warnings and hints to improve your scenarios
27+
- **Deployment Guide**: Step-by-step deployment instructions
28+
- **Docker Test Harness**: Spin up CrowdSec to test scenario behavior
29+
30+
## Demo
31+
32+
### WAF Rules Creation and testing
33+
34+
- [Rule creation from natural language with Claude Desktop](https://claude.ai/share/f0f246b2-6b20-4d70-a16c-c6b627ab2d80)
35+
- [Rule creation from CVE reference](https://claude.ai/share/b6599407-82dd-443c-a12d-9a9825ed99df)
36+
37+
### Scenario Creation and testing
38+
39+
- XX
40+
- XX
41+
42+
## Installation
43+
44+
### Setup
45+
46+
Install dependencies using `uv`:
47+
```bash
48+
uv sync
49+
```
50+
51+
## Configuration for Claude Desktop
52+
53+
### macOS/Linux
54+
55+
1. Find your Claude Desktop config file:
56+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
57+
- Linux: `~/.config/Claude/claude_desktop_config.json`
58+
59+
2. Add the MCP server configuration:
60+
```json
61+
{
62+
"mcpServers": {
63+
"crowdsec-prompt-server": {
64+
"command": "/path/to/crowdsec-mcp-rule-helper/.venv/bin/python",
65+
"args": [
66+
"/path/to/crowdsec-mcp-rule-helper/mcp-prompt.py"
67+
],
68+
"cwd": "/path/to/crowdsec-mcp-rule-helper"
69+
}
70+
}
71+
}
72+
```
73+
74+
**Important**: Replace `/path/to/crowdsec-mcp-rule-helper` with the actual absolute path to your cloned repository.
75+
76+
## Pre Requisites
77+
78+
- Docker + Docker Compose
79+
80+
- Python

compose/waf-test/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated at runtime by the MCP integration.
2+
rules/current-rule.yaml
3+
crowdsec/appsec-configs/mcp-appsec.yaml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Acquisition file registering the MCP-generated AppSec configuration.
2+
appsec_configs:
3+
- mcp-appsec
4+
labels:
5+
type: appsec
6+
listen_addr: 0.0.0.0:7422
7+
source: appsec
8+
name: myAppSecComponent
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: mcp-appsec
2+
default_remediation: ban
3+
inband_rules:
4+
# Keep the CrowdSec base config to ensure essential protections remain active.
5+
- crowdsecurity/base-config
6+
# The MCP tooling copies the user rule into the custom directory as current-rule.yaml
7+
## XXX FIXME : make this a variable :)
8+
- __PLACEHOLDER_FOR_USER_RULE__
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
API_KEY="mcp-nginx-bouncer-test-key"
5+
BOUNCER_NAME="mcp-nginx-bouncer"
6+
7+
/bin/bash /docker_start.sh "$@" &
8+
PID=$!
9+
trap 'kill "$PID" 2>/dev/null || true' EXIT
10+
11+
for _ in $(seq 1 90); do
12+
if cscli lapi status >/dev/null 2>&1; then
13+
break
14+
fi
15+
sleep 2;
16+
done
17+
18+
if ! cscli lapi status >/dev/null 2>&1; then
19+
echo "CrowdSec LAPI did not become ready in time" >&2
20+
wait "$PID"
21+
exit 1
22+
fi
23+
24+
cscli bouncers delete "$BOUNCER_NAME" >/dev/null 2>&1 || true
25+
cscli bouncers add "$BOUNCER_NAME" -k "$API_KEY"
26+
27+
trap - EXIT
28+
wait "$PID"
29+
exit $?
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
version: "3.9"
2+
3+
services:
4+
crowdsec:
5+
image: crowdsecurity/crowdsec:latest
6+
hostname: crowdsec
7+
container_name: crowdsec-appsec
8+
restart: "no"
9+
entrypoint:
10+
- /bin/bash
11+
- /usr/local/bin/init-bouncer.sh
12+
environment:
13+
# Ensure the local API stays accessible for the nginx bouncer.
14+
- DISABLE_LOCAL_API=0
15+
- DISABLE_ONLINE_API=1
16+
# Turn on AppSec mode inside the CrowdSec container.
17+
- ENABLE_APPSEC=1
18+
volumes:
19+
# Persist CrowdSec data (buckets, alerts, etc.) between restarts.
20+
- crowdsec-data:/var/lib/crowdsec/data
21+
# Allow templated acquisition and AppSec config overrides without replacing the whole /etc/crowdsec tree.
22+
- ./crowdsec/acquis.d/appsec.yaml:/etc/crowdsec/acquis.d/appsec-mcp.yaml:ro
23+
- ./crowdsec/appsec-configs/mcp-appsec.yaml:/etc/crowdsec/appsec-configs/mcp-appsec.yaml:ro
24+
- ./crowdsec/init-bouncer.sh:/usr/local/bin/init-bouncer.sh:ro
25+
# The MCP tooling will drop the user-provided rule in this folder as current-rule.yaml
26+
- ./rules:/etc/crowdsec/appsec-rules/custom
27+
ports:
28+
- "18080:8080" # LAPI (use non-default host port to avoid conflicts)
29+
- "17422:7422" # AppSec Live mode (non-default host port)
30+
networks:
31+
- waf-net
32+
33+
nginx:
34+
build:
35+
context: ./nginx
36+
container_name: nginx-appsec
37+
restart: "no"
38+
depends_on:
39+
- crowdsec
40+
- backend
41+
ports:
42+
- "8081:80"
43+
command:
44+
- openresty
45+
- -g
46+
- 'daemon off;'
47+
volumes:
48+
- ./nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
49+
# Site config enabling the CrowdSec module and proxying to the backend.
50+
- ./nginx/site-enabled:/usr/local/openresty/nginx/conf/site-enabled:ro
51+
# Override the bouncer configuration shipped in the image with the harness version.
52+
- ./nginx/crowdsec/crowdsec-openresty-bouncer.conf:/etc/crowdsec/bouncers/crowdsec-openresty-bouncer.conf:ro
53+
networks:
54+
- waf-net
55+
56+
backend:
57+
image: nginxdemos/hello:latest
58+
container_name: app-backend
59+
restart: unless-stopped
60+
networks:
61+
- waf-net
62+
63+
volumes:
64+
crowdsec-data:
65+
66+
networks:
67+
waf-net:
68+
driver: bridge

compose/waf-test/nginx/Dockerfile

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
2+
FROM ubuntu:24.04
3+
4+
# Install dependencies
5+
RUN apt-get update && apt-get install -y \
6+
git \
7+
make \
8+
software-properties-common \
9+
wget \
10+
gnupg \
11+
ca-certificates \
12+
gettext \
13+
curl
14+
15+
RUN wget -O - https://openresty.org/package/pubkey.gpg | apt-key add -
16+
RUN echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"| tee /etc/apt/sources.list.d/openresty.list
17+
RUN curl -s https://install.crowdsec.net | bash
18+
19+
RUN apt update
20+
21+
RUN apt install -y openresty openresty-opm gettext-base
22+
23+
RUN apt install -y crowdsec-openresty-bouncer
24+
25+
26+
27+
EXPOSE 80
28+
29+
30+
# # Install the bouncer
31+
# COPY build.sh /build.sh
32+
# COPY start.sh /start.sh
33+
34+
# RUN chmod +x /build.sh && /build.sh
35+
# RUN chmod +x /start.sh
36+
37+
# # Set the script as the entrypoint
38+
# ENTRYPOINT ["/start.sh"]
39+
40+
41+
42+
# FROM debian:bookworm
43+
44+
# ENV DEBIAN_FRONTEND=noninteractive
45+
46+
# # Install nginx with Lua module support and prerequisites for the CrowdSec nginx bouncer.
47+
# RUN set -eux; \
48+
# apt-get update; \
49+
# apt-get install -y --no-install-recommends \
50+
# ca-certificates \
51+
# curl \
52+
# gnupg2 \
53+
# iproute2 \
54+
# libnginx-mod-http-lua \
55+
# lsb-release \
56+
# nginx \
57+
# procps; \
58+
# curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash; \
59+
# apt-get install -y --no-install-recommends crowdsec-nginx-bouncer; \
60+
# rm -rf /var/lib/apt/lists/*
61+
62+
# # Prepare directories that will receive bind mounts at runtime.
63+
# RUN mkdir -p /etc/nginx/conf.d /etc/nginx/crowdsec
64+
65+
# EXPOSE 80
66+
67+
# CMD ["nginx", "-g", "daemon off;"]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
API_URL=http://crowdsec:8080
2+
API_KEY=mcp-nginx-bouncer-test-key
3+
BOUNCING_ON_TYPE=all
4+
FALLBACK_REMEDIATION=ban
5+
MODE=stream
6+
REQUEST_TIMEOUT=1000
7+
EXCLUDE_LOCATION=
8+
ENABLE_INTERNAL=false
9+
CACHE_EXPIRATION=1
10+
UPDATE_FREQUENCY=10
11+
BAN_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/ban.html
12+
REDIRECT_LOCATION=
13+
RET_CODE=
14+
CAPTCHA_PROVIDER=
15+
SECRET_KEY=
16+
SITE_KEY=
17+
CAPTCHA_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/captcha.html
18+
CAPTCHA_EXPIRATION=3600
19+
APPSEC_URL=http://crowdsec:7422
20+
APPSEC_FAILURE_ACTION=passthrough
21+
APPSEC_CONNECT_TIMEOUT=100
22+
APPSEC_SEND_TIMEOUT=100
23+
APPSEC_PROCESS_TIMEOUT=1000
24+
ALWAYS_SEND_TO_APPSEC=false
25+
SSL_VERIFY=true

compose/waf-test/nginx/nginx.conf

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
worker_processes auto;
3+
4+
error_log /dev/stderr info;
5+
pid /tmp/nginx.pid;
6+
7+
8+
events {
9+
worker_connections 1024;
10+
}
11+
12+
http {
13+
resolver 127.0.0.11;
14+
15+
include /usr/local/openresty/nginx/conf/mime.types;
16+
default_type application/octet-stream;
17+
18+
sendfile on;
19+
keepalive_timeout 65;
20+
21+
access_log /dev/stdout;
22+
23+
include /usr/local/openresty/nginx/conf/conf.d/*.conf;
24+
include /usr/local/openresty/nginx/conf/site-enabled/*.conf;
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Auto-generated by MCP test harness. Route requests through AppSec and the CrowdSec bouncer.
2+
upstream backend_app {
3+
server backend:80;
4+
}
5+
6+
server {
7+
listen 80;
8+
server_name _;
9+
10+
location / {
11+
proxy_pass http://backend_app;
12+
proxy_set_header Host $host;
13+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
14+
}
15+
}

0 commit comments

Comments
 (0)