fix(docker): ensure CrowdSec hub index and collections bootstrap on every startup

This commit is contained in:
GitHub Actions
2026-04-05 05:16:26 +00:00
parent 5e73ba7bd0
commit 47d306b44b
5 changed files with 336 additions and 1107 deletions

View File

@@ -310,10 +310,11 @@ ACQUIS_EOF
echo "✗ WARNING: LAPI port configuration may be incorrect"
fi
# Update hub index to ensure CrowdSec can start
if [ ! -f "/etc/crowdsec/hub/.index.json" ]; then
echo "Updating CrowdSec hub index..."
timeout 60s cscli hub update 2>/dev/null || echo "⚠️ Hub update timed out or failed, continuing..."
# Always refresh hub index on startup (stale index causes hash mismatch errors on collection install)
echo "Updating CrowdSec hub index..."
if ! timeout 60s cscli hub update 2>&1; then
echo "⚠️ Hub index update failed (network issue?). Collections may fail to install."
echo " CrowdSec will still start with whatever index is cached."
fi
# Ensure local machine is registered (auto-heal for volume/config mismatch)
@@ -321,12 +322,11 @@ ACQUIS_EOF
echo "Registering local machine..."
cscli machines add -a --force 2>/dev/null || echo "Warning: Machine registration may have failed"
# Install hub items (parsers, scenarios, collections) if local mode enabled
if [ "$SECURITY_CROWDSEC_MODE" = "local" ]; then
echo "Installing CrowdSec hub items..."
if [ -x /usr/local/bin/install_hub_items.sh ]; then
/usr/local/bin/install_hub_items.sh 2>/dev/null || echo "Warning: Some hub items may not have installed"
fi
# Always ensure required collections are present (idempotent — already-installed items are skipped).
# Collections are just config files with zero runtime cost when CrowdSec is disabled.
echo "Ensuring CrowdSec hub items are installed..."
if [ -x /usr/local/bin/install_hub_items.sh ]; then
/usr/local/bin/install_hub_items.sh || echo "⚠️ Some hub items may not have installed. CrowdSec can still start."
fi
# Fix ownership AFTER cscli commands (they run as root and create root-owned files)

View File

