feat: Add CrowdSec logging, rate limiting, and fix MCP parameter defaults

- Add real client IP detection (CF-Connecting-IP / src fallback) to both frontends
- Add per-IP rate limiting (429) using real IP for Cloudflare compatibility
- Add CrowdSec syslog forwarding with custom log format
- Add httplog option for detailed HTTP logging
- Fix Python-level defaults on MCP tool parameters to match Field(default=X)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kaffa
2026-02-07 00:22:39 +09:00
parent 4a411202d3
commit da533f407a
6 changed files with 53 additions and 14 deletions

View File

@@ -515,7 +515,7 @@ def register_certificate_tools(mcp):
@mcp.tool()
def haproxy_issue_cert(
domain: Annotated[str, Field(description="Primary domain (e.g., example.com)")],
wildcard: Annotated[bool, Field(default=True, description="Include wildcard (*.example.com). Default: true")]
wildcard: Annotated[bool, Field(default=True, description="Include wildcard (*.example.com). Default: true")] = True
) -> str:
"""Issue a new SSL/TLS certificate using acme.sh with Cloudflare DNS.
@@ -528,7 +528,7 @@ def register_certificate_tools(mcp):
@mcp.tool()
def haproxy_renew_cert(
domain: Annotated[str, Field(description="Domain name to renew (e.g., example.com)")],
force: Annotated[bool, Field(default=False, description="Force renewal even if not due. Default: false")]
force: Annotated[bool, Field(default=False, description="Force renewal even if not due. Default: false")] = False
) -> str:
"""Renew an existing certificate.

View File

@@ -162,7 +162,7 @@ def register_domain_tools(mcp):
@mcp.tool()
def haproxy_list_domains(
include_wildcards: Annotated[bool, Field(default=False, description="Include wildcard entries (.example.com). Default: False")]
include_wildcards: Annotated[bool, Field(default=False, description="Include wildcard entries (.example.com). Default: False")] = False
) -> str:
"""List all configured domains with their backend servers."""
try:
@@ -206,9 +206,9 @@ def register_domain_tools(mcp):
@mcp.tool()
def haproxy_add_domain(
domain: Annotated[str, Field(description="Domain name to add (e.g., api.example.com, example.com)")],
ip: Annotated[str, Field(default="", description="Optional: Initial server IP. If provided, adds server to slot 1")],
http_port: Annotated[int, Field(default=80, description="HTTP port for backend server (default: 80)")],
share_with: Annotated[str, Field(default="", description="Optional: Existing domain to share pool with. New domain uses same backend servers.")]
ip: Annotated[str, Field(default="", description="Optional: Initial server IP. If provided, adds server to slot 1")] = "",
http_port: Annotated[int, Field(default=80, description="HTTP port for backend server (default: 80)")] = 80,
share_with: Annotated[str, Field(default="", description="Optional: Existing domain to share pool with. New domain uses same backend servers.")] = ""
) -> str:
"""Add a new domain to HAProxy (no reload required).

View File

@@ -188,7 +188,7 @@ def register_health_tools(mcp):
@mcp.tool()
def haproxy_get_server_health(
backend: Annotated[str, Field(default="", description="Optional: Backend name to filter (e.g., 'pool_1'). Empty = all backends")]
backend: Annotated[str, Field(default="", description="Optional: Backend name to filter (e.g., 'pool_1'). Empty = all backends")] = ""
) -> str:
"""Get health status of all servers (low-level view). For domain-specific, use haproxy_domain_health."""
if backend and not validate_backend_name(backend):

View File

@@ -75,7 +75,7 @@ def register_monitoring_tools(mcp):
@mcp.tool()
def haproxy_get_connections(
backend: Annotated[str, Field(default="", description="Optional: Backend name to filter (e.g., 'pool_1'). Empty = all backends")]
backend: Annotated[str, Field(default="", description="Optional: Backend name to filter (e.g., 'pool_1'). Empty = all backends")] = ""
) -> str:
"""Get active connection counts per server for monitoring traffic distribution."""
if backend and not validate_backend_name(backend):

View File

@@ -455,7 +455,7 @@ def register_server_tools(mcp):
domain: Annotated[str, Field(description="Domain name to add server to (e.g., api.example.com)")],
slot: Annotated[int, Field(description="Server slot number 1-10, or 0 for auto-select next available slot")],
ip: Annotated[str, Field(description="Server IP address (IPv4 like 10.0.0.1 or IPv6 like 2001:db8::1)")],
http_port: Annotated[int, Field(default=80, description="HTTP port for backend connection (default: 80)")]
http_port: Annotated[int, Field(default=80, description="HTTP port for backend connection (default: 80)")] = 80
) -> str:
"""Add a server to a domain's backend pool for load balancing.
@@ -509,7 +509,7 @@ def register_server_tools(mcp):
@mcp.tool()
def haproxy_wait_drain(
domain: Annotated[str, Field(description="Domain name to wait for (e.g., api.example.com)")],
timeout: Annotated[int, Field(default=30, description="Maximum seconds to wait (default: 30, max: 300)")]
timeout: Annotated[int, Field(default=30, description="Maximum seconds to wait (default: 30, max: 300)")] = 30
) -> str:
"""Wait for all active connections to drain from a domain's servers.