Initial: Mesh-Configs vom 03.09.2026 01:23
This commit is contained in:
@@ -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