@@ -7,42 +7,45 @@ set -e
echo "Installing CrowdSec hub items for Charon..."
# Update hub index first
echo "Updating hub index..."
cscli hub update 2>/dev/null || echo "Warning: Failed to update hub index"
# Hub index update is handled by the entrypoint before this script is called.
# Do not duplicate it here — a redundant update adds ~3s to startup for no benefit.
# Install Caddy log parser (if available)
# Note: crowdsecurity/caddy-logs may not exist yet - check hub
if cscli parsers inspect crowdsecurity/caddy-logs >/dev/null 2>&1; then
echo "Installing Caddy log parser..."
cscli parsers install crowdsecurity/caddy-logs --force 2>/dev/null || true
cscli parsers install crowdsecurity/caddy-logs --force || echo "⚠️ Failed to install crowdsecurity/caddy-logs"
else
echo "Caddy-specific parser not available, using HTTP parser..."
fi
# Install base HTTP parsers (always needed)
echo "Installing base parsers..."
cscli parsers install crowdsecurity/http-logs --force 2>/dev/null || true
cscli parsers install crowdsecurity/syslog-logs --force 2>/dev/null || true
cscli parsers install crowdsecurity/geoip-enrich --force 2>/dev/null || true
cscli parsers install crowdsecurity/http-logs --force || echo "⚠️ Failed to install crowdsecurity/http-logs"
cscli parsers install crowdsecurity/syslog-logs --force || echo "⚠️ Failed to install crowdsecurity/syslog-logs"
cscli parsers install crowdsecurity/geoip-enrich --force || echo "⚠️ Failed to install crowdsecurity/geoip-enrich"
# Install HTTP scenarios for attack detection
echo "Installing HTTP scenarios..."
cscli scenarios install crowdsecurity/http-probing --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-sensitive-files --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-backdoors-attempts --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-path-traversal-probing --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-xss-probing --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-sqli-probing --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-generic-bf --force 2>/dev/null || true
cscli scenarios install crowdsecurity/http-probing --force || echo "⚠️ Failed to install crowdsecurity/http-probing"
cscli scenarios install crowdsecurity/http-sensitive-files --force || echo "⚠️ Failed to install crowdsecurity/http-sensitive-files"
cscli scenarios install crowdsecurity/http-backdoors-attempts --force || echo "⚠️ Failed to install crowdsecurity/http-backdoors-attempts"
cscli scenarios install crowdsecurity/http-path-traversal-probing --force || echo "⚠️ Failed to install crowdsecurity/http-path-traversal-probing"
cscli scenarios install crowdsecurity/http-xss-probing --force || echo "⚠️ Failed to install crowdsecurity/http-xss-probing"
cscli scenarios install crowdsecurity/http-sqli-probing --force || echo "⚠️ Failed to install crowdsecurity/http-sqli-probing"
cscli scenarios install crowdsecurity/http-generic-bf --force || echo "⚠️ Failed to install crowdsecurity/http-generic-bf"
# Install CVE collection for known vulnerabilities
echo "Installing CVE collection..."
cscli collections install crowdsecurity/http-cve --force 2>/dev/null || true
cscli collections install crowdsecurity/http-cve --force || echo "⚠️ Failed to install crowdsecurity/http-cve"
# Install base HTTP collection (bundles common scenarios)
echo "Installing base HTTP collection..."
cscli collections install crowdsecurity/base-http-scenarios --force 2>/dev/null || true
cscli collections install crowdsecurity/base-http-scenarios --force || echo "⚠️ Failed to install crowdsecurity/base-http-scenarios"
# Install Caddy collection (parser + scenarios for Caddy access logs)
echo "Installing Caddy collection..."
cscli collections install crowdsecurity/caddy --force || echo "⚠️ Failed to install crowdsecurity/caddy"
# Verify installation
echo ""

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
# QA Report: CrowdSec Hub Bootstrapping Fix
**Date:** 2026-04-05
**Scope:** `.docker/docker-entrypoint.sh`, `configs/crowdsec/install_hub_items.sh`, `scripts/crowdsec_startup_test.sh`
**Status:** PASS
---
## 1. Shell Script Syntax Validation (`bash -n`)
| File | Result |
|------|--------|
| `.docker/docker-entrypoint.sh` | ✓ Syntax OK |
| `configs/crowdsec/install_hub_items.sh` | ✓ Syntax OK |
| `scripts/crowdsec_startup_test.sh` | ✓ Syntax OK |
**Verdict:** PASS — All three scripts parse without errors.
---
## 2. ShellCheck Static Analysis (v0.9.0)
| File | Findings | Severity |
|------|----------|----------|
| `.docker/docker-entrypoint.sh` | SC2012 (L243): `ls` used where `find` is safer for non-alphanumeric filenames | Info |
| `configs/crowdsec/install_hub_items.sh` | None | — |
| `scripts/crowdsec_startup_test.sh` | SC2317 (L70,71,84,85,87-90): Functions in `trap` handler flagged as "unreachable" (false positive — invoked indirectly via `trap cleanup EXIT`) | Info |
| `scripts/crowdsec_startup_test.sh` | SC2086 (L85): `${CONTAINER_NAME}` unquoted in `docker rm -f` | Info |
**Verdict:** PASS — All findings are informational (severity: info). No warnings or errors. The SC2317 findings are false positives (standard `trap` pattern). The SC2086 finding is pre-existing and non-exploitable (variable is set to a constant string `charon-crowdsec-startup-test` without user input).
---
## 3. Pre-commit Hooks (Lefthook v2.1.4)
| Hook | Result |
|------|--------|
| check-yaml | ✓ Pass |
| actionlint | ✓ Pass |
| end-of-file-fixer | ✓ Pass |
| trailing-whitespace | ✓ Pass |
| dockerfile-check | ✓ Pass |
| shellcheck | ✓ Pass |
**Verdict:** PASS — All 6 applicable hooks passed successfully.
---
## 4. Security Review
### 4.1 Secrets and Credential Exposure
| Check | Result |
|-------|--------|
| Hardcoded secrets in changed files | None found |
| API keys/tokens in changed files | None found |
| Gotify tokens in logs/output/URLs | None found |
| Environment variable secrets exposed via `echo` | None — all `echo` statements output status messages only |
**Verdict:** PASS
### 4.2 Shell Injection Vectors
| Check | Result |
|-------|--------|
| User input used in commands | No user-controlled input enters any command. All variables are set from hardcoded paths |
| `eval` usage | None |
| Unquoted variable expansion in commands | All critical variables are quoted or hardcoded strings |
| Command injection via hub item names | Not applicable — all `cscli` arguments are hardcoded collection/parser names |
**Verdict:** PASS
### 4.3 `timeout` Usage Safety
The entrypoint uses `timeout 60s cscli hub update 2>&1`:
- `timeout` is the coreutils version (Alpine `busybox` timeout), sending SIGTERM after 60s
- Prevents indefinite hang if hub CDN is unresponsive
- 60s is appropriate for a single HTTPS request with potential DNS resolution
- Failure is handled gracefully — logged as warning, startup continues
**Verdict:** PASS
### 4.4 `--force` Flag Analysis
All `--force` flags are on `cscli` install commands:
- `--force` in `cscli` context means "re-download and overwrite if already installed" — functionally an upsert
- Does NOT bypass integrity checks or signature verification
- Does NOT skip CrowdSec's hub item hash validation
- Ensures idempotent behavior on every startup
**Verdict:** PASS
### 4.5 Error Visibility Changes
The diff changes `2>/dev/null || true` patterns to `|| echo "⚠️ Failed to install ..."`:
- **Before:** Errors silently swallowed
- **After:** Errors logged with descriptive messages
- This is a security improvement — silent failures can mask missing detection capabilities
**Verdict:** PASS — Improved error visibility is a positive security change.
### 4.6 Deprecated Environment Variable Removal
| Check | Result |
|-------|--------|
| `SECURITY_CROWDSEC_MODE` removed from entrypoint | ✓ env var gate deleted |
| `CERBERUS_SECURITY_CROWDSEC_MODE=local` removed from startup test | ✓ removed from `docker run` |
| No remaining references in changed files | ✓ only documentation files reference it (as deprecated) |
| Backend config still reads `CERBERUS_SECURITY_CROWDSEC_MODE` | ✓ backend uses different var names via `getEnvAny()` |
**Verdict:** PASS
---
## 5. Dockerfile Consistency
### 5.1 `install_hub_items.sh` Copy
```dockerfile
COPY configs/crowdsec/install_hub_items.sh /usr/local/bin/install_hub_items.sh
RUN chmod +x /usr/local/bin/install_hub_items.sh /usr/local/bin/register_bouncer.sh
```
Copy path matches entrypoint invocation path (`-x /usr/local/bin/install_hub_items.sh`).
**Verdict:** PASS
### 5.2 Build-Time vs Runtime Conflict Check
| Concern | Analysis |
|---------|----------|
| Build-time `cscli hub update` | Not performed in Dockerfile |
| Build-time collection install | Not performed in Dockerfile |
| Conflict with runtime approach | None — all hub operations deferred to container startup |
**Verdict:** PASS
### 5.3 Entrypoint Reference
```dockerfile
COPY .docker/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
```
**Verdict:** PASS
---
## 6. Related Tests and CI
| Test | Location | Status |
|------|----------|--------|
| `scripts/crowdsec_startup_test.sh` | Modified in this PR | Updated — no longer passes deprecated env var |
| Backend config tests | `backend/internal/config/config_test.go` | Unchanged, still valid |
| CrowdSec-specific CI workflows | None exist | N/A |
The startup test script is the primary validation mechanism for hub bootstrapping. The E2E Playwright suite covers CrowdSec UI but not hub bootstrapping directly.
---
## 7. Change Summary and Risk Assessment
| Change | Risk | Rationale |
|--------|------|-----------|
| Unconditional `cscli hub update` on startup | Low | Adds ~2-5s. Prevents stale-index hash mismatch. `timeout 60s` prevents hangs. Failure is graceful. |
| Removed `SECURITY_CROWDSEC_MODE` env var gate | Low | Env var was deprecated and never set. Collections are idempotent config files with zero runtime cost. |
| Added `crowdsecurity/caddy` collection | Low | Standard CrowdSec collection for Caddy. Installed via `--force` (idempotent). |
| Removed `2>/dev/null` from `cscli` install commands | Low (positive) | Errors now visible in container logs. |
| Removed redundant `cscli hub update` from `install_hub_items.sh` | Low | Prevents double hub update (~3s saved). |
| Removed deprecated env var from startup test | Low | Test matches actual container behavior. |
---
## 8. Overall Verdict
| Category | Status |
|----------|--------|
| Shell syntax | ✅ PASS |
| ShellCheck | ✅ PASS (info-only findings) |
| Pre-commit hooks | ✅ PASS |
| Security: secrets/credentials | ✅ PASS |
| Security: injection vectors | ✅ PASS |
| Security: `timeout` safety | ✅ PASS |
| Security: `--force` flags | ✅ PASS |
| Dockerfile consistency | ✅ PASS |
| Deprecated env var cleanup | ✅ PASS |
| CI/test coverage | ✅ PASS |
**Overall: PASS — No blockers. Ready for merge.**

