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:
commit
d7dcd9d6b1
29 files changed
+1163
No files matched your search
@@ -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>
|
||||
Reference in new issue
Block a user