Initial: Mesh-Configs vom 03.09.2026 01:23
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
---
|
||||
name: security-audit-hardening
|
||||
description: "Linux-Server-Sicherheitsaudit mit automatischer Härtung – SSH, fail2ban, UFW, Angriffserkennung"
|
||||
version: 1.0.0
|
||||
author: Agent A (netcup VPS Deployer)
|
||||
platforms: [linux]
|
||||
---
|
||||
|
||||
# Security Audit & Hardening
|
||||
|
||||
Schnelles Sicherheitsaudit für Linux-VPS mit Sofortmaßnahmen gegen Bruteforce-Angriffe.
|
||||
|
||||
## Audit-Befehl (alles auf einmal)
|
||||
|
||||
```bash
|
||||
echo "=== OFFENE PORTS ===" && ss -tlnp | awk '{print $4,$NF}' | sort -u
|
||||
echo "=== UFW ===" && ufw status verbose
|
||||
echo "=== SSH ===" && grep -E "^PermitRootLogin|^PasswordAuthentication|^PubkeyAuthentication|^Port" /etc/ssh/sshd_config
|
||||
echo "=== FAIL2BAN ===" && systemctl status fail2ban 2>/dev/null | head -3
|
||||
echo "=== SSH-ANGRIFFE ===" && grep -c "Failed password" /var/log/auth.log 2>/dev/null
|
||||
echo "=== AKTIVE ANGREIFER ===" && grep "Failed password" /var/log/auth.log 2>/dev/null | tail -5 | awk '{print $NF}' | sort -u
|
||||
```
|
||||
|
||||
## Sofortmaßnahmen (in dieser Reihenfolge)
|
||||
|
||||
### 1. Aktuelle Angreifer blockieren
|
||||
|
||||
```bash
|
||||
# IPs aus Auth-Log extrahieren
|
||||
ufw deny from <IP>
|
||||
```
|
||||
|
||||
### 2. fail2ban installieren
|
||||
|
||||
```bash
|
||||
apt-get install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
```
|
||||
|
||||
### 3. fail2ban aggressiv konfigurieren
|
||||
|
||||
```bash
|
||||
cat > /etc/fail2ban/jail.local << 'EOF'
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 3
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = ssh
|
||||
maxretry = 3
|
||||
bantime = 7200
|
||||
EOF
|
||||
systemctl restart fail2ban
|
||||
```
|
||||
|
||||
### 4. SSH härten
|
||||
|
||||
```bash
|
||||
cat >> /etc/ssh/sshd_config << 'EOF'
|
||||
MaxAuthTries 3
|
||||
MaxSessions 2
|
||||
LoginGraceTime 30
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
EOF
|
||||
sshd -t && systemctl reload sshd
|
||||
```
|
||||
|
||||
> ⚠️ **Pitfall: Nicht aussperren!** Vor `PasswordAuthentication no` prüfen ob SSH-Keys existieren: `cat /root/.ssh/authorized_keys`. Wenn keine Keys → Passwort-Auth lassen, aber mit fail2ban + MaxAuthTries absichern.
|
||||
|
||||
> 🔥 **Pitfall: fail2ban bannt eigene Mesh-/Agent-IPs!** SSH/scp-Polling von Sub-Agenten aus (regelmäßige Key-Auth-Verbindungen zum Master) wird von fail2ban als „fehlgeschlagene Login-Versuche" gezählt und gebannt — besonders wenn eine once mal nicht matchen will. **Symptom:** „Connection refused" auf Port 22 NUR für genau eine IP, während Ping geht und andere Agenten noch funktionieren. **Fix — eigene/vertraute IPs dauerhaft whitelisten** in `/etc/fail2ban/jail.local`:
|
||||
```ini
|
||||
[sshd]
|
||||
ignoreip = 172.24.34.142 172.24.20.26 172.24.178.202 172.24.92.162
|
||||
```
|
||||
Dann `fail2ban-client reload` + `fail2ban-client set sshd unbanip <IP>`. WICHTIG: nur EINE `[sshd]`-Sektion pflegen (eine doppelte, appendierte Sektion gilt separat — konsolidieren).
|
||||
|
||||
### 5. Webhook-/Mesh-Schnittstellen auf bekannte ZeroTier-Geräte isolieren
|
||||
|
||||
**Prinzip (NUTZERWUNSCH 2026-08-03):** Kein Subnetz-/16-Catch-all. Mesh-Ports nur für
|
||||
explizit bekannte ZeroTier-IPs freigeben, damit ein unbekanntes Gerät im Netz keine Dienste
|
||||
einschleusen kann. Inkl. Handy (Gerät, kein Agent).
|
||||
|
||||
```bash
|
||||
# Bekannte ZT-Geräte einzeln freigeben (Ports: Webhook 8644, NFS 2049/111/20048, Ollama 11434)
|
||||
for ip in 172.24.34.142 172.24.20.26 172.24.178.202 172.24.92.162; do
|
||||
ufw allow from $ip to any port 8644 proto tcp comment 'Webhook Mesh'
|
||||
ufw allow from $ip to any port 2049 proto tcp comment 'NFS Mesh'
|
||||
ufw allow from $ip to any port 111 proto tcp comment 'rpcbind'
|
||||
ufw allow from $ip to any port 111 proto udp comment 'rpcbind U'
|
||||
ufw allow from $ip to any port 20048 proto tcp comment 'nfs lockd'
|
||||
ufw allow from $ip to any port 11434 proto tcp comment 'Ollama Mesh'
|
||||
done
|
||||
```
|
||||
|
||||
**Zuerst alte /16-Regeln löschen:** `ufw delete <nr>` fragt interaktiv → mit `yes |` erzwingen.
|
||||
**Verifikation:** `ufw status | grep '172.24.0.0/16'` darf NICHTS zeigen; `grep -oP '172\\.24\\.\\d+\\.\\d+' | sort -u` darf nur die bekannten IPs listen.
|
||||
|
||||
> 🔒 **NFS deaktivieren — SSH/scp für Agent-Tasks ist sicherer (2026-08-03).**
|
||||
> NFS (`sec=sys`) transferiert plaintext über TCP → Man-in-the-Middle-anfällig, und braucht
|
||||
> Kernelmodule/FUSE, die auf Tegra-Hosts (Nintendo-Switch: Kernel `4.9.140-l4t` → kein `nfs`/
|
||||
> kein `fuse`) fehlen. Entscheidung: **alle Sub-Agenten nutzen SSH/scp mit Key-Auth**
|
||||
> (Ende-zu-Ende verschlüsselt), NFS-Server deaktiviert und NFS-Ports (2049/111/20048) aus UFW
|
||||
> entfernt. Das schließt die NFS-Angriffsfläche komplett. Details zum SSH/scp-Polling:
|
||||
> siehe Skill `multi-agent-mesh` (Kontext: Task-Polling über SSH/scp).
|
||||
|
||||
> ⚠️ **Pitfall:** Nach VPN/Netz-Wechsel (WireGuard→ZeroTier) hängen UFW-Regeln oft noch an alten
|
||||
> Subnetzen (z.B. `10.8.0.0/24`). Immer prüfen, welches Netzwerk jetzt real aktiv ist.
|
||||
|
||||
### 6. Automatische Sicherheitsupdates
|
||||
|
||||
```bash
|
||||
apt-get install -y unattended-upgrades
|
||||
dpkg-reconfigure -f noninteractive unattended-upgrades
|
||||
```
|
||||
|
||||
## Audit-Ergebnis bewerten
|
||||
|
||||
| Signal | Risiko |
|
||||
|--------|--------|
|
||||
| SSH `PasswordAuthentication yes` | 🔴 Bruteforce möglich |
|
||||
| SSH `PermitRootLogin yes` | 🔴 Root direkt angreifbar |
|
||||
| Kein fail2ban | 🔴 Keine Ratenbegrenzung |
|
||||
| Webhook-Port öffentlich | 🟡 API für jeden erreichbar |
|
||||
| >100 Failed Passwords | 🔴 Aktiver Angriff |
|
||||
| UFW default deny | 🟢 korrekt |
|
||||
|
||||
## Verhalten bei aktivem Angriff
|
||||
|
||||
1. **Nicht in Panik verfallen** — SSH mit Passwort + fail2ban hält 99% der Angriffe ab
|
||||
2. Angreifer-IPs per `ufw deny` dauerhaft sperren
|
||||
3. fail2ban-Status prüfen: `fail2ban-client status sshd`
|
||||
4. Auth-Log monitoren: `tail -f /var/log/auth.log | grep Failed`
|
||||
|
||||
## JIT SSH-Portal (nächste Stufe: SSH schließen, 2FA-Freischaltung)
|
||||
|
||||
Wenn der Server-SSH nicht dauerhaft offen sein soll: baue ein **Just-In-Time-Portal** — Flask + Telegram-2FA +
|
||||
temporäre UFW-Freischaltung (1h Auto-Close). Details + Reset-Rezept: `references/jit-ssh-2fa-portal.md`.
|
||||
|
||||
**Kern-Pitfall:** Ein End-to-End-Test des Portals überschreibt das echte Passwort (PW-Wechsel-Pflicht) —
|
||||
nach dem Test IMMER zurücksetzen, nie Test-Artefakte im laufenden System hinterlassen.
|
||||
|
||||
## Niemals tun
|
||||
|
||||
- ❌ SSH-Port ändern (security by obscurity, bringt nichts)
|
||||
- ❌ `PermitRootLogin no` ohne funktionierenden SSH-Key
|
||||
- ❌ `PasswordAuthentication no` ohne SSH-Key für ALLE User
|
||||
- ❌ UFW deaktivieren "weil's nicht funktioniert"
|
||||
@@ -0,0 +1,68 @@
|
||||
# JIT SSH-Portal mit Telegram-2FA (Just-In-Time Firewall-Freischaltung)
|
||||
|
||||
> Muster (2026-08-03, `https://ssh.perfekt-ohne-en.de`): SSH-Port 22 bleibt standardmäßig für die Welt
|
||||
> GESCHLOSSEN und wird nur nach doppelter Authentifizierung (Passwort + Telegram-2FA) für die
|
||||
> angefragte IP temporär geöffnet (1h) → Auto-Close. Besser als permanent offenes SSH.
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
ssh.perfekt-ohne-en.de (nginx+SSL) → proxy_pass → 127.0.0.1:8091 (Flask)
|
||||
├─ SQLite (users, sessions)
|
||||
├─ Telegram-2FA (6-stelliger Code an Nutzer-Chat)
|
||||
└─ UFW: öffnet Port 22 für angefragte IP (1h)
|
||||
```
|
||||
|
||||
**Ablauf (end-to-end):**
|
||||
1. Login `mehlzoerwer` + **Erstpasswort** `St230918kM@vps` → gilt NUR 1×, dann **PW-Wechsel-Pflicht**
|
||||
2. 2. Login mit neuem Passwort → liest `pw_changed`-Flag → leitet zur **2FA**-Seite
|
||||
3. Backend sendet **6-stelligen Code per Telegram** an Nutzer-Chat (out-of-band, Kanal getrennt vom Web)
|
||||
4. Nach korrektem Code: `ufw allow from <ANFRAGE-IP> to any port 22 proto tcp` (Kommentar "SSH JIT Portal")
|
||||
5. **Auto-Close:** Hintergrund-Thread prüft alle 60s Sessions mit `expiry <= now` → `ufw delete`, Status `closed`
|
||||
|
||||
## Dateien & Infrastruktur
|
||||
|
||||
- App: `/opt/sshportal/app.py` (Flask, Telegram-Token aus `/root/.hermes/.env`, einheitlich via `re.search(key+r"\s*=\s*(\S+)")`)
|
||||
- venv: `/opt/sshportal/venv` (flask, requests; Passwort-Hash via `werkzeug.security.generate_password_hash/check_password_hash` — kein eigener bcrypt-Aufruf nötig)
|
||||
- systemd: `/etc/systemd/system/sshportal.service` → `active` auf `127.0.0.1:8091` (nur localhost; nginx davor)
|
||||
- DB: `/opt/sshportal/portal.db`, Log: `/opt/sshportal/portal.log`
|
||||
- nginx vhost + Let's-Encrypt via Certbot (Standard `certbot --nginx -d ... --redirect`)
|
||||
|
||||
## Sicherheits-Parameter (der Wert des Musters)
|
||||
|
||||
- **Erstpasswort-Einmalnutzung:** `pw_changed=0` → Login erzwingt PW-Wechsel.
|
||||
- **2FA out-of-band:** Telegram = getrennter Kanal vom Web-Portal.
|
||||
- **Code-TTL 120s**, max 3 Fehlversuche → Login gesperrt + Telegram-Alarm.
|
||||
- **Firewall-Freischaltung zeitlich begrenzt:** 1h, danach automatische UFW-Löschung.
|
||||
- **Nur die angefragte IP** wird geöffnet (keine Subnetze).
|
||||
- Flask-Inhalte: `render_template_string(..., content=Markup(...))` damit HTML-Strings als Form nicht escaped werden.
|
||||
|
||||
## ⚠️ KRITISCHER PITFALL: Test verändert den laufenden Zustand
|
||||
|
||||
End-to-End-Test (Login mit Erstpasswort → PW-Wechsel auf Test-Passwort) **überschreibt das echte Passwort**.
|
||||
Der Nutzer kann danach nicht mehr mit dem Erstpasswort einloggen → fälschliche "falsches Passwort"-Meldung.
|
||||
|
||||
**Regel (Betriebsregel #1):** Tests/Änderungen an laufenden Systemen MÜSSEN beendet oder zurückgesetzt
|
||||
werden — nie Test-Artefakte hinterlassen. Sonst Rückfrage statt Test.
|
||||
|
||||
**Reset nach Test:**
|
||||
```bash
|
||||
systemctl stop sshportal.service
|
||||
/opt/sshportal/venv/bin/python -c "
|
||||
import sqlite3; from werkzeug.security import generate_password_hash
|
||||
c=sqlite3.connect('/opt/sshportal/portal.db')
|
||||
c.execute('UPDATE users SET pw_hash=?, pw_changed=0 WHERE username=?', (generate_password_hash('St230918kM@vps'),'mehlzoerwer'))
|
||||
c.commit(); c.close()"
|
||||
systemctl start sshportal.service
|
||||
```
|
||||
|
||||
## Verifikation (im Bau bestätigt)
|
||||
|
||||
| Test | Status |
|
||||
|------|--------|
|
||||
| Login Erstpasswort → Redirect change-pw | ✅ 302 |
|
||||
| PW-Wechsel → "Passwort geändert" (Log) | ✅ |
|
||||
| Login neues PW → Redirect 2fa | ✅ 302 |
|
||||
| 2FA-Code → Telegram-Chat | ✅ Log "2FA-Code gesendet" |
|
||||
| UFW allow für IP | ✅ "Rule added" |
|
||||
| Auto-Close (ufw delete nach 1h) | ✅ "Rule deleted" |
|
||||
Reference in New Issue
Block a user