# 🥷 ninja.sh — Handbuch

**Last-Triage für die RZ-Bereitschaft.** Wenn der Load brennt und die SLA-Uhr
läuft: ninja.sh macht in Sekunden eine read-only Momentaufnahme, damit du
**entscheiden** kannst statt zu raten — *Bot-Angriff* oder *WordPress-Anfänger
mit 500 Plugins*?

> Leitsatz: **Downtime setzen → Momentaufnahme → entscheiden → bestenfalls lösen.**
> ninja.sh führt **nie** selbst etwas aus. Es zeigt nur an. Handeln tust du in
> einer zweiten bash. Damit Meister schlafen kann. 🦉

---

## 1. Was es ist (und was nicht)

| Ist | Ist nicht |
|---|---|
| Read-only Momentaufnahme unter Last | Kein Dauer-Monitoring (dafür: Icinga/Zabbix) |
| Entscheidungshilfe Angriff vs. Eigenverschulden | Kein Auto-Blocker (kein fail2ban-Ersatz) |
| Ein einzelnes File zum Reinpasten per vim/ssh | Kein BSD (Fokus Debian/Ubuntu; BSD → andere wecken) |

**Wichtigste Eigenschaft:** schmal & portabel. Eine Datei, keine Abhängigkeiten
außer Standard-Tools. Auf jede Linux-Box kopierbar.

---

## 2. Installation

Reinpasten (vim) **oder** per scp, dann ausführbar machen:

```bash
chmod +x ninja.sh
```

**Optionale Tools** (für GeoIP-Länder & whois-CIDR — Debian/Ubuntu):

```bash
apt install -y whois geoip-bin geoip-database
```

Fehlt eins, degradiert ninja.sh sauber: ohne GeoIP keine `[CC]`-Marker, ohne
whois nur ein Hinweis. Der Rest läuft trotzdem.

---

## 3. Bedienung

### Interaktiv (Default)
```bash
./ninja.sh
```
Öffnet das Menü mit Live-Status-Header.

### Direkt (ohne Menü — gut zum Pipen)
```bash
./ninja.sh snap            # einmalige Momentaufnahme nach stdout
./ninja.sh watch           # Live-Aufnahme, Refresh alle 30s
./ninja.sh rec             # nur die Handlungs-Vorschläge
./ninja.sh whois <IP>      # eine IP nachschlagen (Land + Org + CIDR)
```

Snapshot wegspeichern oder verschicken (Farben fallen automatisch weg, wenn
die Ausgabe kein Terminal ist):
```bash
./ninja.sh snap > /tmp/triage_$(hostname)_$(date +%H%M).txt
```

### Menüpunkte

| Taste | Aktion |
|---|---|
| `1` | Momentaufnahme (ein Screen, alle Sektionen) |
| `2` | Live-Watch — Aufnahme alle 30s, `Ctrl+C` zurück |
| `3` | Web error-log `tail -f` |
| `4` | Auth-log `tail -f` |
| `5` | System-log `tail -f` |
| `w` | IP nachschlagen — GeoIP-Land + whois (echte CIDR zum Blocken) |
| `r` | Handlungs-Vorschläge (copy-paste-fertig) |
| `q` | Quit |

> In allen `tail -f`- und Watch-Ansichten bringt dich **`Ctrl+C` zurück ins
> Menü** — es killt nicht das Script.

---

## 4. Die Momentaufnahme lesen

Reihenfolge ist Absicht: erst *wo bin ich*, dann *brennt CPU oder Netz*, dann
*wer*.

### Kopfzeile
```
distro=debian  init=systemd  pkg=apt  web=nginx  db=mysql
deine SSH-IP: 1.2.3.4  (NICHT droppen)
```
Was die Umgebungs-Erkennung gefunden hat. Die SSH-IP-Warnung ist deine
Versicherung gegen Selbst-Aussperrung.

### LAST & WER FRISST CPU/RAM — **der wichtigste Split**
- Load 1/5/15 + die Top-Prozesse.
- **CPU bei `mysqld`/`php-fpm`** + normales Web-Volumen → **Eigenverschulden**
  (Plugin/Cron-Loop). *Nicht blocken* — Query/Plugin finden.
- **Load hoch, aber CPU idle** und stattdessen viele Verbindungen → Richtung Netz/Angriff.

> 💡 **Merke:** viele Requests ≠ viel Last. Ein Scanner, den nginx mit `444`
> wegwirft, macht 400 Requests und **0.02 Load**. Erst die Kombination
> entscheidet.

### WEB — Angriffs-Erkennung
- **Rate** (`req/s`): viel los oder normal?
- **Top-Talker IPs** mit `[Land]`: eine IP mit Riesen-Count = simpler Bot.
  Eigene IPs sind als `<= SELF (nicht droppen)` markiert.
