11 KiB
CrowdSec Traffic Blocking - Final Validation Report
Date: December 15, 2025
Agent: QA_Security
Environment: Docker container charon:local
❌ VERDICT: FAIL
Traffic blocking is NOT functional end-to-end.
Test Results Summary
| Component | Status | Details |
|---|---|---|
| CrowdSec Process | ✅ RUNNING | PID 324, started manually |
| LAPI Health | ✅ HEALTHY | Accessible at http://127.0.0.1:8085 |
| Bouncer Registration | ✅ REGISTERED | caddy-bouncer active, last pull at 20:06:01Z |
| Bouncer API Connectivity | ✅ CONNECTED | Bouncer successfully querying LAPI |
| CrowdSec App Config | ✅ CONFIGURED | API key set, ticker_interval: 10s |
| Decision Creation | ✅ SUCCESS | Test IP 203.0.113.99 banned for 15m |
| BLOCKING TEST | ❌ FAIL | Banned IP returned HTTP 200 instead of 403 |
| Normal Traffic | ✅ PASS | Non-banned traffic returns 200 OK |
| Pre-commit | ✅ PASS | All checks passed, 85.1% coverage |
Critical Issue: HTTP Handler Middleware Not Applied
Problem
While the CrowdSec bouncer is successfully:
- Running and connected to LAPI
- Fetching decisions from LAPI
- Registered with valid API key
The Caddy HTTP handler middleware is not applied to routes, so blocking decisions are not enforced on incoming traffic.
Evidence
1. CrowdSec LAPI Running and Healthy
$ docker exec charon ps aux | grep crowdsec
324 root 0:01 /usr/local/bin/crowdsec -c /app/data/crowdsec/config/config.yaml
$ docker exec charon sh -c 'cd /app/data/crowdsec && /usr/local/bin/cscli lapi status'
Trying to authenticate with username "844aa6ea34104e829b80a8b9f459b4d9QqsifNBhWtcwmq1s" on http://127.0.0.1:8085/
You can successfully interact with Local API (LAPI)
2. Bouncer Registered and Active
$ docker exec charon sh -c 'cd /app/data/crowdsec && /usr/local/bin/cscli bouncers list'
---------------------------------------------------------------------------------------------
Name IP Address Valid Last API pull Type Version Auth Type
---------------------------------------------------------------------------------------------
caddy-bouncer 127.0.0.1 ✔️ 2025-12-15T20:06:01Z caddy-cs-bouncer v0.9.2 api-key
---------------------------------------------------------------------------------------------
3. Decision Created Successfully
$ docker exec charon sh -c 'cd /app/data/crowdsec && /usr/local/bin/cscli decisions add --ip 203.0.113.99 --duration 15m --reason "FINAL QA VALIDATION TEST"'
level=info msg="Decision successfully added"
$ docker exec charon sh -c 'cd /app/data/crowdsec && /usr/local/bin/cscli decisions list' | grep 203.0.113.99
| 1 | cscli | Ip:203.0.113.99 | FINAL QA VALIDATION TEST | ban | | | 1 | 14m54s | 1 |
4. ❌ BLOCKING TEST FAILED - Traffic NOT Blocked
$ curl -H "X-Forwarded-For: 203.0.113.99" http://localhost:8080/ -v
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.5.0
> Accept: */*
> X-Forwarded-For: 203.0.113.99
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 687
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 15 Dec 2025 17:46:43 GMT
< Date: Mon, 15 Dec 2025 20:05:59 GMT
Expected: HTTP 403 Forbidden Actual: HTTP 200 OK Result: ❌ FAIL
5. Caddy HTTP Routes Missing CrowdSec Handler
$ docker exec charon curl -s http://localhost:2019/config/apps/http/servers | jq '.[].routes[0].handle'
[
{
"handler": "rewrite",
"uri": "/unknown.html"
},
{
"handler": "file_server",
"root": "/app/frontend/dist"
}
]
No crowdsec handler present in the middleware chain.
6. CrowdSec Headers
No X-Crowdsec-* headers were present in the response, confirming the middleware is not processing requests.
Root Cause Analysis
Configuration Gap
- CrowdSec App Level: ✅ Configured with API key and URL
- HTTP Handler Level: ❌ NOT configured - Missing from route middleware chain
The Caddy server has the CrowdSec bouncer module loaded:
$ docker exec charon caddy list-modules | grep crowdsec
admin.api.crowdsec
crowdsec
http.handlers.crowdsec
layer4.matchers.crowdsec
But the http.handlers.crowdsec is not applied to any routes in the current configuration.
Why This Happened
Looking at the application logs:
{"bin_path":"/usr/local/bin/crowdsec","data_dir":"/app/data/crowdsec","level":"info","msg":"CrowdSec reconciliation: starting startup check","time":"2025-12-15T19:59:33Z"}
{"db_mode":"disabled","level":"info","msg":"CrowdSec reconciliation skipped: both SecurityConfig and Settings indicate disabled","setting_enabled":false,"time":"2025-12-15T19:59:33Z"}
And later:
Initializing CrowdSec configuration...
CrowdSec configuration initialized. Agent lifecycle is GUI-controlled.
The system initialized CrowdSec configuration but did NOT auto-start it or configure Caddy routes because:
- The reconciliation logic checked both
SecurityConfigandSettingstables - Even though I manually set
crowd_sec_mode='local'andenabled=1in the database, the startup check at 19:59:33 found them disabled - The system then initialized configs but left "Agent lifecycle GUI-controlled"
- Manual start of CrowdSec LAPI succeeded, but Caddy route configuration was never updated
What Works
✅ CrowdSec Core Components:
- LAPI running and healthy
- Bouncer registered and polling decisions
- Decision management (add/delete/list) working
csclicommands functional- Database integration working
- Configuration files properly structured
✅ Infrastructure:
- Backend tests: 100% pass
- Code coverage: 85.1% (meets 85% requirement)
- Pre-commit hooks: All passed
- Container build: Successful
- Caddy admin API: Accessible and responsive
What Doesn't Work
❌ Traffic Enforcement:
- HTTP requests from banned IPs are not blocked
- CrowdSec middleware not in Caddy route handler chain
- No automatic configuration of Caddy routes when CrowdSec is enabled
❌ Auto-Start Logic:
- CrowdSec does not auto-start when database is configured to
mode=local, enabled=true - Reconciliation logic may have race condition or query timing issue
- Manual intervention required to start LAPI process
Production Readiness: NO
Blockers
- Critical: Traffic blocking does not work - primary security feature non-functional
- High: Auto-start logic unreliable - requires manual intervention
- High: Caddy route configuration not synchronized with CrowdSec state
Required Fixes
1. Fix Caddy Route Configuration (CRITICAL)
File: backend/internal/caddy/manager.go or similar Caddy config generator
Action Required:
When CrowdSec is enabled, the Caddy configuration builder must inject the crowdsec HTTP handler into the route middleware chain BEFORE other handlers.
Expected Structure:
{
"handle": [
{
"handler": "crowdsec",
"trusted_proxies_raw": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.1/32", "::1/128"]
},
{
"handler": "rewrite",
"uri": "/unknown.html"
},
{
"handler": "file_server",
"root": "/app/frontend/dist"
}
]
}
The trusted_proxies_raw field must be set at the HTTP handler level (not app level).
2. Fix Auto-Start Logic (HIGH)
File: backend/internal/services/crowdsec_startup.go
Issues:
- Line 110-117: The check
if cfg.CrowdSecMode != "local" && !crowdSecEnabledis skipping startup even when database shows enabled - Possible issue:
db.First(&cfg)not finding the manually-created record - Consider: The
Namefield mismatch (code expects "Default Security Config", DB has "default")
Recommended Fix:
// At line 43, ensure proper fallback:
if err := db.First(&cfg).Error; err != nil {
if err == gorm.ErrRecordNotFound {
// Try finding by uuid='default' as fallback
if err := db.Where("uuid = ?", "default").First(&cfg).Error; err != nil {
// Then proceed with auto-initialization logic
// ...
}
}
}
3. Add Integration Test for End-to-End Blocking
File: scripts/crowdsec_blocking_integration.sh (new)
Test Steps:
- Enable CrowdSec in DB
- Restart container
- Verify LAPI running
- Verify bouncer registered
- Add ban decision
- Test traffic with banned IP → Assert 403
- Test normal traffic → Assert 200
- Cleanup
This test must be added to CI/CD and must FAIL if traffic is not blocked.
Recommendation
DO NOT DEPLOY
The CrowdSec feature is non-functional for its primary purpose: blocking traffic. While all the supporting infrastructure works correctly (LAPI, bouncer registration, decision management), the absence of HTTP middleware enforcement makes this a critical security feature gap.
Next Steps (Priority Order)
-
IMMEDIATE (P0): Fix Caddy route handler injection in
caddy/manager.go- Add
crowdsechandler to route middleware chain - Include
trusted_proxies_rawconfiguration - Reload Caddy config when CrowdSec is enabled/disabled
- Add
-
HIGH (P1): Fix CrowdSec auto-start reconciliation logic
- Debug why
db.First(&cfg)returns 0 rows despite data existing - Fix query or add fallback to uuid lookup
- Ensure consistent startup behavior
- Debug why
-
HIGH (P1): Add blocking integration test
- Create
crowdsec_blocking_integration.sh - Add to CI pipeline
- Must verify actual 403 responses
- Create
-
MEDIUM (P2): Add automatic bouncer registration
- When CrowdSec starts, auto-register bouncer if not exists
- Update Caddy config with generated API key
- Eliminate manual registration step
-
LOW (P3): Add admin UI controls
- Start/Stop CrowdSec buttons
- Bouncer status display
- Decision management interface
Test Environment Details
Container Image: charon:local
Build Date: December 15, 2025
Caddy Version: (with crowdsec module v0.9.2)
CrowdSec Version: LAPI running, cscli available
Database: SQLite at /app/data/charon.db
Host OS: Linux
Files Modified During Testing
data/charon.db- Addedsecurity_configsandsettingsentries- Caddy live config - Added
apps.crowdsecconfiguration via admin API
Note: These changes are ephemeral in the container and not persisted in the repository.
Conclusion
CrowdSec infrastructure is 80% complete but missing the critical 20% - actual traffic enforcement. The foundation is solid:
- LAPI works
- Bouncer communicates
- Decisions are managed correctly
- Database integration works
- Code quality is high (85% coverage)
However, without the HTTP handler middleware properly configured, zero traffic is being blocked, making the feature unusable in production.
Estimated effort to fix: 4-8 hours
- Add HTTP handler injection logic (2-4h)
- Fix auto-start logic (1-2h)
- Add integration test (1-2h)
- Verify end-to-end (1h)
Report Author: QA_Security Agent Report Status: FINAL Next Action: Development team to implement fixes per recommendations above