View File

@@ -16,7 +16,7 @@ sleep 1
#
# Steps:
# 1. Build charon:local image if not present
# 2. Start container with CERBERUS_SECURITY_CROWDSEC_MODE=local
# 2. Start container with CrowdSec environment
# 3. Wait for initialization (30 seconds)
# 4. Check for fatal errors
# 5. Check LAPI health
@@ -127,7 +127,7 @@ docker rm -f ${CONTAINER_NAME} 2>/dev/null || true
# ============================================================================
# Step 4: Start container with CrowdSec enabled
# ============================================================================
log_info "Starting Charon container with CERBERUS_SECURITY_CROWDSEC_MODE=local..."
log_info "Starting Charon container with CrowdSec enabled..."
docker run -d --name ${CONTAINER_NAME} \
-p ${HTTP_PORT}:80 \
@@ -136,7 +136,6 @@ docker run -d --name ${CONTAINER_NAME} \
-e CHARON_ENV=development \
-e CHARON_DEBUG=1 \
-e FEATURE_CERBERUS_ENABLED=true \
-e CERBERUS_SECURITY_CROWDSEC_MODE=local \
-e CERBERUS_SECURITY_CROWDSEC_API_KEY=dummy-key \
-v charon_crowdsec_startup_data:/app/data \
-v caddy_crowdsec_startup_data:/data \