From 0d5b87f1638c9e4dacafce4f85eef9030ac5e5e6 Mon Sep 17 00:00:00 2001
From: Till Tomczak
Date: Sun, 25 May 2025 20:59:13 +0200
Subject: [PATCH] feat: Implement SSL support and kiosk mode enhancements
- Added SSL configuration to the backend, including self-signed certificate generation and management.
- Updated `setup_myp.sh` to create SSL certificates during installation.
- Enhanced `app.py` to support SSL context for secure communication.
- Introduced a new SSL management menu in the setup script for easier certificate handling.
- Updated frontend API calls to use HTTPS for secure data transmission.
- Implemented kiosk mode features, including automatic browser launch with SSL support.
- Improved documentation in `SUMMARY.md` to reflect new features and network topology changes.
---
SUMMARY.md | 85 ++++++++++-
backend/app/app.py | 96 +++++++++----
backend/app/config/settings.py | 50 ++++++-
backend/app/static/js/sw.js | 2 +-
backend/install/create_ssl_cert.sh | 160 +++++++++++++++++++++
backend/install/kiosk.sh | 9 +-
backend/install/ssl_check.sh | 99 +++++++++++++
backend/setup_myp.sh | 140 ++++++++++++++----
frontend/next.config.mjs | 4 +-
frontend/src/app/layout.tsx | 2 +
frontend/src/components/ui/ssl-warning.tsx | 86 +++++++++++
frontend/src/utils/api-config.ts | 23 ++-
frontend/src/utils/api-helper.ts | 75 ++++++++++
frontend/src/utils/external-api.ts | 127 +++++++---------
14 files changed, 812 insertions(+), 146 deletions(-)
create mode 100755 backend/install/create_ssl_cert.sh
create mode 100755 backend/install/ssl_check.sh
create mode 100644 frontend/src/components/ui/ssl-warning.tsx
create mode 100644 frontend/src/utils/api-helper.ts
diff --git a/SUMMARY.md b/SUMMARY.md
index 93085aed..ecf494cb 100644
--- a/SUMMARY.md
+++ b/SUMMARY.md
@@ -10,6 +10,8 @@ Die MYP-Plattform (My Young Printer) ist ein Verwaltungssystem für 3D-Drucker m
- Offline-Fähigkeit durch Service Worker Integration
- REST-API für Frontend und externe Dienste
- Automatisierte Auftrags-Planung und -Ausführung via Scheduler
+- Verschlüsselte Kommunikation über HTTPS mit selbstsignierten Zertifikaten
+- Kiosk-Modus für Informationsdisplays und öffentliche Terminals
Das Backend basiert auf Flask (Python) und nutzt eine SQLite-Datenbank für die Datenspeicherung.
@@ -24,22 +26,40 @@ Die MYP-Plattform setzt auf eine verteilte Netzwerk-Architektur:
- **3D-Drucker**: Werden als Netzwerkgeräte mit festen IP-Adressen konfiguriert
- **Frontend**: Als Web-App mit Offline-Funktionalität konzipiert
- **PWA-Funktionalität**: Service Worker für Offline-Betrieb und Cache-Management
+- **Kiosk-Displays**: Raspberry Pi mit Chrome im Kiosk-Modus
### Netzwerk-Topologie
```
-[Frontend/PWA] <-- HTTP/REST --> [Flask-Backend] <-- PyP110-Lib --> [Smart-Plugs]
- ^ ^ |
- | | v
- +------ PWA Offline Cache -------+ [3D-Drucker]
+ ┌──────────────────┐
+ │ Command Center │
+ │ (192.168.0.1) │
+ └──────────┬───────┘
+ │
+ ▼
+┌───────────────┐ ┌─────────────────────┐ ┌──────────────┐
+│ Frontend/PWA │◄────►│ Flask-Backend │◄────►│ Smart-Plugs │
+│(192.168.0.105)│ │ (192.168.0.105) │ │(192.168.0.1xx)│
+└───────┬───────┘ └─────────┬───────────┘ └──────┬────────┘
+ │ │ │
+ │ │ ▼
+┌───────▼───────┐ │ ┌────────────────┐
+│ Kiosk-Mode │ │ │ 3D-Drucker │
+│ (10.0.0.1-10) │ │ │(192.168.0.1xx) │
+└───────────────┘ ▼ └────────────────┘
+ ┌─────────────────────┐
+ │ Ad-hoc Pi-Netzwerk │
+ │ (10.0.0.x/24) │
+ └─────────────────────┘
```
### Kommunikationsprotokolle
-- **HTTP/REST**: Zwischen Frontend und Backend
+- **HTTPS/REST**: Zwischen Frontend und Backend (Port 5000)
- **JSON**: Standardformat für Datenaustausch
- **Tapo-Protokoll**: Für Smart-Plug-Steuerung via PyP110-Bibliothek
- **SQLite**: Lokale Datenbankanbindung
+- **TLS 1.2/1.3**: Verschlüsselte Kommunikation mit selbstsignierten Zertifikaten
### Vernetzungs-Features
@@ -47,6 +67,8 @@ Die MYP-Plattform setzt auf eine verteilte Netzwerk-Architektur:
- SmartPlug-Integration für Fernsteuerung der Stromversorgung
- Offline-Betriebsmodus mit synchronisierenden Service Workern
- Automatische Erkennung der Drucker-Status via Netzwerk-Polling
+- Kiosk-Modus mit automatischem Start nach Systemneustart
+- Command Center für zentrale Verwaltung aller Komponenten
## 3. Hauptkomponenten des Backends
@@ -64,6 +86,7 @@ Das Backend stellt eine umfassende REST-API bereit:
- **Printer Management**: Druckerstatus, Steuerung
- **Job Management**: Auftragsplanung, -verwaltung und -überwachung
- **Stats API**: Statistiken und Auswertungen
+- **Kiosk API**: Steuerung der Kiosk-Displays
### SmartPlug-Integration
@@ -71,6 +94,13 @@ Das Backend stellt eine umfassende REST-API bereit:
- Automatisierte Steuerung der Stromversorgung basierend auf Jobplanung
- Status-Monitoring und Fehlerbehandlung
+### SSL/HTTPS-Implementierung
+
+- Selbstsignierte Zertifikate für verschlüsselte Kommunikation
+- Automatische Zertifikatsgenerierung während der Installation
+- Konfigurierbare Zertifikatslaufzeit (Standard: 10 Jahre)
+- Prüfwerkzeuge für Zertifikatsstatus und -gültigkeit
+
## 4. Datenbankmodell & Scheduler-Logik
### Datenbankmodelle
@@ -111,6 +141,9 @@ Der BackgroundTaskScheduler bietet:
| /api/scheduler/status | GET | Scheduler-Status | Ja |
| /api/scheduler/start | POST | Scheduler starten | Ja, Admin |
| /api/scheduler/stop | POST | Scheduler stoppen | Ja, Admin |
+| /api/kiosk/status | GET | Kiosk-Status | Ja |
+| /api/kiosk/activate | POST | Kiosk aktivieren | Ja, Admin |
+| /api/kiosk/deactivate | POST | Kiosk deaktivieren | Ja, Admin |
| /auth/login | POST | Anmelden | Nein |
| /auth/logout | GET/POST | Abmelden | Ja |
@@ -125,6 +158,7 @@ Der BackgroundTaskScheduler bietet:
### Netzwerksicherheit
+- HTTPS mit selbstsignierten TLS-Zertifikaten
- CORS-Konfiguration für sichere Cross-Origin-Requests
- Sicherheitsheader im Response (X-Content-Type-Options, X-Frame-Options)
- Keine sensiblen Daten in URLs oder Query-Parametern
@@ -137,17 +171,57 @@ Der BackgroundTaskScheduler bietet:
## 7. Build- & Deployment-Ablauf
+### Installationsprozess
+
+Die Installation der MYP-Plattform erfolgt über verschiedene Shell-Skripte:
+
+- **setup_myp.sh**: Hauptinstallationsskript (Command Center)
+ - Standardinstallation mit HTTPS-Unterstützung
+ - Kiosk-Modus-Installation (gehärtete Variante)
+ - Netzwerk- und DNS-Konfiguration
+ - Systemüberwachung und Logging
+
+- **create_ssl_cert.sh**: Generiert selbstsignierte Zertifikate
+ - Unterstützt Multiple-SAN-Entries (Hostname, IP-Adressen)
+ - Konfigurierbare Zertifikatslaufzeit
+ - Automatische Fehlerbehandlung
+
+- **ssl_check.sh**: Prüft SSL-Zertifikatsstatus
+ - Validiert Gültigkeitsdauer
+ - Zeigt Fingerprint und Subject-Details
+ - Warnt vor bald ablaufenden Zertifikaten
+
### Backend-Deployment
- Python 3.11 venv-Umgebung
- Konfigurierbare Entwicklungs- und Produktionsumgebungen
- Log-Rotation und strukturierte Logging-Hierarchie
+- SSL/TLS-Unterstützung mit automatischer Zertifikatsgenerierung
+
+### Kiosk-Modus
+
+- Basiert auf Raspberry Pi mit Chromium Browser
+- Automatischer Start im Vollbildmodus nach Boot
+- Deaktivierung von Fehlerdialogen und Warnungen
+- Integrierte Überwachung und Watchdog-Funktionalität
+- Spezielle Konfiguration für öffentliche Informationsdisplays
+- Ignoriert SSL-Zertifikatswarnungen für selbstsignierte Zertifikate
+
+### Command Center
+
+- Zentrales Verwaltungstool mit CLI-Interface
+- Integrierte Systemdiagnose und -überwachung
+- Einfache Verwaltung von SSL-Zertifikaten
+- Fernsteuerung von Kiosk-Displays
+- Netzwerk- und IP-Konfiguration
+- Dienst-Management (Start/Stop/Neustart)
### Frontend-Integration
- Tailwind CSS für responsive UI
- CLI-Befehle für Tailwind-Kompilierung
- Service Worker für PWA-Funktionalität
+- HTTPS-Unterstützung mit Proxy-Konfiguration
### Systemd-Integration
@@ -169,6 +243,7 @@ Der BackgroundTaskScheduler bietet:
- OAuth2-Integration für externe Authentifizierungsquellen
- Zwei-Faktor-Authentifizierung
- Zertifikatsbasierte Geräteauthentifizierung für SmartPlugs
+- Letsencrypt-Integration für vertrauenswürdige Zertifikate
### Skalierbarkeit
diff --git a/backend/app/app.py b/backend/app/app.py
index def51193..1d811075 100644
--- a/backend/app/app.py
+++ b/backend/app/app.py
@@ -18,7 +18,7 @@ from flask_wtf.csrf import CSRFProtect
from config.settings import (
SECRET_KEY, TAPO_USERNAME, TAPO_PASSWORD, PRINTERS,
FLASK_HOST, FLASK_PORT, FLASK_DEBUG, SESSION_LIFETIME,
- SCHEDULER_INTERVAL, SCHEDULER_ENABLED
+ SCHEDULER_INTERVAL, SCHEDULER_ENABLED, get_ssl_context
)
from utils.logging_config import setup_logging, get_logger, log_startup_info
from models import User, Printer, Job, Stats, get_db_session, init_database, create_initial_admin
@@ -1265,16 +1265,19 @@ def tailwind_watch():
# Auto-Kompilierung beim Serverstart im Debug-Modus
def compile_tailwind_if_debug():
- if app.debug:
+ """Kompiliert Tailwind CSS im Debug-Modus, falls notwendig."""
+ if FLASK_DEBUG:
try:
- subprocess.run(["npx", "tailwindcss", "-i", "./static/css/input.css",
- "-o", "./static/css/tailwind-dark-consolidated.min.css"],
- check=True, capture_output=True)
- print("Tailwind CSS für Debug-Modus kompiliert.")
- except subprocess.CalledProcessError as e:
- print(f"Warnung: Konnte Tailwind CSS nicht kompilieren: {e}")
- except FileNotFoundError:
- print("Warnung: Node.js/npm nicht gefunden. Tailwind CSS wurde nicht kompiliert.")
+ app_logger.info("Kompiliere Tailwind CSS...")
+ subprocess.run([
+ "npx", "tailwindcss", "-i", "static/css/input.css",
+ "-o", "static/css/tailwind.min.css", "--minify"
+ ], check=True)
+ app_logger.info("Tailwind CSS erfolgreich kompiliert.")
+ except subprocess.CalledProcessError:
+ app_logger.warning("Tailwind konnte nicht kompiliert werden. Möglicherweise ist npx/Node.js nicht installiert.")
+ except Exception as e:
+ app_logger.error(f"Fehler beim Kompilieren von Tailwind CSS: {str(e)}")
# Tailwind CSS kompilieren, wenn im Debug-Modus
if FLASK_DEBUG:
@@ -1282,34 +1285,65 @@ if FLASK_DEBUG:
# Initialisierung der Datenbank beim Start
def init_app():
+ """Initialisiert die App-Komponenten und startet den Scheduler."""
+ # Datenbank initialisieren
try:
- # Datenbank initialisieren
init_database()
- # Admin-Benutzer erstellen oder zurücksetzen
create_initial_admin()
-
- # Template-Helper registrieren
- register_template_helpers(app)
- app_logger.info("Template-Helper registriert")
-
- # Scheduler starten, wenn aktiviert
- if SCHEDULER_ENABLED:
- scheduler.start()
- app_logger.info("Job-Scheduler gestartet")
except Exception as e:
- app_logger.error(f"Fehler bei der Initialisierung: {str(e)}")
+ app_logger.error(f"Fehler bei der Datenbank-Initialisierung: {str(e)}")
+
+ # Jinja2-Helfer registrieren
+ register_template_helpers(app)
+
+ # Tailwind im Debug-Modus kompilieren
+ compile_tailwind_if_debug()
+
+ # Scheduler starten, wenn aktiviert
+ if SCHEDULER_ENABLED:
+ try:
+ # Scheduler-Task für Druckauftrags-Prüfung registrieren
+ scheduler.register_task(
+ "check_jobs",
+ check_jobs,
+ interval=SCHEDULER_INTERVAL
+ )
+
+ # Scheduler starten
+ scheduler.start()
+ app_logger.info(f"Scheduler gestartet mit Intervall {SCHEDULER_INTERVAL} Sekunden.")
+ except Exception as e:
+ app_logger.error(f"Fehler beim Starten des Schedulers: {str(e)}")
+
+ # SSL-Kontext protokollieren
+ ssl_context = get_ssl_context()
+ if ssl_context:
+ app_logger.info(f"SSL aktiviert mit Zertifikat {ssl_context[0]}")
+ else:
+ app_logger.warning("SSL ist deaktiviert. Die Verbindung ist unverschlüsselt!")
# App starten
if __name__ == "__main__":
- # Initialisierung ausführen
- init_app()
-
- # Flask-Server starten
- app.run(
- host=FLASK_HOST,
- port=FLASK_PORT,
- debug=FLASK_DEBUG
- )
+ try:
+ # App initialisieren
+ init_app()
+
+ # SSL-Kontext ermitteln
+ ssl_context = get_ssl_context()
+
+ # Konsolen-Ausgabe für HTTPS
+ protocol = "HTTPS" if ssl_context else "HTTP"
+ app_logger.info(f"MYP startet auf {protocol}://{FLASK_HOST}:{FLASK_PORT} (Debug: {FLASK_DEBUG})")
+
+ # App starten
+ app.run(
+ host=FLASK_HOST,
+ port=FLASK_PORT,
+ debug=FLASK_DEBUG,
+ ssl_context=ssl_context
+ )
+ except Exception as e:
+ app_logger.critical(f"Kritischer Fehler beim Starten der Anwendung: {str(e)}")
# Content Security Policy anpassen
@app.after_request
diff --git a/backend/app/config/settings.py b/backend/app/config/settings.py
index beb65ef1..f4571615 100644
--- a/backend/app/config/settings.py
+++ b/backend/app/config/settings.py
@@ -31,6 +31,11 @@ FLASK_PORT = 5000
FLASK_DEBUG = True
SESSION_LIFETIME = timedelta(days=7)
+# SSL-Konfiguration
+SSL_ENABLED = True
+SSL_CERT_PATH = "/opt/myp/ssl/myp.crt"
+SSL_KEY_PATH = "/opt/myp/ssl/myp.key"
+
# Scheduler-Konfiguration
SCHEDULER_INTERVAL = 60 # Sekunden
SCHEDULER_ENABLED = True
@@ -63,4 +68,47 @@ def ensure_database_directory():
"""Erstellt das Datenbank-Verzeichnis."""
db_dir = os.path.dirname(DATABASE_PATH)
if db_dir:
- os.makedirs(db_dir, exist_ok=True)
\ No newline at end of file
+ os.makedirs(db_dir, exist_ok=True)
+
+def ensure_ssl_directory():
+ """Erstellt das SSL-Verzeichnis, falls es nicht existiert."""
+ ssl_dir = os.path.dirname(SSL_CERT_PATH)
+ if ssl_dir and not os.path.exists(ssl_dir):
+ os.makedirs(ssl_dir, exist_ok=True)
+
+def get_ssl_context():
+ """
+ Gibt den SSL-Kontext für Flask zurück, wenn SSL aktiviert ist.
+
+ Returns:
+ tuple oder None: Tuple mit Zertifikat- und Schlüsselpfad, wenn SSL aktiviert ist, sonst None
+ """
+ if not SSL_ENABLED:
+ return None
+
+ # Wenn Zertifikate nicht existieren, diese automatisch erstellen
+ if not os.path.exists(SSL_CERT_PATH) or not os.path.exists(SSL_KEY_PATH):
+ ensure_ssl_directory()
+
+ # Prüfen, ob wir uns im Entwicklungsmodus befinden
+ if FLASK_DEBUG:
+ print("SSL-Zertifikate nicht gefunden. Erstelle selbstsignierte Zertifikate...")
+
+ # Pfad zum create_ssl_cert.sh-Skript ermitteln
+ script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
+ "install", "create_ssl_cert.sh")
+
+ # Ausführungsrechte setzen
+ if os.path.exists(script_path):
+ os.system(f"chmod +x {script_path}")
+
+ # Zertifikate erstellen
+ os.system(f"{script_path} -c {SSL_CERT_PATH} -k {SSL_KEY_PATH}")
+ else:
+ print(f"WARNUNG: SSL-Zertifikat-Generator nicht gefunden: {script_path}")
+ return None
+ else:
+ print("WARNUNG: SSL-Zertifikate nicht gefunden und Nicht-Debug-Modus. SSL wird deaktiviert.")
+ return None
+
+ return (SSL_CERT_PATH, SSL_KEY_PATH)
\ No newline at end of file
diff --git a/backend/app/static/js/sw.js b/backend/app/static/js/sw.js
index 57692ea3..e95026a9 100644
--- a/backend/app/static/js/sw.js
+++ b/backend/app/static/js/sw.js
@@ -87,7 +87,7 @@ self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
- // Skip non-GET requests and unsupported schemes for caching
+ // Unterstütze sowohl HTTP als auch HTTPS
if (request.method !== 'GET' ||
(url.protocol !== 'http:' && url.protocol !== 'https:')) {
return;
diff --git a/backend/install/create_ssl_cert.sh b/backend/install/create_ssl_cert.sh
new file mode 100755
index 00000000..a30e8b85
--- /dev/null
+++ b/backend/install/create_ssl_cert.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+# MYP V2 - SSL-Zertifikat-Generator
+# Erstellt selbstsignierte Zertifikate für die HTTPS-Kommunikation
+
+# Fehlerabbruch aktivieren
+set -e
+
+# Farben für bessere Lesbarkeit
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Funktion für Titel
+print_header() {
+ echo -e "${BLUE}================================================================${NC}"
+ echo -e "${BLUE} MYP V2 - SSL-Zertifikat-Generator ${NC}"
+ echo -e "${BLUE}================================================================${NC}"
+ echo ""
+}
+
+# Standardwerte
+CERT_DIR="/opt/myp/ssl"
+CERT_FILE="$CERT_DIR/myp.crt"
+KEY_FILE="$CERT_DIR/myp.key"
+DAYS_VALID=3650 # 10 Jahre
+HOSTNAME=$(hostname -f)
+IP_ADDRESS=$(hostname -I | awk '{print $1}')
+
+# Hilfe-Funktion
+show_help() {
+ echo "Verwendung: $0 [Optionen]"
+ echo ""
+ echo "Optionen:"
+ echo " -d, --dir DIR Verzeichnis für Zertifikate (Standard: $CERT_DIR)"
+ echo " -c, --cert DATEI Pfad zur Zertifikatsdatei (Standard: $CERT_FILE)"
+ echo " -k, --key DATEI Pfad zur Schlüsseldatei (Standard: $KEY_FILE)"
+ echo " -h, --hostname NAME Hostname für das Zertifikat (Standard: $HOSTNAME)"
+ echo " -i, --ip IP IP-Adresse für das Zertifikat (Standard: $IP_ADDRESS)"
+ echo " -v, --valid TAGE Gültigkeitsdauer in Tagen (Standard: $DAYS_VALID)"
+ echo " --help Diese Hilfe anzeigen"
+ echo ""
+}
+
+# Argumente verarbeiten
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ -d|--dir)
+ CERT_DIR="$2"
+ shift 2
+ ;;
+ -c|--cert)
+ CERT_FILE="$2"
+ shift 2
+ ;;
+ -k|--key)
+ KEY_FILE="$2"
+ shift 2
+ ;;
+ -h|--hostname)
+ HOSTNAME="$2"
+ shift 2
+ ;;
+ -i|--ip)
+ IP_ADDRESS="$2"
+ shift 2
+ ;;
+ -v|--valid)
+ DAYS_VALID="$2"
+ shift 2
+ ;;
+ --help)
+ show_help
+ exit 0
+ ;;
+ *)
+ echo -e "${RED}Unbekannte Option: $1${NC}"
+ show_help
+ exit 1
+ ;;
+ esac
+done
+
+# Header anzeigen
+print_header
+
+# Verzeichnis erstellen, falls es nicht existiert
+if [ ! -d "$CERT_DIR" ]; then
+ echo -e "${YELLOW}Erstelle Verzeichnis $CERT_DIR...${NC}"
+ mkdir -p "$CERT_DIR"
+fi
+
+# Überprüfen, ob openssl installiert ist
+if ! command -v openssl &> /dev/null; then
+ echo -e "${RED}OpenSSL ist nicht installiert!${NC}"
+ echo -e "${YELLOW}Installiere OpenSSL...${NC}"
+ apt-get update && apt-get install -y openssl
+fi
+
+# Zertifikat erstellen
+echo -e "${GREEN}Erstelle selbstsigniertes SSL-Zertifikat...${NC}"
+echo -e "${BLUE}Hostname: ${NC}$HOSTNAME"
+echo -e "${BLUE}IP-Adresse: ${NC}$IP_ADDRESS"
+echo -e "${BLUE}Gültigkeitsdauer: ${NC}$DAYS_VALID Tage"
+echo -e "${BLUE}Zertifikatsdatei: ${NC}$CERT_FILE"
+echo -e "${BLUE}Schlüsseldatei: ${NC}$KEY_FILE"
+echo ""
+
+# OpenSSL-Konfiguration erstellen
+CONFIG_FILE="$CERT_DIR/openssl.cnf"
+cat > "$CONFIG_FILE" << EOF
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+distinguished_name = req_distinguished_name
+x509_extensions = v3_req
+
+[req_distinguished_name]
+C = DE
+ST = Baden-Wuerttemberg
+L = Stuttgart
+O = Mercedes-Benz AG
+OU = MYP Platform
+CN = $HOSTNAME
+
+[v3_req]
+keyUsage = critical, digitalSignature, keyAgreement
+extendedKeyUsage = serverAuth
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = $HOSTNAME
+DNS.2 = localhost
+IP.1 = $IP_ADDRESS
+IP.2 = 127.0.0.1
+EOF
+
+# Schlüssel und Zertifikat generieren
+openssl req -x509 -nodes -days "$DAYS_VALID" -newkey rsa:2048 \
+ -keyout "$KEY_FILE" -out "$CERT_FILE" \
+ -config "$CONFIG_FILE"
+
+# Berechtigungen setzen
+chmod 600 "$KEY_FILE"
+chmod 644 "$CERT_FILE"
+
+echo ""
+echo -e "${GREEN}SSL-Zertifikat erfolgreich erstellt!${NC}"
+echo -e "${YELLOW}Fingerprint:${NC}"
+openssl x509 -noout -fingerprint -sha256 -in "$CERT_FILE"
+echo ""
+echo -e "${BLUE}Um diese Zertifikate mit Flask zu verwenden:${NC}"
+echo " 1. Importiere die SSL-Einstellungen in der app.py"
+echo " 2. Starte Flask mit SSL-Unterstützung"
+echo ""
+echo -e "${YELLOW}Beispiel:${NC}"
+echo " app.run(host='0.0.0.0', port=5000, ssl_context=('$CERT_FILE', '$KEY_FILE'))"
+echo ""
\ No newline at end of file
diff --git a/backend/install/kiosk.sh b/backend/install/kiosk.sh
index 96baf297..c82a7ebf 100755
--- a/backend/install/kiosk.sh
+++ b/backend/install/kiosk.sh
@@ -14,6 +14,11 @@ sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' \
sed -i 's/"exit_type":"Crashed"/"exit_type":"Normal"/' \
"$HOME/.config/chromium/Default/Preferences" 2>/dev/null || true
-# Browser starten
+# Hostname und IP ermitteln
+HOSTNAME=$(hostname -f)
+IP_ADDRESS=$(hostname -I | awk '{print $1}')
+
+# Browser starten mit SSL-Warnung deaktiviert
chromium-browser --kiosk --noerrdialogs --disable-infobars \
- --window-position=0,0 --app=http://localhost:5000/ &
\ No newline at end of file
+ --window-position=0,0 --ignore-certificate-errors \
+ --app=https://${IP_ADDRESS}:5000/ &
\ No newline at end of file
diff --git a/backend/install/ssl_check.sh b/backend/install/ssl_check.sh
new file mode 100755
index 00000000..8f7a21b2
--- /dev/null
+++ b/backend/install/ssl_check.sh
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+# MYP SSL-Zertifikat-Prüfskript
+# Prüft den Status der SSL-Zertifikate und gibt Informationen aus
+
+# Fehlerabbruch aktivieren
+set -e
+
+# Farben für bessere Lesbarkeit
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Standardwerte
+CERT_PATH="/opt/myp/ssl/myp.crt"
+KEY_PATH="/opt/myp/ssl/myp.key"
+
+# Argumente verarbeiten
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ -c|--cert)
+ CERT_PATH="$2"
+ shift 2
+ ;;
+ -k|--key)
+ KEY_PATH="$2"
+ shift 2
+ ;;
+ *)
+ echo -e "${RED}Unbekannte Option: $1${NC}"
+ exit 1
+ ;;
+ esac
+done
+
+# Header anzeigen
+echo -e "${BLUE}================================================================${NC}"
+echo -e "${BLUE} MYP V2 - SSL-Zertifikat-Statusprüfung ${NC}"
+echo -e "${BLUE}================================================================${NC}"
+echo ""
+
+# Prüfen, ob OpenSSL installiert ist
+if ! command -v openssl &> /dev/null; then
+ echo -e "${RED}OpenSSL ist nicht installiert!${NC}"
+ exit 1
+fi
+
+# Prüfen, ob Zertifikat existiert
+if [ ! -f "$CERT_PATH" ]; then
+ echo -e "${RED}Zertifikat nicht gefunden: $CERT_PATH${NC}"
+ echo -e "${YELLOW}Führen Sie 'create_ssl_cert.sh' aus, um ein neues Zertifikat zu erstellen.${NC}"
+ exit 1
+fi
+
+# Prüfen, ob Schlüssel existiert
+if [ ! -f "$KEY_PATH" ]; then
+ echo -e "${RED}Schlüssel nicht gefunden: $KEY_PATH${NC}"
+ echo -e "${YELLOW}Führen Sie 'create_ssl_cert.sh' aus, um einen neuen Schlüssel zu erstellen.${NC}"
+ exit 1
+fi
+
+# Zertifikatsinformationen anzeigen
+echo -e "${GREEN}Zertifikatsinformationen:${NC}"
+echo -e "${BLUE}Zertifikatsdatei: ${NC}$CERT_PATH"
+echo -e "${BLUE}Schlüsseldatei: ${NC}$KEY_PATH"
+echo ""
+
+# Zertifikatsdetails anzeigen
+echo -e "${YELLOW}Zertifikatsdetails:${NC}"
+openssl x509 -in "$CERT_PATH" -noout -subject -issuer -dates -fingerprint -sha256
+
+# Gültigkeit prüfen
+echo ""
+echo -e "${YELLOW}Gültigkeitsprüfung:${NC}"
+not_after=$(openssl x509 -in "$CERT_PATH" -noout -enddate | cut -d= -f2)
+not_after_seconds=$(date -d "$not_after" +%s)
+now_seconds=$(date +%s)
+days_left=$(( (not_after_seconds - now_seconds) / 86400 ))
+
+if [ $days_left -le 0 ]; then
+ echo -e "${RED}Zertifikat ist ABGELAUFEN!${NC}"
+elif [ $days_left -le 30 ]; then
+ echo -e "${YELLOW}Zertifikat läuft in $days_left Tagen ab!${NC}"
+else
+ echo -e "${GREEN}Zertifikat ist noch $days_left Tage gültig.${NC}"
+fi
+
+# Zertifikatsinhalte prüfen
+echo ""
+echo -e "${YELLOW}Zertifikatsinhalte:${NC}"
+echo -e "${BLUE}Alternative Namen (SAN):${NC}"
+openssl x509 -in "$CERT_PATH" -noout -text | grep -A1 "Subject Alternative Name"
+
+# Abschluss
+echo ""
+echo -e "${GREEN}SSL-Prüfung abgeschlossen.${NC}"
+echo -e "${BLUE}Um die Zertifikate zu erneuern, führen Sie 'create_ssl_cert.sh' aus.${NC}"
+echo ""
\ No newline at end of file
diff --git a/backend/setup_myp.sh b/backend/setup_myp.sh
index d5e9976e..4c193723 100755
--- a/backend/setup_myp.sh
+++ b/backend/setup_myp.sh
@@ -61,6 +61,7 @@ show_main_menu() {
echo "7) MYP-Dienst starten/stoppen/neustarten"
echo "8) Logs anzeigen"
echo "9) Dokumentation anzeigen"
+ echo "10) SSL-Zertifikat-Management"
echo ""
echo "q) Beenden"
echo ""
@@ -98,6 +99,9 @@ process_main_menu() {
9)
show_documentation
;;
+ 10)
+ show_ssl_management
+ ;;
q|Q)
echo -e "${GREEN}Auf Wiedersehen!${NC}"
exit 0
@@ -131,7 +135,7 @@ standard_installation() {
echo "Installiere System-Abhängigkeiten..."
apt update
apt install -y python3.11 python3.11-pip python3.11-venv python3.11-dev \
- build-essential git curl
+ build-essential git curl openssl
# Verzeichnis für MYP erstellen/aktualisieren
mkdir -p /opt/myp
@@ -156,6 +160,9 @@ standard_installation() {
# Datenbank-Verzeichnis erstellen
mkdir -p /opt/myp/data
+ # SSL-Verzeichnis erstellen
+ mkdir -p /opt/myp/ssl
+
# Python-Umgebung und Abhängigkeiten einrichten
echo "Richte Python-Umgebung ein..."
cd /opt/myp
@@ -166,12 +173,18 @@ standard_installation() {
pip install --upgrade pip
pip install -r requirements.txt
+ # SSL-Zertifikate erstellen
+ echo "Erstelle SSL-Zertifikate..."
+ chmod +x /opt/myp/install/create_ssl_cert.sh
+ /opt/myp/install/create_ssl_cert.sh -d /opt/myp/ssl
+
# Berechtigungen setzen
echo "Setze Berechtigungen..."
chown -R www-data:www-data /opt/myp
chmod -R 755 /opt/myp
chmod -R 775 /opt/myp/logs
chmod -R 775 /opt/myp/data
+ chmod 600 /opt/myp/ssl/myp.key
echo -e "${GREEN}Installation abgeschlossen.${NC}"
echo ""
@@ -181,6 +194,7 @@ standard_installation() {
echo " cd /opt/myp && source .venv/bin/activate && python3.11 app/app.py"
echo ""
echo -e "${BLUE}Oder verwenden Sie Option 7 für Dienst-Management${NC}"
+ echo -e "${GREEN}MYP V2 ist unter https://$(hostname -I | awk '{print $1}'):5000 erreichbar${NC}"
read -p "Drücken Sie eine Taste, um zum Hauptmenü zurückzukehren..."
show_main_menu
@@ -1267,40 +1281,61 @@ system_status() {
echo -e "${GREEN}System-Status${NC}"
echo ""
- # MYP-Status
+ # Systeminfos anzeigen
+ echo -e "${YELLOW}Systeminformationen:${NC}"
+ echo -e "Hostname: $(hostname)"
+ echo -e "IP-Adresse: $(hostname -I | awk '{print $1}')"
+ echo -e "Betriebssystem: $(lsb_release -ds 2>/dev/null || cat /etc/*release 2>/dev/null | head -n1 || uname -om)"
+ echo -e "Kernel: $(uname -r)"
+ echo -e "CPU: $(grep -c ^processor /proc/cpuinfo) Kerne"
+ echo -e "RAM: $(free -h | awk '/^Mem/ {print $2}')"
+ echo -e "Festplatte: $(df -h / | awk 'NR==2 {print $2}')"
+ echo ""
+
+ # MYP-Status anzeigen
echo -e "${YELLOW}MYP-Status:${NC}"
if is_myp_installed; then
- echo "MYP ist installiert in /opt/myp"
- if systemctl is-active --quiet myp.service; then
- echo -e "MYP-Dienst: ${GREEN}Aktiv${NC}"
+ echo -e "MYP ist installiert: ${GREEN}Ja${NC}"
+
+ # Prüfen, ob der MYP-Service läuft
+ if systemctl is-active --quiet myp.service 2>/dev/null; then
+ echo -e "MYP-Service: ${GREEN}Aktiv${NC}"
else
- echo -e "MYP-Dienst: ${RED}Inaktiv${NC}"
+ echo -e "MYP-Service: ${RED}Inaktiv${NC}"
fi
+
+ # Pfadangaben
+ echo -e "Installationspfad: /opt/myp"
+ echo -e "Datenbank: /opt/myp/data"
+ echo -e "Logs: /opt/myp/logs"
+ echo -e "SSL-Zertifikate: /opt/myp/ssl"
+
+ # SSL-Status überprüfen
+ if [ -f "/opt/myp/ssl/myp.crt" ] && [ -f "/opt/myp/ssl/myp.key" ]; then
+ echo -e "SSL-Zertifikate: ${GREEN}Vorhanden${NC}"
+
+ # Zertifikatsinformationen anzeigen
+ if command -v openssl &> /dev/null; then
+ cert_expiry=$(openssl x509 -enddate -noout -in /opt/myp/ssl/myp.crt | cut -d= -f 2)
+ cert_subject=$(openssl x509 -subject -noout -in /opt/myp/ssl/myp.crt | sed 's/^subject=//')
+ echo -e "Zertifikat für: ${BLUE}$cert_subject${NC}"
+ echo -e "Gültig bis: ${BLUE}$cert_expiry${NC}"
+ fi
+ else
+ echo -e "SSL-Zertifikate: ${RED}Fehlen${NC}"
+ fi
+
+ # MYP-URLs anzeigen
+ echo -e ""
+ echo -e "${YELLOW}MYP-Zugriff:${NC}"
+ ip_address=$(hostname -I | awk '{print $1}')
+ echo -e "${GREEN}https://$ip_address:5000${NC} (verschlüsselt)"
+ echo -e "${YELLOW}http://$ip_address:5000${NC} (unverschlüsselt)"
else
- echo -e "MYP ist ${RED}nicht installiert${NC}"
+ echo -e "MYP ist installiert: ${RED}Nein${NC}"
fi
- echo ""
- # Netzwerkstatus
- echo -e "${YELLOW}Netzwerkstatus:${NC}"
- ip -o addr show | awk '$3 == "inet" {print $2 ": " $4}'
echo ""
-
- # DNS-Server
- echo -e "${YELLOW}DNS-Server:${NC}"
- grep "nameserver" /etc/resolv.conf 2>/dev/null || echo "Keine DNS-Server konfiguriert."
- echo ""
-
- # Systemressourcen
- echo -e "${YELLOW}Systemressourcen:${NC}"
- echo "CPU-Auslastung:"
- top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4 "% genutzt"}'
- echo "Speichernutzung:"
- free -h | grep "Mem:" | awk '{print $3 " von " $2 " genutzt"}'
- echo "Festplattenbelegung:"
- df -h / | grep -v "Filesystem" | awk '{print $3 " von " $2 " genutzt (" $5 ")"}'
- echo ""
-
read -p "Drücken Sie eine Taste, um zum Hauptmenü zurückzukehren..."
show_main_menu
}
@@ -1491,6 +1526,57 @@ show_documentation() {
show_main_menu
}
+# 10) SSL-Zertifikat-Management
+show_ssl_management() {
+ print_header
+ echo -e "${GREEN}SSL-Zertifikat-Management${NC}"
+ echo ""
+
+ echo -e "Bitte wählen Sie eine Option:"
+ echo ""
+ echo "1) SSL-Zertifikatsstatus anzeigen"
+ echo "2) Neue SSL-Zertifikate erstellen"
+ echo "3) SSL-Einstellungen in settings.py anzeigen/bearbeiten"
+ echo ""
+ echo "b) Zurück zum Hauptmenü"
+ echo ""
+ read -p "Ihre Auswahl: " ssl_option
+
+ case $ssl_option in
+ 1)
+ # SSL-Status anzeigen
+ chmod +x /opt/myp/install/ssl_check.sh
+ /opt/myp/install/ssl_check.sh
+ ;;
+ 2)
+ # Neue Zertifikate erstellen
+ echo -e "${YELLOW}Erstelle neue SSL-Zertifikate...${NC}"
+ chmod +x /opt/myp/install/create_ssl_cert.sh
+ /opt/myp/install/create_ssl_cert.sh -d /opt/myp/ssl
+ ;;
+ 3)
+ # SSL-Einstellungen anzeigen/bearbeiten
+ if command -v nano &> /dev/null; then
+ nano /opt/myp/app/config/settings.py
+ else
+ vi /opt/myp/app/config/settings.py
+ fi
+ ;;
+ b|B)
+ show_main_menu
+ return
+ ;;
+ *)
+ echo -e "${RED}Ungültige Option.${NC}"
+ sleep 2
+ show_ssl_management
+ ;;
+ esac
+
+ read -p "Drücken Sie eine Taste, um zum SSL-Menü zurückzukehren..."
+ show_ssl_management
+}
+
# Hauptprogramm
check_root
show_main_menu
\ No newline at end of file
diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs
index a53ad9fa..5f40b97c 100644
--- a/frontend/next.config.mjs
+++ b/frontend/next.config.mjs
@@ -27,12 +27,12 @@ const nextConfig = {
return [
{
source: '/api/backend/:path*',
- destination: 'http://192.168.0.105:5000/api/:path*',
+ destination: 'https://192.168.0.105:5000/api/:path*',
},
// Direkter Proxy für Health-Checks
{
source: '/backend-health',
- destination: 'http://192.168.0.105:5000/health',
+ destination: 'https://192.168.0.105:5000/health',
},
];
},
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
index 06d5c381..d62883f2 100644
--- a/frontend/src/app/layout.tsx
+++ b/frontend/src/app/layout.tsx
@@ -1,6 +1,7 @@
import { Header } from "@/components/header";
import { Toaster } from "@/components/ui/toaster";
import type { Metadata } from "next";
+import { SSLWarning } from "@/components/ui/ssl-warning";
import "@/app/globals.css";
@@ -26,6 +27,7 @@ export default function RootLayout(props: RootLayoutProps) {
+
{children}
diff --git a/frontend/src/components/ui/ssl-warning.tsx b/frontend/src/components/ui/ssl-warning.tsx
new file mode 100644
index 00000000..a7a3c929
--- /dev/null
+++ b/frontend/src/components/ui/ssl-warning.tsx
@@ -0,0 +1,86 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { API_BASE_URL } from "@/utils/api-config";
+
+/**
+ * SSLWarning - Zeigt eine Warnung für selbstsignierte SSL-Zertifikate an
+ * und bietet eine Möglichkeit, diese zu akzeptieren
+ */
+export function SSLWarning() {
+ const [showWarning, setShowWarning] = useState(false);
+
+ useEffect(() => {
+ // Prüfe, ob die Warnung angezeigt werden soll
+ const hasSeenWarning = localStorage.getItem("ssl-warning-dismissed");
+
+ // Zeige die Warnung nur an, wenn der Benutzer sie noch nicht gesehen hat
+ if (!hasSeenWarning) {
+ setShowWarning(true);
+ }
+ }, []);
+
+ // Teste die Backend-Verbindung und prüfe auf Zertifikatsprobleme
+ useEffect(() => {
+ if (!showWarning) return;
+
+ const testConnection = async () => {
+ try {
+ // Teste HTTPS-Verbindung
+ await fetch(`${API_BASE_URL}/health`, {
+ method: 'GET',
+ mode: 'no-cors' // Verwende no-cors für SSL-Tests
+ });
+ // Bei erfolgreicher Verbindung: Blende die Warnung aus
+ setShowWarning(false);
+ } catch (error) {
+ console.warn("SSL-Verbindungstest fehlgeschlagen:", error);
+ // Bei Fehlern: Zeige die Warnung an
+ setShowWarning(true);
+ }
+ };
+
+ testConnection();
+ }, [showWarning]);
+
+ const dismissWarning = () => {
+ localStorage.setItem("ssl-warning-dismissed", "true");
+ setShowWarning(false);
+ };
+
+ const openBackendDirectly = () => {
+ // Öffne das Backend direkt in einem neuen Tab
+ window.open(API_BASE_URL, "_blank");
+ };
+
+ if (!showWarning) {
+ return null;
+ }
+
+ return (
+
+
+
+
SSL-Sicherheitshinweis
+
+ Diese Anwendung verwendet ein selbstsigniertes SSL-Zertifikat für sichere Kommunikation.
+ Um Verbindungsprobleme zu vermeiden, öffnen Sie bitte einmalig die folgende URL und akzeptieren Sie das Zertifikat:
+