From 64a43c8a961e61e14e3fa30fb1d3e0910ea82049 Mon Sep 17 00:00:00 2001 From: Arpad Krejczinger Date: Wed, 17 Sep 2025 01:09:41 +0200 Subject: [PATCH] docs: Add CLAUDE.md and permanent ban script - Add CLAUDE.md with AI assistant configuration - Add scripts/permanent-ban-repeat-offenders.sh for automated permanent banning - Script automatically detects and permanently bans IPs banned >4 times by fail2ban - Integrates with iptables and geoip-shell for comprehensive security --- CLAUDE.md | 46 +++++++++++++++- scripts/permanent-ban-repeat-offenders.sh | 64 +++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100755 scripts/permanent-ban-repeat-offenders.sh diff --git a/CLAUDE.md b/CLAUDE.md index 6eb2096..cae41dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -192,4 +192,48 @@ curl -X PROPFIND https://hoborg:AdminPass2024!@ak-homelab.duckdns.org/files/ \ - Some containers (like Gitea with s6-overlay) need root start then privilege drop via USER_UID/USER_GID environment variables - Test each security change individually, not in batches - Network access patterns matter: SSH Git needs direct access, HTTP can be proxied through localhost -- DO NOT set Docker user: directive for services using s6-overlay init systems (breaks initialization) \ No newline at end of file +- DO NOT set Docker user: directive for services using s6-overlay init systems (breaks initialization) + +## Claude Development Guidelines + +### CRITICAL: Anti-Pattern Reminders +**BEFORE writing ANY script or solution, Claude MUST:** + +1. **RESEARCH FIRST, CODE NEVER** - Always research existing solutions in this exact order: + - **pacman** (official Arch packages) - HIGHEST PREFERENCE + - **AUR** (yay/paru) - SECOND PREFERENCE + - **GitHub/manual installs** - LOWEST PREFERENCE, last resort only + +2. **VERIFY SYNTAX BEFORE WRITING** - Never generate scripts with broken syntax: + - Use `--help` to check command syntax BEFORE using it + - Test commands in small parts first + - Never assume argument names or formats + +3. **STICK TO THE CHOSEN SOLUTION** - Don't drift away from proven solutions: + - If research finds tool X works, USE tool X + - Don't randomly switch to tool Y mid-implementation + - Finish what you start before considering alternatives + +4. **PREFER SIMPLE OVER COMPLEX**: + - Use existing tools rather than writing custom scripts + - Bash for simple tasks, Python only when complexity requires it + - One working solution beats three broken attempts + +5. **CHECK PACKAGE REPOSITORIES FIRST**: + - Always check `pacman -Ss` before any manual installation + - Always check `yay -Ss` before downloading random scripts + - Maintained packages > GitHub scripts > custom solutions + +### Failure Patterns to Avoid +- ❌ Writing broken syntax without testing commands first +- ❌ Switching solutions mid-implementation without reason +- ❌ Overcomplicating when simple solutions exist +- ❌ Installing random scripts before checking packages +- ❌ Creating custom tools when proven ones exist + +### Success Pattern to Follow +- ✅ Research → Choose → Verify syntax → Implement → Test +- ✅ pacman → AUR → GitHub (in that preference order) +- ✅ Use proven, maintained tools over custom scripts +- ✅ Test command syntax with `--help` first +- ✅ Stay focused on the chosen solution \ No newline at end of file diff --git a/scripts/permanent-ban-repeat-offenders.sh b/scripts/permanent-ban-repeat-offenders.sh new file mode 100755 index 0000000..9b1c144 --- /dev/null +++ b/scripts/permanent-ban-repeat-offenders.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Permanent ban script for IPs that have been banned more than 4 times by fail2ban +# Run via cron: 0 */6 * * * /home/hoborg/homelab/scripts/permanent-ban-repeat-offenders.sh + +LOGFILE="/var/log/permanent-ban.log" +BANLIST_FILE="/etc/fail2ban/permanent-banlist.conf" +THRESHOLD=4 + +# Function to log with timestamp +log_message() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOGFILE" +} + +# Ensure permanent banlist file exists +if [ ! -f "$BANLIST_FILE" ]; then + touch "$BANLIST_FILE" + chmod 644 "$BANLIST_FILE" +fi + +log_message "Starting repeat offender analysis (threshold: $THRESHOLD bans)" + +# Get list of IPs with ban counts from fail2ban logs +REPEAT_OFFENDERS=$(grep "Ban " /var/log/fail2ban.log* | \ + grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \ + sort | uniq -c | sort -nr | \ + awk -v threshold="$THRESHOLD" '$1 > threshold {print $2 " " $1}') + +if [ -z "$REPEAT_OFFENDERS" ]; then + log_message "No repeat offenders found above threshold" + exit 0 +fi + +# Process each repeat offender +echo "$REPEAT_OFFENDERS" | while read ip ban_count; do + # Skip if already permanently banned + if grep -q "^$ip" "$BANLIST_FILE"; then + log_message "IP $ip already permanently banned (skip)" + continue + fi + + # Get country information + COUNTRY=$(whois "$ip" 2>/dev/null | grep -i "country:" | head -1 | awk '{print $2}') + + # Add to permanent banlist + echo "$ip # Banned $(date '+%Y-%m-%d') - $ban_count offenses - Country: $COUNTRY" >> "$BANLIST_FILE" + + # Add permanent iptables rule + iptables -I INPUT -s "$ip" -j DROP + + # Make iptables rule persistent (systemd approach) + if command -v iptables-save >/dev/null 2>&1; then + iptables-save > /etc/iptables/iptables.rules 2>/dev/null || true + fi + + log_message "PERMANENTLY BANNED: $ip ($ban_count offenses, Country: $COUNTRY)" +done + +# Reload fail2ban to ensure it sees the updated banlist +if systemctl is-active --quiet fail2ban; then + log_message "Reloading fail2ban service" + systemctl reload fail2ban +fi + +log_message "Repeat offender analysis completed" \ No newline at end of file