Initial version: npm-scanner image, filters, entrypoint, watcher, testbed

- Image based on crazymax/fail2ban:1.1.1 with inotify-tools
- Filters npm-scanner and npm-wplogin, tested with fail2ban-regex in the build
- jail.local generated from .env, optional npm-wplogin and recidive
- Watcher reloads the jails when NPM creates a new proxy host log
- check command, docs, testbed for LXC

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schiwagoaandClaude Opus 5.5 committed 2026-10-25 20:41:08 +01:00
commit d7dcd9d6b1
29 files changed
+1163

No files matched your search

+169
View File
@@ -0,0 +1,169 @@
# npm-scanner
fail2ban als Docker-Container für den **Nginx Proxy Manager (NPM)**: sperrt IPs, die nach
Schwachstellen scannen (`/.env`, `/.git/`, `phpinfo`, `/vendor/phpunit` …), direkt in der
Firewall des Hosts.
- Liest die Access-Logs des NPM nur lesend – für `jc21/nginx-proxy-manager` und `jlesage/nginx-proxy-manager`
- Sperrt in der Kette **`DOCKER-USER`** auf allen Ports (Docker leitet veröffentlichte Ports an `INPUT` vorbei)
- **3 Treffer in 1 Stunde → 24 h**, bei Wiederholung steigend bis **4 Wochen**
- Optional: WordPress-Login-Schutz (`npm-wplogin`) und Wiederholungstäter (`recidive`)
- Neue Proxy-Hosts im NPM werden automatisch überwacht, ohne Neustart
- Alle Einstellungen in einer `.env`, Filter werden beim Image-Build getestet
- `amd64` und `arm64`, Basis [crazymax/fail2ban](https://github.com/crazy-max/docker-fail2ban)
```
Internet ──▶ Host-Firewall ──DOCKER-USER──▶ NPM (Docker) ──▶ Dienste
▲ │ schreibt
│ sperrt ▼
npm-scanner ◀──── liest ──── proxy-host-*_access.log
(fail2ban, network_mode: host)
```
Bewusst **nicht** gezählt: reine 404 (Nextcloud-/Matrix-Clients erzeugen legitim viele davon)
und Weiterleitungen am Default-Host ohne Scanner-Pfad (Suchmaschinen).
## Quellen
| | |
|---|---|
| Repository | https://git.retronerd.at/schiwagoa/npm-scanner |
| Image | `git.retronerd.at/schiwagoa/npm-scanner` |
| Basis-Image | https://github.com/crazy-max/docker-fail2ban · https://hub.docker.com/r/crazymax/fail2ban |
| fail2ban | https://github.com/fail2ban/fail2ban |
| Nginx Proxy Manager | https://nginxproxymanager.com · https://github.com/jlesage/docker-nginx-proxy-manager |
## Voraussetzungen
- Linux-Host mit Docker Engine und Compose-Plugin (`docker compose version`)
- NPM läuft auf **demselben Host** als Container
- **Kein** fail2ban direkt auf dem Host, das dieselben NPM-Logs liest – sonst wird doppelt gesperrt
- Im LXC-Container: auf dem Proxmox-Host `pct set <id> --features nesting=1,keyctl=1`
## Installation
Alle Befehle als root.
### 1. Log-Ordner des NPM ermitteln
```bash
docker ps --format '{{.Names}}\t{{.Image}}' | grep -i proxy
```
Den Containernamen einsetzen:
```bash
docker inspect <npm-container> --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}'
```
| Image | Container-Pfad | `NPM_LOG_DIR` | `NPM_LOG_DIR2` |
|---|---|---|---|
| `jc21/nginx-proxy-manager` | `/data` | `<Quelle von /data>/logs` | leer |
| `jlesage/nginx-proxy-manager` | `/config` | `<Quelle von /config>/log` | `<Quelle von /config>/logs` |
Bei jlesage schreiben neu gespeicherte Hosts nach `logs/`, ältere nach `log/` – deshalb beide.
Kontrolle – es müssen `proxy-host-*_access.log` erscheinen:
```bash
ls /home/docker-projekte/nginx/data/log | grep proxy-host | head
```
### 2. Projektordner anlegen
```bash
mkdir -p /home/docker-projekte/npm-scanner
cd /home/docker-projekte/npm-scanner
curl -fsSLO https://git.retronerd.at/schiwagoa/npm-scanner/raw/branch/main/docker-compose.yaml
curl -fsSL -o .env https://git.retronerd.at/schiwagoa/npm-scanner/raw/branch/main/.env.example
chmod 600 .env
```
### 3. `.env` anpassen
```bash
nano .env
```
Mindestens `NPM_LOG_DIR`, `NPM_LOG_DIR2` und **`WHITELIST`** (eigene öffentliche IP, LAN, VPN,
Monitoring) setzen. Alle Variablen: [docs/configuration.md](docs/configuration.md).
### 4. Konfiguration prüfen (startet nichts)
```bash
docker compose run --rm npm-scanner check
```
Erwartet: die erzeugte `jail.local` und am Ende `OK: configuration test is successful`.
### 5. Starten
```bash
docker compose up -d
docker compose ps
docker logs npm-scanner
```
Erwartet im Log: `[npm-scanner] log directories: …`, `[npm-newlog] watching …` und
`Jail 'npm-scanner' started`. Nach etwa 30 Sekunden steht der Container auf `healthy`.
### 6. Prüfen
```bash
docker exec npm-scanner fail2ban-client status npm-scanner
docker exec npm-scanner fail2ban-client get npm-scanner logpath
```
Nach der **ersten** Sperre hängt die Regel in `DOCKER-USER`:
```bash
iptables -S DOCKER-USER
iptables -S f2b-npm-scan
```
## Betrieb
| | |
|---|---|
| Gesperrte IPs | `docker exec npm-scanner fail2ban-client get npm-scanner banip --with-time` |
| Entsperren | `docker exec npm-scanner fail2ban-client set npm-scanner unbanip <ip>` |
| Von Hand sperren | `docker exec npm-scanner fail2ban-client set npm-scanner banip <ip>` |
| Letzte Sperren | `docker logs npm-scanner 2>&1 \| grep ' Ban ' \| tail -20` |
| Update | `docker compose pull && docker compose up -d` |
Mehr – Backup, eigene Jails, Fehlersuche, Rückbau: [docs/operations.md](docs/operations.md).
## Warum kein eigenes Docker-Netzwerk?
fail2ban muss Regeln in der Firewall **des Hosts** setzen. Deshalb läuft der Container mit
`network_mode: host` und den Capabilities `NET_ADMIN` und `NET_RAW`. In einem Bridge-Netz
würde er nur die Firewall seines eigenen Netz-Namespaces ändern. Der Container öffnet keine
Ports und liest die NPM-Logs nur lesend.
## Dokumentation
| | |
|---|---|
| [Konfiguration](docs/configuration.md) | alle Variablen der `.env`, eigene Jails und Filter |
| [Betrieb](docs/operations.md) | Logs, Updates, Backup, Fehlerbehebung, Rückbau |
| [Sicherheit](SECURITY.md) | Rechte des Containers, Melden von Schwachstellen |
## Lizenz
[MIT](LICENSE). Basiert auf [crazymax/fail2ban](https://github.com/crazy-max/docker-fail2ban) (MIT)
und [fail2ban](https://github.com/fail2ban/fail2ban) (GPL-2.0).
---
<p align="center">
🏰 <a href="https://retronerd.at">Website</a> ·
🐘 <a href="https://social.retronerd.at/@schiwagoa">Mastodon</a> ·
🔒 <a href="https://retronerd.at/datenschutzerklaerung/">Datenschutz</a> ·
💬 <a href="https://helpdesk.retronerd.at/open.php">Support</a>
</p>
<p align="center">
<img src="https://dl.retronerd.at/Bilder/cat-footer.png" alt="" width="100%">
</p>
<p align="center">© Thomas Werner Schwaighofer · retronerd.AT · <a href="LICENSE">MIT Lizenz</a></p>