Files

69 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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" |