#!/bin/bash # ntfy-blocked-pickup.sh - Scans nginx error log for blocked webhook attempts # and sends ntfy.sh notifications for each new occurrence. # # Designed to run as a cron job every 60 seconds: # * * * * * /opt/webhook-security/scripts/ntfy-blocked-pickup.sh # # State file tracks the last-seen log position to avoid duplicate alerts. # ------------------------------------------------------------------ set -euo pipefail # ========================= CONFIGURATION ========================= # Customize these values for your environment ERROR_LOG="/var/log/nginx/error.log" STATE_FILE="/var/lib/webhook-security/ntfy-pickup-state" NTFY_TOPIC="@@NTFY_TOPIC@@" PATTERN="gitea-hmac: BLOCKED webhook from unauthorized repo:" # ================================================================= # Ensure state directory exists mkdir -p "$(dirname "$STATE_FILE")" # Read last processed byte offset (0 if first run) if [ -f "$STATE_FILE" ]; then LAST_OFFSET=$(cat "$STATE_FILE") else LAST_OFFSET=0 fi # Get current file size if [ ! -f "$ERROR_LOG" ]; then echo "No error log found at $ERROR_LOG" exit 0 fi CURRENT_SIZE=$(stat -c%s "$ERROR_LOG" 2>/dev/null || stat -f%z "$ERROR_LOG" 2>/dev/null) # Handle log rotation (file shrank) if [ "$CURRENT_SIZE" -lt "$LAST_OFFSET" ]; then LAST_OFFSET=0 fi # No new data if [ "$CURRENT_SIZE" -eq "$LAST_OFFSET" ]; then exit 0 fi # Extract new lines and filter for blocked webhook entries NEW_BLOCKS=$(tail -c +"$((LAST_OFFSET + 1))" "$ERROR_LOG" | grep -F "$PATTERN" || true) if [ -n "$NEW_BLOCKS" ]; then # Count blocked attempts COUNT=$(echo "$NEW_BLOCKS" | wc -l) # Extract unique repo names REPOS=$(echo "$NEW_BLOCKS" | grep -oP 'unauthorized repo: \K\S+' | sort -u | tr '\n' ', ' | sed 's/,$//') # Build notification message MSG="BLOCKED: ${COUNT} webhook(s) from unauthorized repo(s): ${REPOS}" # Send ntfy notification curl -sf \ -H "Title: Gitea Webhook Blocked" \ -H "Priority: urgent" \ -H "Content-Type: text/plain" \ -d "$MSG" \ "$NTFY_TOPIC" > /dev/null 2>&1 || true # Also log to syslog logger -t webhook-security "ntfy-blocked-pickup: $MSG" fi # Update state file with current position echo "$CURRENT_SIZE" > "$STATE_FILE"