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:
@@ -31,6 +31,7 @@ global
|
||||
defaults
|
||||
log global
|
||||
mode http
|
||||
option httplog
|
||||
option dontlognull
|
||||
option http-keep-alive
|
||||
option forwardfor
|
||||
@@ -53,10 +54,27 @@ frontend stats
|
||||
# HTTP Frontend - forward to backend (same as HTTPS)
|
||||
frontend http_front
|
||||
bind *:80
|
||||
# ACME challenge for certbot (unused - using DNS-01)
|
||||
# acl is_acme path_beg /.well-known/acme-challenge/
|
||||
# use_backend acme_backend if is_acme
|
||||
# http-request redirect scheme https unless is_acme
|
||||
|
||||
# -- Shared security config (keep in sync with http_front/https_front) --
|
||||
|
||||
# Send HTTP logs to CrowdSec
|
||||
log 10.253.100.240:514 local0
|
||||
|
||||
# Set real client IP (CF-Connecting-IP if via Cloudflare, otherwise direct client IP)
|
||||
http-request set-var(txn.real_ip) req.hdr(CF-Connecting-IP) if { req.hdr(CF-Connecting-IP) -m found }
|
||||
http-request set-var(txn.real_ip) src unless { var(txn.real_ip) -m found }
|
||||
http-request set-header X-Real-IP %[var(txn.real_ip)]
|
||||
log-format "%[var(txn.real_ip)]:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq \"%r\""
|
||||
|
||||
# Per-IP concurrent connection limit (slowloris protection)
|
||||
# Note: http_front and https_front have separate stick-tables, so the same
|
||||
# IP is counted independently in each frontend (HTTP vs HTTPS).
|
||||
stick-table type ip size 200k expire 5m store conn_cur
|
||||
acl is_internal src 127.0.0.0/8 100.64.0.0/10
|
||||
http-request track-sc0 hdr_ip(X-Real-IP) if !is_internal
|
||||
http-request deny deny_status 429 if !is_internal { sc_conn_cur(0) gt 500 }
|
||||
|
||||
# -- End shared security config --
|
||||
|
||||
# 2-stage map-based routing for performance:
|
||||
# Stage 1: Exact match with map_str (O(log n) - fast, uses ebtree)
|
||||
@@ -72,6 +90,27 @@ frontend https_front
|
||||
bind quic4@:443 ssl crt /etc/haproxy/certs/ alpn h3
|
||||
http-response set-header alt-svc "h3=\":443\"; ma=86400"
|
||||
|
||||
# -- Shared security config (keep in sync with http_front/https_front) --
|
||||
|
||||
# Send HTTP logs to CrowdSec
|
||||
log 10.253.100.240:514 local0
|
||||
|
||||
# Set real client IP (CF-Connecting-IP if via Cloudflare, otherwise direct client IP)
|
||||
http-request set-var(txn.real_ip) req.hdr(CF-Connecting-IP) if { req.hdr(CF-Connecting-IP) -m found }
|
||||
http-request set-var(txn.real_ip) src unless { var(txn.real_ip) -m found }
|
||||
http-request set-header X-Real-IP %[var(txn.real_ip)]
|
||||
log-format "%[var(txn.real_ip)]:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq \"%r\""
|
||||
|
||||
# Per-IP concurrent connection limit (slowloris protection)
|
||||
# Note: http_front and https_front have separate stick-tables, so the same
|
||||
# IP is counted independently in each frontend (HTTP vs HTTPS).
|
||||
stick-table type ip size 200k expire 5m store conn_cur
|
||||
acl is_internal src 127.0.0.0/8 100.64.0.0/10
|
||||
http-request track-sc0 hdr_ip(X-Real-IP) if !is_internal
|
||||
http-request deny deny_status 429 if !is_internal { sc_conn_cur(0) gt 500 }
|
||||
|
||||
# -- End shared security config --
|
||||
|
||||
# MCP authentication (Bearer Token or Tailscale)
|
||||
acl is_mcp hdr(host) -i mcp.inouter.com
|
||||
acl valid_token req.hdr(Authorization) -m str "Bearer dcb7963ab3ef705f6b780818f78942a100efa3b55e3d2f99c4560b65da64c426"
|
||||
|
||||
Reference in New Issue
Block a user