
Automatisieren Sie das Blockieren bösartiger Bots am Zugriff auf Ihren Server
Eine TUI, eine Web-UI und eine CLI, die dir helfen, deinen Server so zu konfigurieren, dass böswillige Bots gestoppt werden, ohne sich hinter einem CDN zu verstecken.
Es arbeitet zusammen mit NGINX und deiner bestehenden Firewall (iptables oder nftables) auf zwei getrennten Ebenen:
Die App versucht ihr Bestes, um dich nicht vom Server auszusperren, aber du verwendest sie auf eigenes Risiko. Und beachte, dass sie unter AGPL lizenziert ist. Wenn du sie kommerziell nutzt, stelle sicher, dass du den Wortlaut der Lizenz einhältst.
Von crates.io:``` cargo install stop-bots
Oder aus einem Checkout erstellen:```
cargo install --path .
Ein vorgefertigtes x86_64-Linux-Binary ist auf den GitHub-Releases verfügbar.
Erfordert Rust 1.88 oder neuer zum Bauen. In der Praxis nur unter Linux: Es ruft
systemctl, nginx -t und nft/iptables auf, sodass es zwar anderswo kompiliert, dort aber
kaum nützlich sein wird.
Führe das Binary ohne Argumente aus, um die TUI zu starten, stop-bots web für dieselben Ansichten
in einem Browser (siehe Die Web-UI), oder siehe stop-bots --help für die vollständige Liste der
CLI-Unterbefehle. TUI und CLI können zusammen verwendet werden. Konfiguriere alles in der TUI und
verwende dann die CLI in einem Crontab, um die Regeln aktuell zu halten.
Du kannst die App mit 'q' oder Escape beenden oder aus einem Popup/Untermenü zurückgehen.
Du kannst mit 't' zwischen dem dunklen und dem hellen Theme wechseln. Die App versucht, das Theme automatisch zu erkennen, aber für einige Terminal- und Multiplexer-Kombinationen sind nicht genügend Informationen verfügbar, um die richtige Wahl zu treffen.
1–4 (oder d/b/s/p) springen direkt zu einem Bildschirm; Links/Rechts oder ihre vim-Aliase h/l
schalten durch sie; ? schaltet jederzeit eine vollständige Tastenbelegungsreferenz um, und :
öffnet eine Befehlspalette, die jede Aktion namentlich auflistet. Tab / Shift+Tab wechseln immer zwischen
den Panels des aktuellen Bildschirms, niemals zwischen Bildschirmen.
Das Dashboard besitzt alles, was im Firewall-Skript landet; die Site-Einstellungen besitzen alles, was in der NGINX-Konfiguration landet. Diese Aufteilung entscheidet, wo eine bestimmte Einstellung lebt.
Up/Down wechseln zwischen den drei Listen; m schaltet den Geo-Modus um. Drücke F, um die
aktuellen Firewall-Regeln in ein Skript zu rendern — das
Popup hat außerdem einen Schalter „nach dem Schreiben anwenden“ (Leertaste), um es tatsächlich sofort durchzusetzen,
statt es danach von Hand anzuwenden. Drei weitere Tasten wirken auf den gesamten
Host: u lädt jede Liste herunter, a wendet beide Ebenen an (NGINX, dann die Firewall), und
w stellt diese Konsole hinter NGINX — dieselben drei, die der Browser als Buttons und ein
Panel hat.Tab zum Fokussieren), das die hostweiten
Auswahlmöglichkeiten enthält, die die generierte Konfiguration prägen — was eine blockierte Anfrage zurückbekommt (siehe unten),
ob eine generierte robots.txt ausgeliefert werden soll und Rate Limiting — über jeder auf der Festplatte
entdeckten NGINX-Site, jede mit einem Live-Status „aktuell / veraltet / nicht gefunden“ und Aktionen, um
die aktuelle Richtlinie auf eine Site oder alle anzuwenden. Das Ändern einer dieser Einstellungen setzt
jede angewendete Site auf STALE, was dein Signal zum erneuten Anwenden ist. Das Öffnen einer Site lässt dich
ihre Kategorie-/Bot-Richtlinie überschreiben, eine der sechs Request-Shape-Regeln einschalten und
Pfade auflisten, die vom Blockieren ausgenommen sind.NOT BLOCKED/BLOCKED (rot dargestellt). Tab/Shift+Tab wechseln, auf welches der beiden Panels
Up/Down wirken; f durchläuft einen gemeinsamen Filter (alle / nur nicht blockierte / nur blockierte);
Enter blockiert die ausgewählte NOT BLOCKED-Zeile oder hebt die Blockierung auf, wenn sie bereits BLOCKED ist.
i untersucht die ausgewählte Adresse: welche der Reputations-Feeds sie auflisten, ob sie
innerhalb eines veröffentlichten Crawler-Bereichs liegt (was einen echten Googlebot von einem User
Agent unterscheidet, der es nur behauptet), zu welchem Land sie gehört und als welche Konten sie sich
anzumelden versuchte. Alles aus Listen, die dieser Host bereits heruntergeladen hat — es gibt hier kein Reverse-
DNS oder Whois-Lookup, weil ein PTR-Eintrag von demjenigen geschrieben wird, dem die Adresse gehört,
und angreifergelieferter Text wäre, der als autoritativ gelesen wird.Bot-Einstellungen, wo jede Listen-Quelle und jeder einzelne Bot lebt:
Site-Einstellungen, wo die hostweiten NGINX-Auswahlmöglichkeiten über jeder auf der Festplatte gefundenen Site stehen:
Dynamischer Schutz, die Live-Ansicht dessen, was gerade jetzt auf den Server trifft:
Bekannte Bots, nach Kategorie (Scanner / Suchmaschine / KI-Crawler), bezogen aus
ArcJet's Well-Known Bots,
ai.robots.txt und der
NGINX Ultimate Bad Bot Blocker
Liste. Das Blockieren einer Kategorie fügt eine if ($http_user_agent ...)-Regel in die NGINX-
Konfiguration jeder Site ein (apply-blocks / a/A in den Site-Einstellungen).
Zu viele Anfragen, über NGINXs eigenes Rate Limiting. Anders als alles andere hier wird dies von NGINX zur Anfragezeit durchgesetzt, statt einen Log danach zu analysieren. Standardmäßig aus: ein für die falsche Site abgestimmtes Limit weist echte Besucher ab.
Zuerst höflich — eine optionale generierte robots.txt, die jeden Bot auflistet, den du blockierst,
für die Crawler, die sie respektieren, plus den Honeypot-Pfad unten. Standardmäßig aus, weil sie
ersetzt, was deine Site heute unter /robots.txt ausliefert.
Außer wo du etwas anderes sagst — Pfad-Ausnahmen pro Site, sodass du KI-Crawler
überall außer /blog blockieren kannst.
Anfragen, die nicht wie ein Browser aussehen, pro Site. Sechs unabhängige Regeln, jede mit eigenem Schalter und jede standardmäßig aus — ein Schalter pro Regel, damit du, wenn etwas von dir aufhört zu funktionieren, erkennen kannst, welche Regel es verursacht hat:
Zwei Schutzmaßnahmen gelten für alle und werden durchgesetzt, statt dir überlassen zu werden:
server-Blöcke geschrieben. Browser
machen kein HTTP/2 ohne TLS, also ist auf einem einfachen listen 80-Block jede Anfrage HTTP/1.1 —
einschließlich der Weiterleitung, die ein Browser auf dem Weg zu HTTPS macht. Deine Port-80- und Port-443-
Blöcke teilen sich üblicherweise einen server_name, sodass die Einstellung beide erreicht; nur der TLS-Block bekommt
diese Regeln. Die Header-Shape-Regeln funktionieren über einfaches HTTP und werden in beide geschrieben./.well-known/ ist immer ausgenommen, sobald eine Regel aktiv ist. Dort holt Let's Encrypt
seine HTTP-01-Challenge, über HTTP/1.1 ohne Accept und oft ohne User-Agent —
ohne die Ausnahme hört dein Zertifikat Wochen später auf, sich zu erneuern.Eine hostweite Auswahl, in den Site-Einstellungen. Diese Statuscodes sind nicht austauschbar — jeder sagt etwas anderes, und der Unterschied zählt am meisten für die Clients, die du nicht treffen wolltest:
| Option | Wofür sie gedacht ist |
|---|---|
403 Forbidden (Standard) | sagt, dass die Blockierung absichtlich war; die einzige, auf die ein fälschlich getroffener Mensch reagieren kann |
404 Not Found | verbirgt, dass überhaupt etwas blockiert wurde |
410 Gone | bittet wohlerzogene Crawler, die URL endgültig zu verwerfen — bevorzuge dies gegenüber 403, wenn du Crawler statt Angreifer abweist |
429 Too Many Requests | sagt einem höflichen Client, sich zurückzuziehen und es erneut zu versuchen |
418 I'm a teapot | der Scherz aus RFC 2324. Er funktioniert; er ist nur nicht IANA-registriert, und NGINX sendet ihn mit leerem Body |
444 close connection | überhaupt keine Antwort; am günstigsten, aber nicht von einem ausgefallenen Server zu unterscheiden |
Tarpit | antwortet mit 403, tröpfelt aber den Body mit einem Byte pro Sekunde, sodass der Client wartet, statt weiterzuziehen |
Der Tarpit ist die sanfteste Option bei einem Fehlalarm — ein fälschlich getroffener Client wird verlangsamt,
nicht abgewiesen — und die härteste bei den Kosten für einen Bot, dessen Verbindung untätig bleibt. Zwei Dinge, die du
vor der Wahl wissen solltest: Er hält auch für die Dauer eine deiner Worker-Verbindungen, sodass
eine Flut getarpiteter Clients mit echten Besuchern um worker_connections konkurriert; und wie
lange er tatsächlich dauert, hängt davon ab, wie NGINX einen kleinen Fehler-Body zu schreiben wählt, was
in TODO.md als gegen einen echten Server zu prüfen vermerkt ist.
Jede davon ist ein unabhängiger Schalter im Panel „Automatisches Blockieren“ des Dashboards, und jede fügt eine temporäre Firewall-Blockierung hinzu, die von selbst abläuft und erneut hinzugefügt wird, wenn das Verhalten anhält.
Sie laufen auf einem internen Timer, der deine SSH- und NGINX-Zugriffslogs jede Minute neu liest — aber nur, solange die TUI oder die Web-UI läuft. Beide halten denselben Zeitplan, in derselben Datenbank, sodass es reicht, die Web-UI laufen zu lassen; nichts wird erkannt, wenn keine von beiden läuft. Für einen Server ganz ohne stop-bots-Prozess siehe Unbeaufsichtigt, per Cron unten.
/.env, /.git/config,
/wp-config.php und Ähnlichem ist für sich genommen schlüssig, also braucht dies keine Schwelle. Die
eingebaute Liste lässt bewusst Pfade weg, die irgendwo legitim sind — /wp-login.php,
/wp-admin/, /xmlrpc.php, /phpmyadmin — da das Aussperren deines eigenen Administrators schlimmer wäre
als einen Scanner zu verpassen, den der 404-Detektor ohnehin fängt. Füge eigene mit
set-probe-paths hinzu.Disallow: in der generierten robots.txt veröffentlicht und nirgends verlinkt ist.
Ihn zu erreichen bedeutet, robots.txt zu ignorieren, was nichts Legitimes versehentlich tut —
das stärkste Signal hier und die längste Blockierung. Erfordert, dass die robots.txt-Generierung eingeschaltet ist, um
überhaupt zu funktionieren.Drei weitere betrachten, wie ein Client sich verhält, statt wonach er fragt. Alle drei sind standardmäßig aus, weil jeder einen Fehlalarm hat, den er nicht allein ausschließen kann — und alle drei nehmen verifizierte Suchmaschinen-Crawler aus, die sonst auf jeden von ihnen passen würden:
304 zählt als geladenes Asset).
Kann dir auf einer Site nicht helfen, die überhaupt keine Assets ausliefert — eine reine JSON-API.Referer. Abgeschwächt durch
Referrer-Policy: no-referrer und Datenschutz-Tools; die Schwelle für verschiedene Pfade ist das, was
es überhaupt brauchbar macht./24 im selben Durchlauf markiert
werden, blockiere das /24. Standardmäßig aus — 256 Adressen zu blockieren, weil drei sich schlecht benommen haben, ist Kollateralschaden by design. (IPv6 ist anders und braucht keinen Schalter: Eine Erkennung
blockiert immer das /64, weil ein /64 ein LAN ist, dasselbe, was eine einzelne IPv4-Adresse
repräsentiert. Die einzelne Adresse zu blockieren, die ein IPv6-Angreifer zufällig benutzt hat, würde nichts stoppen
— er hat 2^64 weitere.)Alles oben ist generiert. Ob irgendetwas davon in Kraft ist, ist eine separate
Frage, und stop-bots status ist diejenige, die sie beantwortet:```
stop-bots status
Sieben Prüfungen, und die erste ist die, die es wert ist: Sind die generierten Regeln tatsächlich im Kernel oder nur auf der Festplatte? Ein echter Host lief drei Wochen lang mit 48.860 Drop-Regeln in `/etc/stop-bots/firewall.nft` und einem leeren Ruleset, weil das Schreiben des Skripts und das Laden zwei Schritte sind und niemand jemals den zweiten betrachtet hatte.
Der Rest: Übersteht das Ruleset einen Neustart (`nftables.service` aktiviert?), stimmt das Skript noch mit den Regeln überein, sind die NGINX-Blöcke angewendet, läuft der Konsolendienst mit der Binärdatei, die er angibt, ist Platz für die Datenbank vorhanden, und können die Detektoren ihre Logs lesen.
Es beendet sich mit einem Nicht-Null-Code, wenn etwas **CRITICAL** ist, sodass es als Monitoring-Check funktioniert. `--quiet` gibt nur aus, was Aufmerksamkeit erfordert, was die Form für cron ist:```
0 * * * * /usr/local/bin/stop-bots status --quiet
Eine Prüfung, die nicht ausgeführt werden konnte — nft list benötigt Root-Rechte — meldet UNKNOWN, niemals
OK. Eine Zustandsprüfung, die meldet, dass alles in Ordnung sei, weil sie nicht nachsehen konnte, ist
schlimmer als keine, denn ihr wird geglaubt.
Derselbe Bericht befindet sich im Dashboard sowohl in der Konsole als auch in der TUI, stündlich
vom internen Cron erstellt statt bei jedem Rendern: nft list auf einem großen
Regelwerk sind Megabytes an Text.
Jede Firewall-Entscheidung oben wird generiert, niemals automatisch angewendet: render-firewall
(oder die f-Taste des Dashboards) schreibt ein iptables- oder nftables-Skript, das du selbst prüfen und anwenden
kannst, und weigert sich, eines zu schreiben, das eine aktuell verbundene SSH-Sitzung aussperren würde.
Drei Dinge können es für dich anwenden, und alle drei erfordern, dass du danach fragst: das Render-Popup der TUI
(„nach dem Schreiben anwenden") oder ihre a-Taste, das Firewall-Panel der Web-Konsole („nach dem
Schreiben ausführen") oder ihre Schaltfläche „Alles anwenden", und batch --apply aus einem von dir geschriebenen Crontab — siehe
Unbeaufsichtigt, aus cron. Nichts davon ist ein Nebeneffekt von irgendetwas
Automatischem: Der interne Cron rendert das Skript und führt es niemals aus.
Dasselbe gilt auf der NGINX-Seite: Eine Einstellung zu ändern ändert nur, was geschrieben würde.
Die Site-Einstellungen zeigen jede Site als STALE, bis du sie anwendest.
Einen Detektor abzuschalten entfernt niemals bereits hinzugefügte Sperren — diese laufen von selbst ab.
„Erkennung stoppen" und „rückgängig machen, was erkannt wurde" sind bewusst getrennt; Letzteres ist der
Bildschirm „Dynamic Protection" oder remove-firewall-rule.
Es gibt außerdem eine einfache Zugriffsprotokoll-Zählung, unabhängig vom Blockieren: record-access-stats /
list-access-stats zählen, wie oft jeder User-Agent in erfolgreichen (fehlerfreien)
Anfragen auftaucht, sodass du sehen kannst, wer tatsächlich zu Besuch ist, zusätzlich dazu, wer blockiert wird.
stop-bots batch ist ein Durchlauf über alles, was die TUI von Hand tut: jede Liste aktualisieren,
die Logs scannen, die NGINX-Blockierungsregeln und das Firewall-Skript schreiben.```
0 4 * * * root /usr/local/bin/stop-bots batch --apply --ssh-log /var/log/auth.log
*/10 * * * * root /usr/local/bin/stop-bots batch --apply --no-fetch --ssh-log /var/log/auth.log
Es sagt nichts, wenn alles funktioniert hat, also schickt ein gesunder nächtlicher Lauf keine E-Mail. Ein fehlgeschlagener Schritt gibt eine Meldung auf stderr aus und setzt einen Exit-Status ungleich null, was dazu führt, dass cron Sie darüber informiert. Führen Sie es zuerst einmal manuell mit `--verbose` aus — das gibt eine Zeile pro Schritt aus und ist der einfachste Weg zu sehen, was es tatsächlich tut.
**`--apply` ist das, was es überhaupt etwas durchsetzen lässt.** Ohne es schreibt `batch` die NGINX-Konfiguration und das Firewall-Skript und hört dann auf: Die Konfiguration bewirkt nichts bis zu einem Reload, ein Skript bewirkt nichts, bis es ausgeführt wird. Das ist überall in diesem Projekt die Standardeinstellung, und hier bleibt es die Standardeinstellung.
`batch` und ein langlaufendes Front-End koexistieren sicher. Die TUI, die Web-UI und `batch` zeichnen alle auf, was sie getan haben, über dieselben Schlüssel in derselben Datenbank, sodass derjenige, der zuerst an einen Job gelangt, ihn erledigt und die anderen feststellen, dass er nicht mehr fällig ist — Sie bekommen keine zwei Erkennungsdurchläufe, und das Panel „Scheduled tasks" des Dashboards zeigt, was tatsächlich passiert ist, anstatt zu behaupten, alles sei überfällig. Wenn Sie die Web-UI bereits laufen lassen, ist der nächtliche `batch`-Eintrag eher eine Absicherung als eine Notwendigkeit; wenn nicht, ist er das Einzige, was die Erkennung aktuell hält.
**Mit `--apply` kann die SSH-Lockout-Schutzvorrichtung ablehnen — und eine Ablehnung bedeutet, dass nichts angewendet wird.** Sie lehnt ab, wenn die Regeln einen Client blockieren würden, der gerade jetzt verbunden ist, *und* wenn überhaupt kein SSH-Log gelesen werden konnte, weil dann die Prüfung nicht ausgeführt werden konnte. Das interaktive `render-firewall` gibt im zweiten Fall nur einen Hinweis aus, mit der Begründung, dass ein Mensch das Terminal beobachtet; von cron aus tut das niemand. **Übergeben Sie `--ssh-log` explizit**: cron läuft als root, sodass `/var/log/auth.log` normalerweise problemlos gelesen wird, aber auf einem reinen journald-Host kann `journalctl` unter cron leer zurückkommen, was genau der Fall ist, in dem abgelehnt wird. `--force` übergeht die Schutzvorrichtung, wenn Sie es ernst meinen.
Ein fehlgeschlagener Schritt stoppt nie die anderen, und die NGINX- und Firewall-Hälften sind unabhängig — ein fehlgeschlagener NGINX-Reload hinterlässt die Firewall trotzdem angewendet, und umgekehrt.
`batch` zeichnet jeden Schritt gegen denselben Zeitplan auf, den der interne cron der TUI verwendet, sodass beide darin übereinstimmen, was bereits gelaufen ist, anstatt es beide zu tun, und das Panel „Scheduled tasks" des Dashboards zeigt, was Ihr echter cron getan hat.
# Die Web-UI
`stop-bots web` liefert dieselben fünf Bildschirme in einem Browser aus.```
stop-bots web
Es bindet 127.0.0.1:8787 — nur von dieser Maschine aus erreichbar — und gibt beim ersten Start einmalig ein generiertes Passwort aus. Erreiche es von deinem Laptop aus über einen SSH-Tunnel:``` ssh -L 8787:127.0.0.1:8787 your-server
then open <http://127.0.0.1:8787/>.

The console follows the operating system's light or dark setting, with a toggle in the
header; the TUI screenshots above are the dark theme, these the light one. The keys the
TUI uses work here too: `1`–`4` switch screens, `/` focuses the search box, `?` opens Help.

Three host-wide actions live in the header, in the browser as buttons and in the TUI
as single keys:
- **Update everything** (`u`) downloads every bot list, every crawler IP range, every
*enabled* reputation feed and every *selected* country — the same set `stop-bots batch`
fetches, from the same plan. One source failing does not stop the rest, and nothing is
enforced until something applies it.
- **Apply everything** (`a`) writes and reloads the NGINX config, then writes and runs the
firewall script. The two planes are independent: whichever fails, the other still gets
its turn, because a half-applied host beats one where an NGINX syntax error also left
the firewall stale.
- **Web Access** (`w`) sets NGINX up to serve the console itself — see
[Behind NGINX](#behind-nginx-a-subdomain-or-a-path-prefix).
## As a service (Debian)```
sudo stop-bots install web
Schreibt /etc/systemd/system/stop-bots-web.service, erstellt /var/lib/stop-bots (0700 — es
enthält den Passwort-Hash der Konsole) und /etc/stop-bots, generiert ein Passwort, falls noch
keines existiert, und aktiviert und startet die Unit.
--dry-run gibt den gesamten Plan aus und ändert nichts. Dies ist der einzige Befehl im
Projekt, der einen Daemon startet, also fange dort an. --prefix <dir> schreibt denselben Baum
an einen Ort, den du ohne Root lesen kannst. Wenn die Unit bereits existiert und du sie bearbeitet
hast, stoppt der Installer und teilt dies mit, anstatt deine Änderung zu überschreiben; --force,
wenn du es ernst meinst.
Der Dienst läuft als root, weil die Konsole /etc/nginx umschreibt, das Firewall-Skript
schreibt und nginx -t sowie systemctl reload nginx ausführt. Es gibt keine unprivilegierte
Aufteilung, die den Funktionsumfang intakt lässt. Die Unit trägt die Härtung, die diese
Anforderung übersteht, sowie einen Kommentar, der angibt, welche Härtung weggelassen wurde und
warum.
Die Bind-Adresse, die Host-Allowlist und das Pfad-Präfix sind bewusst nicht in der Unit — der
laufende Server liest sie erneut aus der Datenbank, sodass sie in ExecStart zwei Quellen der
Wahrheit hätten. Ändere sie mit stop-bots web --save ... und starte neu.
Eine Sache ändert sich, sobald dies als root läuft: Der tägliche RenderFirewall-Job des internen
Cron kann nun /etc/stop-bots/firewall.nft schreiben, was nicht möglich war, als du die Konsole
manuell als du selbst ausgeführt hast. Nichts wendet dieses Skript an — es auszuführen bleibt
weiterhin dir überlassen.
Es wird nur auf Debian geprüft, weil das getestet wurde; die Unit ist sehr wahrscheinlich auf jeder systemd-Distribution korrekt, aber der angenommene SSH-Log-Pfad ist der von Debian.
Etwas anderes als Loopback zu binden, erfordert ein zweites, bewusstes Flag, weil diese Konsole die Firewall und die NGINX-Konfiguration des Hosts, auf dem sie läuft, umschreiben kann:``` stop-bots web --bind 0.0.0.0:8787 --expose --allowed-hosts admin.example.com --save
`--allowed-hosts` ist in der Praxis nicht optional: Eine Anfrage mit einem Hostnamen, der nicht
aufgeführt ist, wird abgelehnt. Das ist es, was DNS-Rebinding gegen die Konsole scheitern lässt, und es ist
der Grund, warum ein exponierter Server, der über einen Namen erreicht wird, den Namen explizit benötigt.
Setzen Sie ihn hinter NGINX mit TLS — denselben NGINX, den dieses Tool schützt. Wenn Sie das tun und der
Proxy `X-Forwarded-For` setzt, teilen Sie der Konsole mit, dass sie diesem Header vertrauen darf, sonst kann sie nicht
erkennen, von welcher Adresse eine Anfrage wirklich kam:```
stop-bots web --bind 127.0.0.1:8787 # and set web:trust_forwarded_for
Hinter TLS setzen Sie außerdem web:secure_cookie. Ohne dies sendet ein Browser das Session-
Cookie auch an eine http://-URL für denselben Host.
web:trust_forwarded_for ist wichtiger, als es aussieht. Ohne dies kommt jede Anfrage hinter einem
Proxy von 127.0.0.1 an, sodass die Konsole einen Client nicht von einem anderen unterscheiden kann — was
bedeutet, dass eine Flut von Login-Versuchen denselben Throttle-Bucket wie Sie teilt und der Guard, der
Sie daran hindert, Ihre eigene Adresse zu blockieren, nichts zum Vergleichen hat. Mit dieser Option
funktionieren beide pro Client.
Die Konsole kann dies für Sie einrichten, und die TUI kann das auch (w auf dem Dashboard). Beide
schreiben die NGINX-Konfiguration, zeichnen das Pfadpräfix auf und fügen den Hostnamen zur Allowlist hinzu — die
drei Dinge, die übereinstimmen müssen, denn ein fehlendes Präfix lässt jeden Link den
location-Block verlassen und ein fehlender Hostname macht jede Anfrage zu einem 403. Beide validieren mit
nginx -t, bevor die Konfiguration wirksam werden kann, machen sie rückgängig, wenn dies fehlschlägt, und zeichnen die
neue Adresse erst auf, nachdem sie validiert wurde.
Zwei Modi, und Pfad ist aus einem Grund die Standardeinstellung: Er fügt einen location-Block zu einer Site hinzu, die Sie
bereits haben, sodass die Konsole das Zertifikat dieser Site erbt. Eine Subdomain benötigt ein eigenes,
und bis certbot --nginx -d <host> ausgeführt wurde, übertragen das Passwortformular und das Session-
Cookie dieser Konsole Daten unverschlüsselt über das Netzwerk.
Der Rest dieses Abschnitts behandelt dasselbe manuell, was es wert ist, einmal gelesen zu werden, selbst wenn Sie das Panel verwenden — die unten beschriebene Trailing-Slash-Falle ist der Fehler, den es zu verhindern gilt.
Eine Subdomain ist die einfachere Bereitstellung, und die, die Sie wählen sollten, wenn Sie können:```nginx server { server_name stopbots.example.com; location / { proxy_pass http://127.0.0.1:8787; proxy_set_header Host $host; } }
| `-s` | `--server` | Server URL (default: `http://localhost:8080`) |
| `-t` | `--token` | API token for authentication |
| `-o` | `--output` | Output format: `json`, `yaml`, `table` (default: `table`) |
| `-v` | `--verbose` | Enable verbose output |
| `-q` | `--quiet` | Suppress non-essential output |
| `-h` | `--help` | Show help message |
| `-V` | `--version` | Show version information |
### Beispiele
```bash
# Standardverbindung zum lokalen Server
kitploit-cli scan --target example.com
# Verbindung zu einem Remote-Server mit Authentifizierung
kitploit-cli scan --target example.com --server https://api.example.com --token YOUR_TOKEN
# Ausgabe im JSON-Format
kitploit-cli scan --target example.com --output json
# Ausführliche Ausgabe aktivieren
kitploit-cli scan --target example.com --verbose
Die CLI unterstützt eine Konfigurationsdatei unter ~/.kitploit/config.yaml:
server: http://localhost:8080
token: your-api-token
output: table
verbose: false
timeout: 30
| Variable | Beschreibung |
|---|---|
KITPLOIT_SERVER | Server-URL |
KITPLOIT_TOKEN | API-Token |
KITPLOIT_OUTPUT | Ausgabeformat |
KITPLOIT_TIMEOUT | Timeout in Sekunden |
Alle API-Anfragen erfordern einen gültigen API-Token im Authorization-Header:
Authorization: Bearer YOUR_API_TOKEN
| Methode | Endpunkt | Beschreibung |
|---|---|---|
GET | /api/v1/health | Integritätsprüfung |
GET | /api/v1/scans | Alle Scans auflisten |
POST | /api/v1/scans | Neuen Scan starten |
GET | /api/v1/scans/{id} | Scan-Details abrufen |
DELETE | /api/v1/scans/{id} | Scan löschen |
GET | /api/v1/scans/{id}/results | Scan-Ergebnisse abrufen |
# Integritätsprüfung
curl http://localhost:8080/api/v1/health
# Neuen Scan starten
curl -X POST http://localhost:8080/api/v1/scans \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "scan_type": "full"}'
# Scan-Ergebnisse abrufen
curl http://localhost:8080/api/v1/scans/SCAN_ID/results \
-H "Authorization: Bearer YOUR_API_TOKEN"
Stellen Sie sicher, dass der Server läuft:
curl http://localhost:8080/api/v1/health
Überprüfen Sie die Firewall-Einstellungen und stellen Sie sicher, dass der Port nicht blockiert ist.
Überprüfen Sie die Server-URL in Ihrer Konfigurationsdatei oder den Umgebungsvariablen.
Überprüfen Sie, ob Ihr API-Token gültig und nicht abgelaufen ist.
Stellen Sie sicher, dass der Authorization-Header korrekt formatiert ist.
Überprüfen Sie, ob Ihr Konto über die erforderlichen Berechtigungen verfügt.
Überprüfen Sie die Serverprotokolle auf Fehlermeldungen:
docker logs kitploit-server
Erhöhen Sie den Timeout-Wert in Ihrer Konfiguration.
Stellen Sie sicher, dass das Ziel erreichbar ist und nicht durch eine Firewall blockiert wird.
Reduzieren Sie die Anzahl gleichzeitiger Scans.
Erhöhen Sie die Speichergrenzen in Ihrer Docker-Konfiguration.
Überprüfen Sie die Datenbankgröße und bereinigen Sie alte Scan-Ergebnisse.``` stop-bots web --allowed-hosts stopbots.example.com --save
**Ein Pfadpräfix funktioniert ebenfalls**, aber die Konsole muss darüber informiert werden — sie muss jeden Link, jede Formularaktion, jede Weiterleitung und jeden Cookie-Pfad mit dem bereits enthaltenen Präfix generieren, und sie kann es nicht erraten:```
stop-bots web --base-path /stop-bots --allowed-hosts example.com --save
| -s | --server | Server-Modus aktivieren (erfordert --port) |
| -p | --port | Port für den Server-Modus (Standard: 8080) |
| -c | --config | Pfad zur Konfigurationsdatei |
| -v | --verbose | Ausführliche Ausgabe aktivieren |
| -h | --help | Hilfemeldung anzeigen |
| -V | --version | Versionsinformationen anzeigen |
# Grundlegende Verwendung
python main.py
# Server-Modus auf Port 9000
python main.py --server --port 9000
# Mit benutzerdefinierter Konfiguration
python main.py --config /path/to/config.yaml
# Ausführlicher Modus
python main.py --verbose
Die Anwendung kann über eine YAML-Konfigurationsdatei konfiguriert werden:
server:
host: "0.0.0.0"
port: 8080
debug: false
database:
url: "postgresql://localhost:5432/mydb"
pool_size: 10
logging:
level: "INFO"
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
project/
├── src/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ └── utils/
│ ├── __init__.py
│ └── helpers.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
├── requirements.txt
└── README.md
# Alle Tests ausführen
pytest
# Mit Coverage
pytest --cov=src
# Bestimmte Testdatei ausführen
pytest tests/test_main.py
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Dieses Projekt ist unter der MIT-Lizenz lizenziert – siehe die Datei LICENSE für Details.
**Der abschließende Schrägstrich bei `proxy_pass` ist entscheidend, und sein Fehlen ist der ganze Trick.**
Ohne ihn leitet NGINX den vollständigen Pfad weiter, und `stop-bots` sieht
`/stop-bots/whatever`, was es nun ausliefert und generiert. *Mit* einem abschließenden
Schrägstrich entfernt NGINX das Präfix — und dann löst der Browser die Links auf der Seite
gegen die Domain-Wurzel auf, landet außerhalb des `location`-Blocks, und alles gibt 404 zurück. Keine
noch so große Sorgfalt auf der Serverseite kann das beheben, also muss das Präfix den
Proxy überleben.
Von außen erzwingt das nichts, aber der Fehler ist laut statt subtil: mit dem
konfigurierten Präfix ist eine Anfrage ohne Präfix ein einfacher 404 statt einer Seite, die
halb funktioniert.
## Was es nicht tun wird
Zwei Dinge fehlen absichtlich, und der Hilfe-Bildschirm sagt das mit den Gründen:
- **Es wird nichts entsperren, was eine heruntergeladene Liste blockiert hat** — die nächste Aktualisierung dieser
Liste würde es stillschweigend rückgängig machen.
- **Es wird sein eigenes Passwort nicht ändern.** Verwenden Sie `stop-bots web --set-password` auf dem Host.
Es weigert sich außerdem, die Adresse zu blockieren, von der Sie verbunden sind, was die
Konsole entfernen würde, mit der Sie es rückgängig machen würden.
**Früher waren es drei.** Das Anwenden des Firewall-Skripts war das dritte, mit der Begründung, dass
seine Ausführung die einzige Operation ist, die den Host vom Netzwerk trennen kann. Das ist jetzt
verfügbar — „Apply everything" auf dem Dashboard (`a` in der TUI), oder das Kästchen „run it after
writing" im Firewall-Panel — weil die Absicherung, die es von cron aus sicher macht, es auch von einem
Knopf aus sicher macht: Die Regeln werden gegen die aktuell über SSH angemeldeten Clients geprüft, in der
Reihenfolge, in der das Skript selbst sie auswerten wird, und eine Regel, die einen von ihnen blockieren würde, ist eine
Ablehnung statt einer Warnung. Starten Sie die Konsole mit `--no-apply`, um das alte
Nur-Schreib-Verhalten zurückzubekommen.
Anmeldeversuche werden gedrosselt. Nicht weil das Passwort erratbar wäre — es wird generiert,
144 Bit — sondern weil die Verifizierung eines Versuchs Argon2id ausführt, und einen nicht authentifizierten Aufrufer
das so schnell betreiben zu lassen, wie er posten kann, ein Denial of Service gegen den Host ist, den dieses Tool
schützen soll. Zehn falsche Versuche sind kostenlos; danach zieht sich ein Client exponentiell
zurück, und eine globale Obergrenze begrenzt die CPU unabhängig davon, von wie vielen Adressen die
Versuche kommen.
## NGINX in einem Container ausführen
Wenn NGINX in Docker läuft und seine Konfiguration auf einem Bind-Mount liegt, lädt `systemctl reload nginx` nichts
neu. Richten Sie die beiden Befehle stattdessen auf den Container — das gilt auch für die CLI und die
TUI:```
stop-bots set-nginx-commands \
--test "docker exec web nginx -t" \
--reload "docker exec web nginx -s reload"
Der Befehl wird in Wörter aufgeteilt und direkt ausgeführt. Er durchläuft niemals eine Shell, daher sind ;,
| und $VAR gewöhnliche Zeichen und keine Syntax.
Wie der Code aufgebaut ist, wie er getestet wird und nach welchen Regeln er geschrieben wird, steht in CONTRIBUTING.md. Der Release-Prozess steht in RELEASING.md.
Du kannst mich unter [email protected] erreichen.
Copyright (C) 2026 Marko Ivankovic
Dieses Programm ist freie Software: Du kannst es unter den Bedingungen der GNU Affero General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (nach deiner Wahl) jeder späteren Version.
Siehe die Datei LICENSE für den vollständigen Text der Lizenz.
Eine alternative Lizenzierung ist NICHT verfügbar.
| Regel | Weist ab, außer Bots |
|---|
| HTTP/1.0 und HTTP/1.1 | Crawler und API-Clients, die kein HTTP/2 sprechen |
Kein Accept-Header | manche API-Clients senden keinen |
Kein Accept-Language | Datenschutz-Tools entfernen ihn |
Leerer/fehlender User-Agent | Skripte und Gesundheitsprüfungen lassen ihn oft weg |
Host ist eine bloße IP | bricht das Erreichen der Site per IP |
| TLS 1.0 / 1.1 | nur sehr alte Clients |