Files
Charon/scripts/crowdsec_startup_test.sh

339 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ⚠️ DEPRECATED: This script is deprecated and will be removed in v2.0.0
# Please use: .github/skills/scripts/skill-runner.sh integration-test-crowdsec-startup
# For more info: docs/AGENT_SKILLS_MIGRATION.md
echo "⚠️ WARNING: This script is deprecated and will be removed in v2.0.0" >&2
echo " Please use: .github/skills/scripts/skill-runner.sh integration-test-crowdsec-startup" >&2
echo " For more info: docs/AGENT_SKILLS_MIGRATION.md" >&2
echo "" >&2
sleep 1
# Brief: Focused integration test for CrowdSec startup in Charon container
# This test verifies that CrowdSec can start successfully without the fatal
# "no datasource enabled" error, which indicates a missing or empty acquis.yaml.
#
# Steps:
# 1. Build charon:local image if not present
# 2. Start container with CERBERUS_SECURITY_CROWDSEC_MODE=local
# 3. Wait for initialization (30 seconds)
# 4. Check for fatal errors
# 5. Check LAPI health
# 6. Check acquisition config
# 7. Check installed parsers/scenarios
# 8. Output clear PASS/FAIL results
# 9. Clean up container
# Ensure we operate from repo root
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$PROJECT_ROOT"
# ============================================================================
# Configuration
# ============================================================================
CONTAINER_NAME="charon-crowdsec-startup-test"
INIT_WAIT_SECONDS=30
# Use unique ports to avoid conflicts with running Charon
API_PORT=8580
HTTP_PORT=8480
HTTPS_PORT=8443
# ============================================================================
# Colors for output
# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_test() { echo -e "${BLUE}[TEST]${NC} $1"; }
# ============================================================================
# Test counters
# ============================================================================
PASSED=0
FAILED=0
CRITICAL_FAILURE=false
pass_test() {
PASSED=$((PASSED + 1))
echo -e " ${GREEN}✓ PASS${NC}"
}
fail_test() {
FAILED=$((FAILED + 1))
echo -e " ${RED}✗ FAIL${NC}: $1"
}
critical_fail() {
FAILED=$((FAILED + 1))
CRITICAL_FAILURE=true
echo -e " ${RED}✗ CRITICAL FAIL${NC}: $1"
}
# ============================================================================
# Cleanup function
# ============================================================================
cleanup() {
log_info "Cleaning up test resources..."
docker rm -f ${CONTAINER_NAME} 2>/dev/null || true
# Clean up test volumes
docker volume rm charon_crowdsec_startup_data 2>/dev/null || true
docker volume rm caddy_crowdsec_startup_data 2>/dev/null || true
docker volume rm caddy_crowdsec_startup_config 2>/dev/null || true
log_info "Cleanup complete"
}
# Set up trap for cleanup on exit (success or failure)
trap cleanup EXIT
echo "=============================================="
echo "=== CrowdSec Startup Integration Test ==="
echo "=============================================="
echo ""
# ============================================================================
# Step 1: Check dependencies
# ============================================================================
log_info "Checking dependencies..."
if ! command -v docker >/dev/null 2>&1; then
log_error "docker is not available; aborting"
exit 1
fi
# ============================================================================
# Step 2: Build image if needed
# ============================================================================
if ! docker image inspect charon:local >/dev/null 2>&1; then
log_info "Building charon:local image..."
docker build -t charon:local .
else
log_info "Using existing charon:local image"
fi
# ============================================================================
# Step 3: Clean up any existing container
# ============================================================================
log_info "Stopping any existing test containers..."
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..."
docker run -d --name ${CONTAINER_NAME} \
-p ${HTTP_PORT}:80 \
-p ${HTTPS_PORT}:443 \
-p ${API_PORT}:8080 \
-e CHARON_ENV=development \
-e CHARON_DEBUG=1 \
-e FEATURE_CERBERUS_ENABLED=true \
-e CERBERUS_SECURITY_CROWDSEC_MODE=local \
-v charon_crowdsec_startup_data:/app/data \
-v caddy_crowdsec_startup_data:/data \
-v caddy_crowdsec_startup_config:/config \
charon:local
log_info "Waiting ${INIT_WAIT_SECONDS} seconds for CrowdSec to initialize..."
sleep ${INIT_WAIT_SECONDS}
echo ""
echo "=============================================="
echo "=== Running CrowdSec Startup Checks ==="
echo "=============================================="
echo ""
# ============================================================================
# Test 1: Check for fatal "no datasource enabled" error
# ============================================================================
log_test "Check 1: No fatal 'no datasource enabled' error"
FATAL_ERROR_COUNT=$(docker logs ${CONTAINER_NAME} 2>&1 | grep -c "no datasource enabled" || echo "0")
if [ "$FATAL_ERROR_COUNT" -ge 1 ]; then
critical_fail "Found fatal 'no datasource enabled' error - acquis.yaml is missing or empty"
echo ""
echo "=== Relevant Container Logs ==="
docker logs ${CONTAINER_NAME} 2>&1 | grep -i "crowdsec\|acquis\|datasource\|fatal" | tail -20
echo ""
else
log_info " No 'no datasource enabled' fatal error found"
pass_test
fi
# ============================================================================
# Test 2: Check LAPI health endpoint
# ============================================================================
log_test "Check 2: CrowdSec LAPI health (127.0.0.1:8085/health)"
# Use docker exec to check LAPI health from inside the container
LAPI_HEALTH=$(docker exec ${CONTAINER_NAME} wget -q -O- http://127.0.0.1:8085/health 2>/dev/null || echo "FAILED")
if [ "$LAPI_HEALTH" != "FAILED" ] && [ -n "$LAPI_HEALTH" ]; then
log_info " LAPI is healthy"
log_info " Response: $LAPI_HEALTH"
pass_test
else
fail_test "LAPI health check failed (port 8085 not responding)"
# This could be expected if CrowdSec binary is not in the image
log_warn " This may be expected if CrowdSec binary is not installed"
fi
# ============================================================================
# Test 3: Check acquisition config exists and has datasource
# ============================================================================
log_test "Check 3: Acquisition config exists and has 'source:' definition"
ACQUIS_CONTENT=$(docker exec ${CONTAINER_NAME} cat /etc/crowdsec/acquis.yaml 2>/dev/null || echo "")
if [ -z "$ACQUIS_CONTENT" ]; then
critical_fail "acquis.yaml does not exist or is empty"
else
SOURCE_COUNT=$(echo "$ACQUIS_CONTENT" | grep -c "source:" || echo "0")
if [ "$SOURCE_COUNT" -ge 1 ]; then
log_info " acquis.yaml found with $SOURCE_COUNT datasource definition(s)"
echo ""
echo " --- acquis.yaml content ---"
echo "$ACQUIS_CONTENT" | head -15 | sed 's/^/ /'
echo " ---"
echo ""
pass_test
else
critical_fail "acquis.yaml exists but has no 'source:' definition"
echo " Content:"
echo "$ACQUIS_CONTENT" | head -10 | sed 's/^/ /'
fi
fi
# ============================================================================
# Test 4: Check for installed parsers
# ============================================================================
log_test "Check 4: Installed parsers (at least one expected)"
PARSERS_OUTPUT=$(docker exec ${CONTAINER_NAME} cscli parsers list 2>&1 || echo "CSCLI_NOT_AVAILABLE")
if [ "$PARSERS_OUTPUT" = "CSCLI_NOT_AVAILABLE" ]; then
log_warn " cscli command not available - cannot check parsers"
# Not a failure - cscli may not be in the image
pass_test
elif echo "$PARSERS_OUTPUT" | grep -q "PARSERS"; then
# cscli output includes "PARSERS" header
PARSER_COUNT=$(echo "$PARSERS_OUTPUT" | grep -c "✔" || echo "0")
if [ "$PARSER_COUNT" -ge 1 ]; then
log_info " Found $PARSER_COUNT installed parser(s)"
echo "$PARSERS_OUTPUT" | head -10 | sed 's/^/ /'
pass_test
else
log_warn " No parsers installed (CrowdSec may not parse logs correctly)"
pass_test
fi
else
log_warn " Unexpected cscli output"
echo "$PARSERS_OUTPUT" | head -5 | sed 's/^/ /'
pass_test
fi
# ============================================================================
# Test 5: Check for installed scenarios
# ============================================================================
log_test "Check 5: Installed scenarios (at least one expected)"
SCENARIOS_OUTPUT=$(docker exec ${CONTAINER_NAME} cscli scenarios list 2>&1 || echo "CSCLI_NOT_AVAILABLE")
if [ "$SCENARIOS_OUTPUT" = "CSCLI_NOT_AVAILABLE" ]; then
log_warn " cscli command not available - cannot check scenarios"
pass_test
elif echo "$SCENARIOS_OUTPUT" | grep -q "SCENARIOS"; then
SCENARIO_COUNT=$(echo "$SCENARIOS_OUTPUT" | grep -c "✔" || echo "0")
if [ "$SCENARIO_COUNT" -ge 1 ]; then
log_info " Found $SCENARIO_COUNT installed scenario(s)"
echo "$SCENARIOS_OUTPUT" | head -10 | sed 's/^/ /'
pass_test
else
log_warn " No scenarios installed (CrowdSec may not detect attacks)"
pass_test
fi
else
log_warn " Unexpected cscli output"
echo "$SCENARIOS_OUTPUT" | head -5 | sed 's/^/ /'
pass_test
fi
# ============================================================================
# Test 6: Check CrowdSec process is running (if expected)
# ============================================================================
log_test "Check 6: CrowdSec process running"
CROWDSEC_PID=$(docker exec ${CONTAINER_NAME} pgrep -f "crowdsec" 2>/dev/null || echo "")
if [ -n "$CROWDSEC_PID" ]; then
log_info " CrowdSec process is running (PID: $CROWDSEC_PID)"
pass_test
else
log_warn " CrowdSec process not found (may not be installed or may have crashed)"
# Check if crowdsec binary exists
CROWDSEC_BIN=$(docker exec ${CONTAINER_NAME} which crowdsec 2>/dev/null || echo "")
if [ -z "$CROWDSEC_BIN" ]; then
log_warn " crowdsec binary not found in container"
fi
pass_test
fi
# ============================================================================
# Show last container logs for debugging
# ============================================================================
echo ""
echo "=== Container Logs (last 30 lines) ==="
docker logs ${CONTAINER_NAME} 2>&1 | tail -30
echo ""
# ============================================================================
# Results Summary
# ============================================================================
echo ""
echo "=============================================="
echo "=== CrowdSec Startup Test Results ==="
echo "=============================================="
echo ""
echo -e " ${GREEN}Passed:${NC} $PASSED"
echo -e " ${RED}Failed:${NC} $FAILED"
echo ""
if [ "$CRITICAL_FAILURE" = "true" ]; then
echo -e "${RED}=============================================="
echo "=== CRITICAL: CrowdSec STARTUP BROKEN ==="
echo "==============================================${NC}"
echo ""
echo "CrowdSec cannot start properly. The 'no datasource enabled' error"
echo "indicates that acquis.yaml is missing or has no datasource definitions."
echo ""
echo "To fix:"
echo " 1. Ensure configs/crowdsec/acquis.yaml exists with 'source:' definition"
echo " 2. Ensure Dockerfile copies acquis.yaml to /etc/crowdsec.dist/"
echo " 3. Ensure .docker/docker-entrypoint.sh copies configs to /etc/crowdsec/"
echo ""
exit 1
fi
if [ $FAILED -eq 0 ]; then
echo "=============================================="
echo "=== ALL CROWDSEC STARTUP TESTS PASSED ==="
echo "=============================================="
echo ""
exit 0
else
echo "=============================================="
echo "=== CROWDSEC STARTUP TESTS FAILED ==="
echo "=============================================="
echo ""
exit 1
fi