#!/bin/bash # ============================================================= # CrowdSec Local Attack Notifier # Host: NAS16 (192.168.150.40) # Target: CrowdSec LAPI on HAS (192.168.150.30:8080) # Schedule: Every 5 minutes via OMV Scheduled Jobs # # Local (crowdsec origin) decisions are appended to: # /var/log/crowdsec-notifier/local_pending.json # and batched into a 4-hour digest by cs_local_digest.sh. # # CAPI decisions continue to be appended to capi_digest.json # and sent as a midnight summary by cs_digest.sh (unchanged). # ============================================================= LAPI_URL="http://192.168.150.30:8080" API_KEY="K1nZ5TYeyl18adqw7+dop/po9gHZNQvsuLz78lla/34" STARTUP_FLAG="/var/log/crowdsec-notifier/stream_initialized.flag" LOCAL_PENDING="/var/log/crowdsec-notifier/local_pending.json" CAPI_DIGEST="/var/log/crowdsec-notifier/capi_digest.json" LOG_FILE="/var/log/crowdsec-notifier/notifier.log" mkdir -p /var/log/crowdsec-notifier touch "$CAPI_DIGEST" touch "$LOCAL_PENDING" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; } log "--- cs_notifier run start ---" # --- First run: initialize stream cursor silently --- if [[ ! -f "$STARTUP_FLAG" ]]; then log "First run — initializing stream cursor. No emails will be sent." INIT_RESPONSE=$(curl -s --max-time 60 \ -H "X-Api-Key: $API_KEY" \ "${LAPI_URL}/v1/decisions/stream?startup=true") if [[ -z "$INIT_RESPONSE" ]]; then log "ERROR: Empty response from LAPI during startup. Will retry next run." exit 1 fi NEW_COUNT=$(echo "$INIT_RESPONSE" | python3 -c " import sys, json data = json.load(sys.stdin) print(len(data.get('new') or [])) " 2>/dev/null) log "Stream cursor initialized. Skipped $NEW_COUNT existing decisions. Ready for live monitoring." touch "$STARTUP_FLAG" log "--- cs_notifier run complete (init) ---" exit 0 fi # --- Normal run: fetch only new decisions since last call --- RESPONSE=$(curl -s --max-time 30 \ -H "X-Api-Key: $API_KEY" \ "${LAPI_URL}/v1/decisions/stream?startup=false") HTTP_CHECK=$(echo "$RESPONSE" | python3 -c "import sys,json; json.load(sys.stdin); print('ok')" 2>/dev/null) if [[ "$HTTP_CHECK" != "ok" ]]; then log "ERROR: Invalid response from LAPI stream endpoint" exit 1 fi # --- Parse and route new decisions --- NEW_LOCAL_COUNT=0 NEW_CAPI_COUNT=0 while IFS= read -r decision; do [[ -z "$decision" ]] && continue ORIGIN=$(echo "$decision" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('origin',''))" 2>/dev/null) if [[ "$ORIGIN" == "crowdsec" ]]; then # Append to pending local digest — cs_local_digest.sh will send every 4 hours echo "$decision" >> "$LOCAL_PENDING" NEW_LOCAL_COUNT=$((NEW_LOCAL_COUNT + 1)) elif [[ "$ORIGIN" == "CAPI" ]]; then # Append to CAPI digest — cs_digest.sh will send at midnight echo "$decision" >> "$CAPI_DIGEST" NEW_CAPI_COUNT=$((NEW_CAPI_COUNT + 1)) fi done < <(echo "$RESPONSE" | python3 -c " import sys, json data = json.load(sys.stdin) new = data.get('new') or [] for item in new: print(json.dumps(item)) ") CAPI_TODAY=$(wc -l < "$CAPI_DIGEST" 2>/dev/null | tr -d ' ') CAPI_TODAY=${CAPI_TODAY:-0} LOCAL_PENDING_COUNT=$(wc -l < "$LOCAL_PENDING" 2>/dev/null | tr -d ' ') LOCAL_PENDING_COUNT=${LOCAL_PENDING_COUNT:-0} log "New local: $NEW_LOCAL_COUNT (pending total: $LOCAL_PENDING_COUNT) | New CAPI: $NEW_CAPI_COUNT | CAPI today: $CAPI_TODAY" log "--- cs_notifier run complete ---"