feat: merge Gitea webhook security into setup wizard (issue #2)
Integrates the 5-layer Gitea webhook security system from sol/clawgravity-hook-security (v2.0) into the setup wizard. ## What's added ### New files (from clawgravity-hook-security v2.0) - scripts/webhook-security/gitea-hmac-verify.js -- njs HMAC-SHA256 module - scripts/webhook-security/gitea-approve-repo -- allowlist helper - scripts/webhook-security/rotate-webhook-secret.sh -- monthly secret rotation (templated) - scripts/webhook-security/webhook-audit-alert.sh -- daily audit summaries (templated) - scripts/webhook-security/ntfy-blocked-pickup.sh -- blocked webhook alerts (templated) - templates/webhook-security/nginx-site.conf.example - templates/webhook-security/nginx.conf.example - templates/webhook-security/gitea-repo-allowlist.json.example - docs/WEBHOOK-SECURITY.md -- full documentation - docs/SECURITY-AUDIT.md -- 35-case test matrix - tests/test-webhook-security.sh -- 48 offline tests ### Modified files - setup.sh: Step 11 (webhook security wizard with 6 sub-sections) - scripts/uninstall.sh: webhook security cleanup section - README.md: Webhook Security section after Quick Start - Makefile: test target now runs test-webhook-security.sh - .secret-scan-allowlist: allowlist docs/SECURITY-AUDIT.md (test fixture) ## Security layers 1. IP allowlisting (nginx) 2. Rate limiting 10 req/s burst 20 (nginx) 3. Payload size 1MB (nginx) 4. HMAC-SHA256 signature verification (njs) 5. Per-repository allowlist (njs) ## make check - prettier: PASS - secret-scan: PASS - tests: 48/48 PASS Closes #2
This commit is contained in:
77
scripts/webhook-security/ntfy-blocked-pickup.sh
Executable file
77
scripts/webhook-security/ntfy-blocked-pickup.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user