#!/usr/bin/env bash # npm-scanner – watch the NPM log directories for new hosts # https://git.retronerd.at/schiwagoa/npm-scanner # # fail2ban resolves logpath globs only when a jail starts. A proxy host created later # in NPM would therefore not be watched. This script reloads the jails as soon as a # log file appears that no jail watches yet. Bans survive the reload. # # npm-newlog-watch.sh [ …] # NPM_SCANNER_JAILS="npm-scanner npm-wplogin" jails that read NPM logs set -uo pipefail JAILS=${NPM_SCANNER_JAILS:-npm-scanner} SETTLE=${NEWLOG_SETTLE_SECONDS:-5} log() { echo "[npm-newlog] $*"; } [[ $# -gt 0 ]] || { log "no directories given"; exit 1; } # Wait until the fail2ban server answers (max. 5 minutes). for _ in $(seq 1 300); do fail2ban-client ping >/dev/null 2>&1 && break sleep 1 done if ! fail2ban-client ping >/dev/null 2>&1; then log "fail2ban does not answer – watcher stopped" exit 1 fi # Same mode the crazymax entrypoint chose (nft or legacy) is active by now. chain=$(grep -m1 -oE 'chain=[A-Za-z0-9_-]+' /etc/fail2ban/jail.local | cut -d= -f2) if [[ -n "$chain" ]] && ! iptables -n -L "$chain" >/dev/null 2>&1; then log "WARNING: iptables chain $chain does not exist – bans will fail. Is Docker running on the host?" fi watched() { local file=$1 jail for jail in $JAILS; do fail2ban-client get "$jail" logpath 2>/dev/null | grep -qF -- "$file" && return 0 done return 1 } declare -A tried=() log "watching $* (jails: $JAILS)" inotifywait -m -q -e create -e moved_to --format '%w%f' "$@" | while read -r file; do case "${file##*/}" in proxy-host-*_access.log|default-host_access.log|fallback_access.log|fallback_http_access.log) ;; *) continue ;; esac [[ -n "${tried[$file]:-}" ]] && continue # Give NPM time to finish writing the config, and debounce log rotation. sleep "$SETTLE" [[ -f "$file" ]] || continue watched "$file" && continue tried[$file]=1 for jail in $JAILS; do # npm-wplogin only reads proxy-host logs. [[ "$jail" == npm-wplogin && "${file##*/}" != proxy-host-* ]] && continue if fail2ban-client reload "$jail" >/dev/null 2>&1; then log "new log $file – $jail reloaded" else log "WARNING: reload of $jail failed for $file" fi done done