- **Top /24-Netze**: Bots verteilen sich über Ranges — hier werden sie sichtbar,
  auch wenn einzelne IPs niedrig aussehen.
- **HTTP-Status-Mix**: viele `4xx/444` = Scanner/Block · viele `5xx` = App kippt.
- **Verdächtige Pfade**: `/.env`, `wp-login`, `xmlrpc`, `/.git` = Credential-/WP-Probing.
- **Top User-Agents**: ein einzelner UA mit hohem Count = Bot-Signatur.

### AUTH / SSH
Top-Quell-IPs fehlgeschlagener Logins + Gesamtzahl. Hohe Zahl → SSH-Brute →
sshguard/fail2ban (siehe Empfehlungen).

### DATENBANK
Live `SHOW FULL PROCESSLIST` (Sleep-Verbindungen ausgeblendet) — im Akutfall
aussagekräftiger als das Slow-Log, weil es zeigt, was **jetzt** läuft. Ohne
passwortlosen Zugang gibt's den fertigen `-u root -p`-Befehl.

---

## 5. Entscheiden & handeln (`r`)

`r` druckt copy-paste-fertige Befehle — **du** setzt sie in der zweiten bash ab:

0. **Downtime setzen** — Monitoring auf Wartung, damit nicht mitten in der
   Analyse Pager hochgehen.
1. **IP/Netz sperren** — gestaffelt: einzeln → `/24` → echte CIDR (via `w`).
   Inklusive *Block prüfen* und *Block wieder lösen*.
2. **Dienste** reload/restart — Befehle automatisch im richtigen Init-Format.
3. **Entscheidungshilfe** Angriff vs. Eigenverschulden in Worten.
4. **nginx-Snippet** gegen WP-Bot-Flut (`xmlrpc` deny + `wp-login` ratelimit).

### IP-Lookup (`w`) für den präzisen Block
```bash
./ninja.sh whois 185.12.59.118
```
Zeigt Land + Org + **echte CIDR** (`route`/`inetnum`, oft `/20`). Damit blockst
du den realen Netzbereich des Angreifers statt blind `/24` zu raten:
```bash
iptables -I INPUT -s <CIDR aus whois> -j DROP
```

---

## 6. Sicherheits-Eigenschaften (warum man dem Tool trauen kann)

- **Read-only.** Keine Sektion verändert irgendetwas am System.
- **Nie Auto-Ausführung.** Alle Aktionen sind Text, den du bewusst absetzt.
- **Self-Lockout-Schutz.** Eigene Server-IPs (v4/v6/Loopback) und die
  SSH-Client-IP werden erkannt und in den Talkern markiert — sie landen nicht
  versehentlich in einem `DROP`.
- **ANSI-Injection entschärft.** User-Agents/Pfade/Logzeilen sind
  angreiferkontrolliert; alle untrusted Felder werden vor der Ausgabe von
  Steuerzeichen befreit (`clean()`), damit ein präparierter Log-Eintrag nicht
  dein Terminal kapert.
- **`set -u`.** Tippfehler in Variablen brechen früh ab statt still ins Leere zu laufen.

---

## 7. Troubleshooting

| Symptom | Ursache / Fix |
|---|---|
| `Rate:`-Zeile fehlt | Logformat ohne `[dd/Mon/yyyy:HH:MM:SS]` → `_logepoch()`-Regex anpassen |
| Keine `[CC]`-Länder | `apt install geoip-bin geoip-database` (oder GeoLite2-.mmdb fehlt) |
| `web=none` | Kein Standard-Logpfad gefunden → Pfad in `probe()` (`for f in …`) ergänzen |
| DB-Sektion: „kein passwortloser Zugang" | normal ohne `unix_socket`-Auth → gezeigten `-u root -p`-Befehl nutzen |
| `init=unknown` / Service-Befehle stimmen nicht | exotisches Init → `svc_cmd()` erweitern |
| Farben als `\033[…]` in einer Datei | passiert nicht — Farben werden ohne TTY automatisch leer (sonst Bug melden) |

---

## 8. Anpassen

Alle Stellschrauben stehen oben im Script:

- `WATCH_INTERVAL=30` — Refresh-Takt im Watch-Modus.
- `TAIL_N=500` — wie viele Log-Zeilen analysiert werden.
- `probe()` — neue Logpfade/Distros hier eintragen.
- `snap_web()` → „Verdächtige Pfade" — eigene Probe-Muster ins `grep -aoiE` aufnehmen.

Der Code ist durchgehend kommentiert (*was/wo/wie/warum*) — fang bei `probe()`
an, dann liest sich der Rest von selbst.
