#!/bin/bash # =================================================================== # MYP Druckerverwaltung - VOLLSTÄNDIGER KIOSK-INSTALLER für Raspbian # Entwickelt auf Windows, ausführbar auf Raspberry Pi / Debian # OHNE virtualenv - verwendet System-Python mit --break-system-packages # ECHTER KIOSK-MODUS: Entfernt Desktop, 1 Backend-Instanz HTTPS, Autologin # =================================================================== set -euo pipefail # =========================== KONFIGURATION =========================== APP_NAME="MYP Druckerverwaltung" APP_DIR="/opt/myp" SERVICE_NAME="myp-https" KIOSK_USER="kiosk" CURRENT_DIR="$(pwd)" INSTALL_LOG="/var/log/myp-install.log" # Farben für Ausgabe RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' NC='\033[0m' # ========================== LOGGING-SYSTEM ========================== log() { echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$INSTALL_LOG" } error() { echo -e "${RED}[FEHLER] $1${NC}" | tee -a "$INSTALL_LOG" exit 1 } warning() { echo -e "${YELLOW}[WARNUNG] $1${NC}" | tee -a "$INSTALL_LOG" } info() { echo -e "${BLUE}[INFO] $1${NC}" | tee -a "$INSTALL_LOG" } progress() { echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG" } # ========================== SYSTEM-CHECKS ========================== check_root() { if [ "$EUID" -ne 0 ]; then error "Dieses Skript muss als Root ausgeführt werden: sudo $0" fi export PATH="/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:$PATH" } check_debian_system() { if [ ! -f /etc/debian_version ]; then error "Dieses Skript ist nur für Debian/Raspbian-Systeme geeignet!" fi log "✅ Debian/Raspbian-System erkannt" } # ========================== DESKTOP ENVIRONMENTS ENTFERNEN ========================== remove_desktop_environments() { log "=== ENTFERNE ALLE DESKTOP ENVIRONMENTS ===" progress "Stoppe alle Desktop-Services..." systemctl stop lightdm 2>/dev/null || true systemctl stop gdm3 2>/dev/null || true systemctl stop sddm 2>/dev/null || true systemctl stop xdm 2>/dev/null || true systemctl disable lightdm 2>/dev/null || true systemctl disable gdm3 2>/dev/null || true systemctl disable sddm 2>/dev/null || true systemctl disable xdm 2>/dev/null || true progress "Entferne Desktop-Pakete vollständig..." # Raspberry Pi OS Desktop entfernen apt-get remove --purge -y \ raspberrypi-ui-mods \ pi-package \ desktop-base \ lxde* \ xfce4* \ gnome* \ kde* \ mate* \ cinnamon* \ openbox \ pcmanfm \ file-manager* \ task-lxde-desktop \ task-xfce-desktop \ task-gnome-desktop \ task-kde-desktop \ 2>/dev/null || true # Display Manager entfernen apt-get remove --purge -y \ lightdm* \ gdm3* \ sddm* \ xdm* \ nodm* \ 2>/dev/null || true # Unnötige Pakete entfernen apt-get remove --purge -y \ libreoffice* \ thunderbird* \ firefox* \ vlc* \ gimp* \ scratch* \ minecraft-pi \ sonic-pi \ 2>/dev/null || true # Autoremove und Autoclean apt-get autoremove --purge -y apt-get autoclean log "✅ Desktop Environments vollständig entfernt" } # ========================== KIOSK BENUTZER ERSTELLEN ========================== create_kiosk_user() { log "=== ERSTELLE KIOSK-BENUTZER ===" # Kiosk-Benutzer erstellen falls nicht vorhanden if ! id "$KIOSK_USER" &>/dev/null; then progress "Erstelle Kiosk-Benutzer: $KIOSK_USER" useradd -m -s /bin/bash "$KIOSK_USER" || error "Kann Kiosk-Benutzer nicht erstellen" # Gruppen hinzufügen usermod -aG audio,video,input,dialout,plugdev,users "$KIOSK_USER" 2>/dev/null || true else info "Kiosk-Benutzer $KIOSK_USER existiert bereits" fi # Passwort entfernen für automatischen Login passwd -d "$KIOSK_USER" || warning "Konnte Passwort nicht entfernen" log "✅ Kiosk-Benutzer erstellt: $KIOSK_USER" } # ========================== MINIMALE X11 INSTALLATION ========================== install_minimal_x11() { log "=== INSTALLIERE MINIMALE X11-UMGEBUNG ===" progress "Installiere minimale X11-Pakete..." apt-get install -y \ xserver-xorg-core \ xserver-xorg-input-all \ xserver-xorg-video-fbdev \ xserver-xorg-video-vesa \ xinit \ x11-xserver-utils \ xdotool \ unclutter \ openbox \ || error "X11 Installation fehlgeschlagen" # Chromium Browser mit Fallback-Mechanismus installieren progress "Installiere Chromium Browser..." if apt-get install -y chromium 2>/dev/null; then log "✅ Chromium erfolgreich installiert" elif apt-get install -y chromium-browser 2>/dev/null; then log "✅ Chromium-Browser erfolgreich installiert" elif apt-get install -y firefox-esr 2>/dev/null; then warning "⚠️ Chromium nicht verfügbar - Firefox ESR als Fallback installiert" # Firefox-Konfiguration für Kiosk-Modus anpassen progress "Konfiguriere Firefox für Kiosk-Modus..." FIREFOX_BROWSER="firefox-esr" else error "❌ Kein Browser verfügbar (chromium, chromium-browser, firefox-esr)" fi log "✅ Minimale X11-Umgebung installiert" } # ========================== MERCEDES ZERTIFIKATE ========================== install_mercedes_certificates() { log "=== INSTALLIERE MERCEDES SSL-ZERTIFIKATE ===" progress "Aktualisiere CA-Zertifikate..." # CA-Zertifikate Paket installieren/aktualisieren apt-get install -y ca-certificates curl openssl || error "CA-Zertifikate Installation fehlgeschlagen" # Standard CA-Bundle aktualisieren update-ca-certificates || warning "CA-Zertifikate Update teilweise fehlgeschlagen" # Mercedes Corporate Zertifikate installieren (falls vorhanden) if [ -d "$CURRENT_DIR/certs/mercedes" ] && [ "$(ls -A $CURRENT_DIR/certs/mercedes 2>/dev/null)" ]; then progress "Installiere Mercedes Corporate Zertifikate..." # Erstelle temporäres Verzeichnis für Zertifikat-Verarbeitung TEMP_CERT_DIR="/tmp/mercedes_certs" mkdir -p "$TEMP_CERT_DIR" # Alle Zertifikatsdateien verarbeiten (.crt, .pem, .cer) find "$CURRENT_DIR/certs/mercedes" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) | while read cert_file; do cert_basename=$(basename "$cert_file") cert_name="${cert_basename%.*}" progress "Verarbeite Mercedes-Zertifikat: $cert_basename" # .cer Dateien in .crt konvertieren (falls nötig) if [[ "$cert_file" == *.cer ]]; then progress "Konvertiere .cer zu .crt: $cert_basename" # Überprüfe ob es sich um DER- oder PEM-Format handelt if openssl x509 -in "$cert_file" -text -noout >/dev/null 2>&1; then # PEM-Format - direkt kopieren mit .crt Endung cp "$cert_file" "$TEMP_CERT_DIR/${cert_name}.crt" elif openssl x509 -in "$cert_file" -inform DER -text -noout >/dev/null 2>&1; then # DER-Format - zu PEM konvertieren openssl x509 -in "$cert_file" -inform DER -out "$TEMP_CERT_DIR/${cert_name}.crt" -outform PEM else warning "Unbekanntes Zertifikatformat: $cert_file" continue fi else # .crt oder .pem - direkt kopieren cp "$cert_file" "$TEMP_CERT_DIR/" fi # Zertifikat validieren if openssl x509 -in "$TEMP_CERT_DIR/${cert_name}.crt" -text -noout >/dev/null 2>&1; then log "✅ Zertifikat validiert: ${cert_name}.crt" # In CA-Zertifikate-Verzeichnis kopieren cp "$TEMP_CERT_DIR/${cert_name}.crt" "/usr/local/share/ca-certificates/" else warning "⚠️ Ungültiges Zertifikat übersprungen: $cert_file" fi done # Spezielle Mercedes-Zertifikate verarbeiten if [ -f "$CURRENT_DIR/certs/mercedes/Corp-Pri-Root-CA.cer" ]; then progress "Installiere Mercedes Corporate Primary Root CA..." # Versuche DER-zu-PEM Konvertierung if openssl x509 -in "$CURRENT_DIR/certs/mercedes/Corp-Pri-Root-CA.cer" -inform DER -out "/usr/local/share/ca-certificates/Mercedes-Corp-Pri-Root-CA.crt" -outform PEM 2>/dev/null; then log "✅ Corp-Pri-Root-CA.cer erfolgreich als DER konvertiert" # Versuche direkte PEM-Kopie elif openssl x509 -in "$CURRENT_DIR/certs/mercedes/Corp-Pri-Root-CA.cer" -text -noout >/dev/null 2>&1; then cp "$CURRENT_DIR/certs/mercedes/Corp-Pri-Root-CA.cer" "/usr/local/share/ca-certificates/Mercedes-Corp-Pri-Root-CA.crt" log "✅ Corp-Pri-Root-CA.cer direkt als PEM kopiert" else warning "⚠️ Corp-Pri-Root-CA.cer Format nicht erkannt - übersprungen" fi fi if [ -f "$CURRENT_DIR/certs/mercedes/Corp-Root-CA-G2.cer" ]; then progress "Installiere Mercedes Corporate Root CA G2..." # Versuche DER-zu-PEM Konvertierung if openssl x509 -in "$CURRENT_DIR/certs/mercedes/Corp-Root-CA-G2.cer" -inform DER -out "/usr/local/share/ca-certificates/Mercedes-Corp-Root-CA-G2.crt" -outform PEM 2>/dev/null; then log "✅ Corp-Root-CA-G2.cer erfolgreich als DER konvertiert" # Versuche direkte PEM-Kopie elif openssl x509 -in "$CURRENT_DIR/certs/mercedes/Corp-Root-CA-G2.cer" -text -noout >/dev/null 2>&1; then cp "$CURRENT_DIR/certs/mercedes/Corp-Root-CA-G2.cer" "/usr/local/share/ca-certificates/Mercedes-Corp-Root-CA-G2.crt" log "✅ Corp-Root-CA-G2.cer direkt als PEM kopiert" else warning "⚠️ Corp-Root-CA-G2.cer Format nicht erkannt - übersprungen" fi fi # Temporäres Verzeichnis aufräumen rm -rf "$TEMP_CERT_DIR" # CA-Zertifikate aktualisieren update-ca-certificates || warning "Mercedes Zertifikate Update fehlgeschlagen" # Installierte Mercedes-Zertifikate auflisten MERCEDES_CERTS=$(ls /usr/local/share/ca-certificates/*Mercedes* 2>/dev/null | wc -l) if [ "$MERCEDES_CERTS" -gt 0 ]; then log "✅ $MERCEDES_CERTS Mercedes-Zertifikate erfolgreich installiert" ls /usr/local/share/ca-certificates/*Mercedes* 2>/dev/null | while read cert; do log " 📜 $(basename "$cert")" done else warning "⚠️ Keine Mercedes-Zertifikate installiert" fi else info "Keine Mercedes-Zertifikate gefunden - verwende Standard CA-Bundle" fi # Python SSL-Konfiguration optimieren progress "Konfiguriere Python SSL-Einstellungen..." # pip Konfiguration für SSL mit erweiterten Optionen mkdir -p /root/.pip cat > /root/.pip/pip.conf << EOF [global] trusted-host = pypi.org pypi.python.org files.pythonhosted.org cert = /etc/ssl/certs/ca-certificates.crt timeout = 60 retries = 3 no-cache-dir = true [install] trusted-host = pypi.org pypi.python.org files.pythonhosted.org no-warn-script-location = true EOF # Python HTTPS-Konfiguration mit Mercedes-Support export SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt" export REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" export CURL_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" export PYTHONHTTPSVERIFY=1 # Node.js SSL-Konfiguration hinzufügen export NODE_TLS_REJECT_UNAUTHORIZED=1 export NODE_EXTRA_CA_CERTS="/etc/ssl/certs/ca-certificates.crt" # npm SSL-Konfiguration mit verbesserter Fehlerbehandlung progress "Konfiguriere npm SSL-Einstellungen..." npm config set ca "" 2>/dev/null || true npm config set cafile "/etc/ssl/certs/ca-certificates.crt" 2>/dev/null || true npm config set strict-ssl true 2>/dev/null || true npm config set registry "https://registry.npmjs.org/" 2>/dev/null || true # Pip auf neueste Version aktualisieren progress "Aktualisiere pip auf neueste Version..." python3 -m pip install --upgrade pip --break-system-packages --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org || warning "pip Update fehlgeschlagen" # SSL-Tests durchführen progress "Teste SSL-Verbindungen..." # Test PyPI if python3 -c "import ssl, urllib.request; urllib.request.urlopen('https://pypi.org')" 2>/dev/null; then log "✅ SSL-Verbindung zu PyPI erfolgreich" else warning "⚠️ SSL-Verbindung zu PyPI problematisch" fi # Test npm Registry if npm ping --registry https://registry.npmjs.org >/dev/null 2>&1; then log "✅ SSL-Verbindung zu npm Registry erfolgreich" else warning "⚠️ SSL-Verbindung zu npm Registry problematisch" fi # Zeige installierte CA-Zertifikate-Anzahl CA_COUNT=$(ls /etc/ssl/certs/*.pem 2>/dev/null | wc -l) log "✅ Insgesamt $CA_COUNT CA-Zertifikate im System verfügbar" log "✅ Mercedes SSL-Zertifikate und System-Konfiguration abgeschlossen" } # ========================== ABHÄNGIGKEITEN INSTALLIEREN ========================== install_system_dependencies() { log "=== INSTALLIERE SYSTEM-ABHÄNGIGKEITEN ===" progress "Aktualisiere Paketlisten..." apt-get update -y || error "APT Update fehlgeschlagen" progress "Installiere Python 3 und grundlegende Pakete..." apt-get install -y \ python3 \ python3-pip \ python3-dev \ python3-setuptools \ build-essential \ libssl-dev \ libffi-dev \ git \ curl \ wget \ nano \ htop \ rsync \ unzip \ sudo \ systemd \ ca-certificates \ gnupg \ lsb-release \ sqlite3 \ || error "System-Pakete Installation fehlgeschlagen" # ========================== NODE.JS UND NPM INSTALLATION ========================== progress "Installiere Node.js und npm..." # Alte Node.js-Installationen entfernen progress "Entferne alte Node.js-Installationen..." apt-get remove --purge -y nodejs npm 2>/dev/null || true apt-get autoremove -y 2>/dev/null || true # NodeSource Repository für Node.js LTS 20.x hinzufügen progress "Füge NodeSource Repository für Node.js 20.x LTS hinzu..." curl -fsSL https://deb.nodesource.com/setup_20.x | bash - || { warning "NodeSource Repository konnte nicht hinzugefügt werden" # Fallback: Manueller Download und Installation progress "Fallback: Installiere Node.js aus Debian-Repository..." apt-get update -y apt-get install -y nodejs npm || { error "Node.js Installation komplett fehlgeschlagen" } } # Node.js aus NodeSource Repository installieren (falls Repository erfolgreich hinzugefügt) if [ -f /etc/apt/sources.list.d/nodesource.list ]; then progress "Installiere Node.js 20.x LTS aus NodeSource Repository..." apt-get update -y apt-get install -y nodejs || { warning "NodeSource Installation fehlgeschlagen - verwende Fallback" apt-get install -y nodejs npm || error "Node.js Fallback Installation fehlgeschlagen" } fi # Überprüfe Node.js Installation if command -v node >/dev/null 2>&1; then NODE_VERSION=$(node --version) log "✅ Node.js erfolgreich installiert: $NODE_VERSION" else error "❌ Node.js Installation fehlgeschlagen" fi # npm sollte automatisch mit Node.js mitgeliefert werden if command -v npm >/dev/null 2>&1; then NPM_VERSION=$(npm --version) log "✅ npm automatisch installiert: $NPM_VERSION" # VORSICHTIG: npm NUR aktualisieren wenn Node.js >= 18 NODE_MAJOR_VERSION=$(node --version | cut -d'.' -f1 | sed 's/v//') if [ "$NODE_MAJOR_VERSION" -ge 18 ]; then progress "Node.js $NODE_MAJOR_VERSION erkannt - npm-Update möglich..." npm install -g npm@latest --force 2>/dev/null || { warning "npm-Update fehlgeschlagen - verwende bestehende Version $NPM_VERSION" } NPM_VERSION_NEW=$(npm --version) log "✅ npm Version: $NPM_VERSION_NEW" else warning "⚠️ Node.js < 18 - npm-Update übersprungen für Kompatibilität" fi else error "❌ npm nicht verfügbar" fi # npm-Konfiguration für bessere Performance progress "Konfiguriere npm-Einstellungen..." npm config set fund false 2>/dev/null || true npm config set audit-level moderate 2>/dev/null || true npm config set package-lock true 2>/dev/null || true # WICHTIG: Mercedes Zertifikate vor Python-Paketen installieren install_mercedes_certificates progress "Installiere Python-Abhängigkeiten mit Mercedes SSL-Konfiguration..." # SSL-freundliche pip-Installation mit Fallback-Optionen PIP_OPTS="--break-system-packages --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --timeout 60 --retries 3" # Core Flask Framework pip3 install $PIP_OPTS Flask==3.1.1 || pip3 install $PIP_OPTS Flask || error "Flask Installation fehlgeschlagen" pip3 install $PIP_OPTS Flask-Login==0.6.3 || pip3 install $PIP_OPTS Flask-Login || error "Flask-Login Installation fehlgeschlagen" pip3 install $PIP_OPTS Flask-WTF==1.2.1 || pip3 install $PIP_OPTS Flask-WTF || error "Flask-WTF Installation fehlgeschlagen" # Datenbank pip3 install $PIP_OPTS SQLAlchemy==2.0.36 || pip3 install $PIP_OPTS SQLAlchemy || error "SQLAlchemy Installation fehlgeschlagen" # Sicherheit pip3 install $PIP_OPTS bcrypt==4.2.1 || pip3 install $PIP_OPTS bcrypt || error "bcrypt Installation fehlgeschlagen" pip3 install $PIP_OPTS cryptography==44.0.0 || pip3 install $PIP_OPTS cryptography || error "cryptography Installation fehlgeschlagen" pip3 install $PIP_OPTS Werkzeug==3.1.3 || pip3 install $PIP_OPTS Werkzeug || error "Werkzeug Installation fehlgeschlagen" # Smart Plug Steuerung pip3 install $PIP_OPTS PyP100 || warning "PyP100 Installation fehlgeschlagen (optional)" # HTTP Requests pip3 install $PIP_OPTS requests==2.32.3 || pip3 install $PIP_OPTS requests || error "requests Installation fehlgeschlagen" # System Monitoring pip3 install $PIP_OPTS psutil==6.1.1 || pip3 install $PIP_OPTS psutil || error "psutil Installation fehlgeschlagen" # Redis (optional) pip3 install $PIP_OPTS redis==5.2.1 || warning "redis Installation fehlgeschlagen (optional)" # Weitere Core-Abhängigkeiten pip3 install $PIP_OPTS MarkupSafe==3.0.2 || pip3 install $PIP_OPTS MarkupSafe || error "MarkupSafe Installation fehlgeschlagen" # Produktions-Server pip3 install $PIP_OPTS gunicorn==23.0.0 || pip3 install $PIP_OPTS gunicorn || error "gunicorn Installation fehlgeschlagen" # ========================== NPM-ABHÄNGIGKEITEN INSTALLIEREN ========================== progress "Prüfe auf package.json für npm-Abhängigkeiten..." if [ -f "$CURRENT_DIR/package.json" ]; then progress "package.json gefunden - installiere npm-Abhängigkeiten..." cd "$CURRENT_DIR" # npm install OHNE --production um auch devDependencies (TailwindCSS) zu installieren progress "Installiere alle npm-Abhängigkeiten (inklusive devDependencies für TailwindCSS)..." # Erste Versuche: Standard-Installation if npm install --no-optional --no-audit --no-fund 2>/dev/null; then log "✅ npm install erfolgreich (Standard)" # Zweiter Versuch: Mit legacy-peer-deps elif npm install --no-optional --no-audit --no-fund --legacy-peer-deps 2>/dev/null; then log "✅ npm install erfolgreich (mit --legacy-peer-deps)" # Dritter Versuch: Mit force Flag elif npm install --no-optional --no-audit --no-fund --force 2>/dev/null; then log "✅ npm install erfolgreich (mit --force)" # Vierter Versuch: Mit allen Flags elif npm install --no-optional --no-audit --no-fund --legacy-peer-deps --force 2>/dev/null; then log "✅ npm install erfolgreich (mit allen Flags)" else warning "⚠️ npm install fehlgeschlagen - versuche manuelle Paket-Installation..." # Manuelle Installation der wichtigsten Pakete npm install tailwindcss --no-audit --no-fund --force 2>/dev/null || warning "TailwindCSS Installation fehlgeschlagen" npm install @tailwindcss/forms --no-audit --no-fund --force 2>/dev/null || warning "@tailwindcss/forms Installation fehlgeschlagen" npm install chart.js --no-audit --no-fund --force 2>/dev/null || warning "Chart.js Installation fehlgeschlagen" npm install @fortawesome/fontawesome-free --no-audit --no-fund --force 2>/dev/null || warning "FontAwesome Installation fehlgeschlagen" fi log "✅ npm-Abhängigkeiten-Installation abgeschlossen:" log " 📦 Dependencies: FontAwesome, FullCalendar, Chart.js, Vite" log " 🛠️ DevDependencies: TailwindCSS, PostCSS, Autoprefixer" # Überprüfe TailwindCSS Installation speziell if npx tailwindcss --help >/dev/null 2>&1; then log "✅ TailwindCSS erfolgreich installiert und verfügbar" else warning "❌ TailwindCSS nicht verfügbar - versuche globale Installation..." npm install -g tailwindcss --force 2>/dev/null || error "TailwindCSS Installation komplett fehlgeschlagen" fi # Überprüfe auf Build-Scripts und führe sie aus if npm run | grep -q "build:css"; then progress "CSS-Build-Script gefunden - führe npm run build:css aus..." if npm run build:css 2>/dev/null; then log "✅ TailwindCSS Build erfolgreich abgeschlossen" else warning "⚠️ npm run build:css fehlgeschlagen - versuche direkten npx-Aufruf..." if npx tailwindcss -i ./static/css/input.css -o ./static/css/tailwind.min.css --minify 2>/dev/null; then log "✅ TailwindCSS mit npx erfolgreich kompiliert" else error "❌ TailwindCSS Build komplett fehlgeschlagen" fi fi fi if npm run | grep -q "build" && ! npm run | grep -q "build:css"; then progress "Haupt-Build-Script gefunden - führe npm run build aus..." npm run build 2>/dev/null || warning "npm run build fehlgeschlagen" log "✅ Frontend-Build abgeschlossen" fi # Überprüfe generierte CSS-Dateien if [ -f "$CURRENT_DIR/static/css/tailwind.min.css" ]; then TAILWIND_SIZE=$(du -h "$CURRENT_DIR/static/css/tailwind.min.css" | cut -f1) log "✅ TailwindCSS erfolgreich kompiliert: tailwind.min.css ($TAILWIND_SIZE)" else warning "⚠️ tailwind.min.css nicht gefunden - CSS-Build möglicherweise fehlgeschlagen" fi else info "Keine package.json gefunden - überspringe npm-Abhängigkeiten" fi log "✅ Alle Abhängigkeiten (Python + Node.js + npm) erfolgreich installiert" } # ========================== SSL-ZERTIFIKATE FÜR LOCALHOST ERSTELLEN ========================== create_localhost_ssl_certificates() { log "=== ERSTELLE SSL-ZERTIFIKATE FÜR LOCALHOST ===" SSL_DIR="$APP_DIR/certs/localhost" mkdir -p "$SSL_DIR" progress "Erstelle SSL-Zertifikate für localhost..." # Private Key erstellen openssl genrsa -out "$SSL_DIR/localhost.key" 2048 || error "Private Key Erstellung fehlgeschlagen" # Certificate Signing Request (CSR) erstellen openssl req -new -key "$SSL_DIR/localhost.key" -out "$SSL_DIR/localhost.csr" -subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=MYP/CN=localhost" || error "CSR Erstellung fehlgeschlagen" # Self-signed Zertifikat erstellen (gültig für 365 Tage) openssl x509 -req -days 365 -in "$SSL_DIR/localhost.csr" -signkey "$SSL_DIR/localhost.key" -out "$SSL_DIR/localhost.crt" -extensions v3_req -extfile <( cat < "$APP_DIR/start_https.py" << 'EOF' #!/usr/bin/env python3 import sys import os # Füge App-Verzeichnis zum Python-Pfad hinzu sys.path.insert(0, '/opt/myp') # Setze Umgebungsvariablen os.environ['FLASK_PORT'] = '443' os.environ['FLASK_HOST'] = '0.0.0.0' os.environ['FLASK_ENV'] = 'production' try: from app import app, get_ssl_context ssl_context = get_ssl_context() if ssl_context: print("Starte HTTPS-Server auf Port 443...") app.run(host='0.0.0.0', port=443, debug=False, ssl_context=ssl_context, threaded=True) else: print('SSL-Kontext nicht verfügbar - verwende localhost Zertifikate') # Fallback auf localhost-Zertifikate ssl_context = ('/opt/myp/certs/localhost/localhost.crt', '/opt/myp/certs/localhost/localhost.key') app.run(host='0.0.0.0', port=443, debug=False, ssl_context=ssl_context, threaded=True) except Exception as e: print(f"Fehler beim Starten des HTTPS-Servers: {e}") sys.exit(1) EOF # Skript ausführbar machen chmod +x "$APP_DIR/start_https.py" # HTTPS-Service (Port 443) progress "Erstelle myp-https.service (Port 443)..." cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF [Unit] Description=MYP HTTPS Backend (Port 443) After=network.target network-online.target Wants=network-online.target Requires=network.target [Service] Type=simple User=root Group=root WorkingDirectory=$APP_DIR ExecStart=/usr/bin/python3 $APP_DIR/start_https.py Restart=always RestartSec=5 StartLimitBurst=5 StartLimitInterval=60 # Umgebungsvariablen Environment=PYTHONUNBUFFERED=1 Environment=FLASK_ENV=production Environment=FLASK_HOST=0.0.0.0 Environment=FLASK_PORT=443 Environment=PYTHONPATH=$APP_DIR Environment=LC_ALL=C.UTF-8 Environment=LANG=C.UTF-8 Environment=SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt Environment=REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt # Logging StandardOutput=journal StandardError=journal SyslogIdentifier=myp-https # Security-Einstellungen NoNewPrivileges=true PrivateTmp=false ProtectSystem=strict ReadWritePaths=$APP_DIR [Install] WantedBy=multi-user.target EOF log "✅ HTTPS Backend-Service erstellt" } # ========================== KIOSK BROWSER KONFIGURATION ========================== configure_kiosk_browser() { log "=== KONFIGURIERE KIOSK-BROWSER FÜR HTTPS ===" KIOSK_HOME="/home/$KIOSK_USER" # Openbox-Konfiguration für randlosen Vollbildmodus progress "Konfiguriere Openbox für randlosen Vollbildmodus..." mkdir -p "$KIOSK_HOME/.config/openbox" cat > "$KIOSK_HOME/.config/openbox/rc.xml" << 'EOF' 10 20 yes no Smart
yes
Clearlooks NLIMC no yes 1 1 Desktop 1 875 yes Nonpixel Center 0 0 0 0 TopLeft 0 0 no Above Vertical no 300 300 Middle 8 200 400 no no 0 0 100% 100% yes yes yes yes above no no 0 0 100% 100% yes yes yes yes above
EOF # .bashrc für automatischen X-Start erweitern progress "Konfiguriere automatischen X-Start..." cat >> "$KIOSK_HOME/.bashrc" << 'EOF' # Automatischer X-Start für Kiosk-Modus if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then exec startx fi EOF # .xinitrc für HTTPS Kiosk-Session erstellen progress "Erstelle optimierte HTTPS Kiosk X-Session..." cat > "$KIOSK_HOME/.xinitrc" << 'EOF' #!/bin/bash # Bildschirmauflösung automatisch erkennen RESOLUTION=$(xrandr | grep '*' | head -1 | awk '{print $1}') if [ -z "$RESOLUTION" ]; then RESOLUTION="1920x1080" # Fallback-Auflösung fi WIDTH=$(echo $RESOLUTION | cut -d'x' -f1) HEIGHT=$(echo $RESOLUTION | cut -d'x' -f2) echo "Erkannte Bildschirmauflösung: ${WIDTH}x${HEIGHT}" # Bildschirmschoner und Energieverwaltung komplett deaktivieren xset s off xset s noblank xset s noexpose xset -dpms xset s 0 0 # Mauszeiger verstecken (aggressiver) unclutter -idle 0.1 -root -noevents & # Openbox im Hintergrund starten openbox & # Warte auf HTTPS Backend-Service echo "Warte auf MYP HTTPS Backend-Service..." WAIT_COUNT=0 while ! curl -k -s https://localhost:443 > /dev/null; do echo "Warte auf HTTPS Backend (Port 443)... ($WAIT_COUNT/60)" sleep 2 WAIT_COUNT=$((WAIT_COUNT + 1)) if [ $WAIT_COUNT -gt 60 ]; then echo "FEHLER: HTTPS Backend nach 120s nicht erreichbar!" break fi done echo "HTTPS Backend erreichbar - starte Kiosk-Browser..." # Browser-Erkennung und -Start mit HTTPS-optimierten Vollbild-Flags if command -v chromium >/dev/null 2>&1; then echo "Starte Chromium Browser mit HTTPS und Auflösung ${WIDTH}x${HEIGHT}..." exec chromium \ --kiosk \ --no-sandbox \ --disable-infobars \ --disable-session-crashed-bubble \ --disable-restore-session-state \ --disable-features=TranslateUI \ --disable-extensions \ --disable-plugins \ --disable-popup-blocking \ --disable-prompt-on-repost \ --disable-sync \ --disable-translate \ --noerrdialogs \ --no-first-run \ --no-default-browser-check \ --autoplay-policy=no-user-gesture-required \ --start-fullscreen \ --start-maximized \ --window-size=${WIDTH},${HEIGHT} \ --window-position=0,0 \ --user-data-dir=/home/kiosk/.chromium-kiosk \ --disable-background-mode \ --force-device-scale-factor=1.0 \ --disable-pinch \ --overscroll-history-navigation=0 \ --disable-dev-shm-usage \ --memory-pressure-off \ --max_old_space_size=512 \ --disable-background-timer-throttling \ --disable-backgrounding-occluded-windows \ --disable-renderer-backgrounding \ --disable-features=VizDisplayCompositor \ --enable-features=OverlayScrollbar \ --hide-scrollbars \ --ignore-certificate-errors \ --ignore-ssl-errors \ --ignore-certificate-errors-spki-list \ --disable-web-security \ --allow-running-insecure-content \ --unsafely-treat-insecure-origin-as-secure=https://localhost:443 \ https://localhost:443 elif command -v chromium-browser >/dev/null 2>&1; then echo "Starte Chromium-Browser mit HTTPS und Auflösung ${WIDTH}x${HEIGHT}..." exec chromium-browser \ --kiosk \ --no-sandbox \ --disable-infobars \ --disable-session-crashed-bubble \ --disable-restore-session-state \ --disable-features=TranslateUI \ --disable-extensions \ --disable-plugins \ --disable-popup-blocking \ --disable-prompt-on-repost \ --disable-sync \ --disable-translate \ --noerrdialogs \ --no-first-run \ --no-default-browser-check \ --autoplay-policy=no-user-gesture-required \ --start-fullscreen \ --start-maximized \ --window-size=${WIDTH},${HEIGHT} \ --window-position=0,0 \ --user-data-dir=/home/kiosk/.chromium-kiosk \ --disable-background-mode \ --force-device-scale-factor=1.0 \ --disable-pinch \ --overscroll-history-navigation=0 \ --disable-dev-shm-usage \ --memory-pressure-off \ --max_old_space_size=512 \ --disable-background-timer-throttling \ --disable-backgrounding-occluded-windows \ --disable-renderer-backgrounding \ --disable-features=VizDisplayCompositor \ --enable-features=OverlayScrollbar \ --hide-scrollbars \ --ignore-certificate-errors \ --ignore-ssl-errors \ --ignore-certificate-errors-spki-list \ --disable-web-security \ --allow-running-insecure-content \ --unsafely-treat-insecure-origin-as-secure=https://localhost:443 \ https://localhost:443 elif command -v firefox-esr >/dev/null 2>&1; then echo "Starte Firefox ESR mit HTTPS und Auflösung ${WIDTH}x${HEIGHT}..." # Firefox-Profil für HTTPS Kiosk erstellen mkdir -p /home/kiosk/.mozilla/firefox/kiosk.default cat > /home/kiosk/.mozilla/firefox/kiosk.default/user.js << FIREFOXEOF user_pref("browser.shell.checkDefaultBrowser", false); user_pref("browser.startup.homepage", "https://localhost:443"); user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true); user_pref("browser.tabs.warnOnClose", false); user_pref("browser.sessionstore.resume_from_crash", false); user_pref("security.tls.insecure_fallback_hosts", "localhost"); user_pref("security.mixed_content.block_active_content", false); user_pref("security.mixed_content.block_display_content", false); user_pref("browser.cache.disk.enable", false); user_pref("browser.cache.memory.enable", true); user_pref("browser.cache.offline.enable", false); user_pref("network.http.use-cache", false); user_pref("browser.fullscreen.autohide", true); user_pref("dom.disable_open_during_load", false); user_pref("privacy.popups.disable_from_plugins", 0); user_pref("dom.popup_maximum", 0); user_pref("security.cert_pinning.enforcement_level", 0); user_pref("security.tls.unrestricted_rc4_fallback", true); FIREFOXEOF # Firefox CSS für randlosen Vollbildmodus mkdir -p /home/kiosk/.mozilla/firefox/kiosk.default/chrome cat > /home/kiosk/.mozilla/firefox/kiosk.default/chrome/userChrome.css << FIREFOXCSS /* Firefox Kiosk Mode - Hide all UI elements */ #navigator-toolbox { display: none !important; } #sidebar-box { display: none !important; } #urlbar-container { display: none !important; } #TabsToolbar { display: none !important; } #nav-bar { display: none !important; } #PersonalToolbar { display: none !important; } .toolbar-items { display: none !important; } #main-window[sizemode="fullscreen"] .tabbrowser-tab { visibility: collapse !important; } FIREFOXCSS exec firefox-esr \ --kiosk \ --width=${WIDTH} \ --height=${HEIGHT} \ --profile /home/kiosk/.mozilla/firefox/kiosk.default \ https://localhost:443 else echo "FEHLER: Kein Browser verfügbar!" exit 1 fi EOF # Berechtigungen setzen chmod +x "$KIOSK_HOME/.xinitrc" chown -R "$KIOSK_USER:$KIOSK_USER" "$KIOSK_HOME" log "✅ Kiosk-Browser mit HTTPS-Vollbildmodus konfiguriert" } # ========================== APP.PY SSL-UNTERSTÜTZUNG PRÜFEN ========================== verify_app_ssl_support() { log "=== PRÜFE APP.PY SSL-UNTERSTÜTZUNG ===" if [ ! -f "$APP_DIR/app.py" ]; then error "app.py nicht gefunden in $APP_DIR" fi # Prüfe ob get_ssl_context Funktion existiert if grep -q "def get_ssl_context" "$APP_DIR/app.py"; then log "✅ get_ssl_context Funktion bereits vorhanden in app.py" else progress "Füge SSL-Unterstützung zu app.py hinzu..." # Backup der originalen app.py cp "$APP_DIR/app.py" "$APP_DIR/app.py.backup.$(date +%s)" # SSL-Funktion am Ende der Datei hinzufügen (vor dem if __name__ == '__main__' Block) cat >> "$APP_DIR/app.py" << 'EOF' def get_ssl_context(): """ SSL-Kontext für HTTPS-Server erstellen Verwendet localhost-Zertifikate falls verfügbar """ import os ssl_cert_path = '/opt/myp/certs/localhost/localhost.crt' ssl_key_path = '/opt/myp/certs/localhost/localhost.key' if os.path.exists(ssl_cert_path) and os.path.exists(ssl_key_path): try: context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) context.load_cert_chain(ssl_cert_path, ssl_key_path) context.check_hostname = False context.verify_mode = ssl.CERT_NONE print(f"✅ SSL-Kontext erstellt mit: {ssl_cert_path}") return context except Exception as e: print(f"❌ Fehler beim Erstellen des SSL-Kontexts: {e}") return None else: print(f"❌ SSL-Zertifikate nicht gefunden:") print(f" Cert: {ssl_cert_path}") print(f" Key: {ssl_key_path}") return None EOF log "✅ SSL-Unterstützung zu app.py hinzugefügt" fi # Prüfe ob SSL-Imports vorhanden sind if ! grep -q "import ssl" "$APP_DIR/app.py"; then progress "Füge SSL-Import zu app.py hinzu..." sed -i '1i import ssl' "$APP_DIR/app.py" fi log "✅ app.py SSL-Unterstützung verifiziert" } # ========================== PRODUKTIONS-KIOSK SETUP ========================== setup_production_kiosk() { log "=== RICHTE PRODUKTIONS-KIOSK-MODUS MIT HTTPS EIN ===" # 1. System-Abhängigkeiten installieren install_system_dependencies # 2. Desktop-Environments entfernen remove_desktop_environments # 3. Minimale X11-Umgebung installieren install_minimal_x11 # 4. Kiosk-Benutzer erstellen create_kiosk_user # 5. Anwendung kopieren progress "Erstelle Zielverzeichnis /opt/myp..." mkdir -p "$APP_DIR" || error "Konnte Zielverzeichnis nicht erstellen" progress "Kopiere Projektdateien selektiv nach $APP_DIR..." # Liste der zu kopierenden Dateien/Ordner (ohne unnötige Inhalte) declare -a COPY_ITEMS=( "app.py" "models.py" "requirements.txt" "blueprints/" "config/" "database/" "docs/" "static/" "templates/" "uploads/" "utils/" "logs/" "certs/" ) # Sichere selektive Kopie for item in "${COPY_ITEMS[@]}"; do if [ -e "$CURRENT_DIR/$item" ]; then progress "Kopiere: $item" cp -r "$CURRENT_DIR/$item" "$APP_DIR/" || warning "Fehler beim Kopieren von $item" else info "Überspringe nicht vorhandenes Element: $item" fi done # Spezielle Dateien einzeln kopieren (falls vorhanden) for file in "package.json" "package-lock.json" "tailwind.config.js" "postcss.config.js"; do if [ -f "$CURRENT_DIR/$file" ]; then cp "$CURRENT_DIR/$file" "$APP_DIR/" || warning "Fehler beim Kopieren von $file" fi done # Stelle sicher, dass app.py ausführbar ist chmod +x "$APP_DIR/app.py" || error "Konnte app.py nicht ausführbar machen" # Erstelle notwendige Verzeichnisse falls sie nicht existieren mkdir -p "$APP_DIR/database/backups" mkdir -p "$APP_DIR/logs/app" mkdir -p "$APP_DIR/logs/auth" mkdir -p "$APP_DIR/logs/errors" mkdir -p "$APP_DIR/uploads/temp" mkdir -p "$APP_DIR/certs/localhost" # Berechtigungen setzen chown -R root:root "$APP_DIR" chmod -R 755 "$APP_DIR" chmod 750 "$APP_DIR/database" chmod 750 "$APP_DIR/logs" chmod 755 "$APP_DIR/uploads" chmod 750 "$APP_DIR/certs" # 6. App.py SSL-Unterstützung prüfen und hinzufügen verify_app_ssl_support # 7. SSL-Zertifikate für localhost erstellen create_localhost_ssl_certificates # 8. Backend-Service erstellen create_backend_service # 9. Autologin konfigurieren configure_autologin # 10. Kiosk-Browser konfigurieren configure_kiosk_browser # 11. Service aktivieren und starten progress "Lade Systemd-Konfiguration neu..." systemctl daemon-reload || error "Systemd Reload fehlgeschlagen" progress "Aktiviere HTTPS Backend-Service..." systemctl enable "$SERVICE_NAME.service" || error "HTTPS-Service Enable fehlgeschlagen" progress "Starte HTTPS Backend-Service..." systemctl start "$SERVICE_NAME.service" || error "HTTPS-Service Start fehlgeschlagen" # Service-Status prüfen sleep 5 info "=== SERVICE-STATUS ===" if systemctl is-active --quiet "$SERVICE_NAME.service"; then log "✅ $SERVICE_NAME Service läuft erfolgreich" else warning "⚠️ $SERVICE_NAME Service läuft nicht - prüfen Sie die Logs: journalctl -u $SERVICE_NAME -f" fi # Backend-Tests progress "Teste HTTPS Backend-Erreichbarkeit..." sleep 3 if curl -k -s https://localhost:443 > /dev/null 2>&1; then log "✅ Port 443 (HTTPS) erreichbar" else warning "⚠️ Port 443 (HTTPS) nicht erreichbar" # Zusätzliche Debug-Information progress "Versuche SSL-Zertifikat zu testen..." if openssl s_client -connect localhost:443 -servername localhost < /dev/null 2>/dev/null | grep -q "CONNECTED"; then log "✅ SSL-Verbindung funktioniert" else warning "⚠️ SSL-Verbindung fehlgeschlagen" fi fi # SSL-Zertifikat Status if [ -f "$APP_DIR/certs/localhost/localhost.crt" ]; then log "✅ SSL-Zertifikat vorhanden: $APP_DIR/certs/localhost/localhost.crt" CERT_EXPIRY=$(openssl x509 -in "$APP_DIR/certs/localhost/localhost.crt" -noout -enddate | cut -d= -f2) log "📅 Zertifikat läuft ab: $CERT_EXPIRY" else warning "⚠️ SSL-Zertifikat nicht gefunden" fi log "✅ PRODUKTIONS-KIOSK-MODUS MIT HTTPS ERFOLGREICH EINGERICHTET" log "" log "🚀 WICHTIG: NEUSTART ERFORDERLICH!" log " sudo reboot" log "" log "📊 NACH DEM NEUSTART:" log " • Automatischer Login als Benutzer: $KIOSK_USER" log " • Automatischer X-Start und Chromium-Kiosk" log " • Backend läuft auf HTTPS:" log " - https://localhost:443 (Kiosk-Anzeige mit SSL)" log " - https://0.0.0.0:443 (Netzwerk-Zugriff)" log "" log "🔐 SSL-ZERTIFIKATE:" log " • Self-Signed Zertifikat für localhost erstellt" log " • Chromium akzeptiert Zertifikat automatisch" log " • Zertifikat im System CA-Store installiert" log "" log "🔧 SERVICE-BEFEHLE:" log " • Status: sudo systemctl status $SERVICE_NAME" log " • Logs: sudo journalctl -u $SERVICE_NAME -f" log " • Restart: sudo systemctl restart $SERVICE_NAME" log " • SSL-Test: curl -k https://localhost:443" log "" warning "🔄 FÜHRE JETZT 'sudo reboot' AUS, UM DEN HTTPS-KIOSK-MODUS ZU AKTIVIEREN!" } # ========================== HAUPTMENÜ ========================== show_menu() { clear echo -e "${BLUE}=================================================================${NC}" echo -e "${GREEN} $APP_NAME - HTTPS KIOSK-INSTALLER${NC}" echo -e "${BLUE}=================================================================${NC}" echo "" echo -e "${YELLOW}Aktuelles Verzeichnis:${NC} $CURRENT_DIR" echo -e "${YELLOW}Systemzeit:${NC} $(date)" echo -e "${YELLOW}Zielverzeichnis:${NC} $APP_DIR" echo -e "${YELLOW}Kiosk-Benutzer:${NC} $KIOSK_USER" echo -e "${YELLOW}HTTPS-Service:${NC} $SERVICE_NAME" echo "" echo -e "${PURPLE}Wählen Sie eine Option:${NC}" echo "" echo -e "${GREEN}1)${NC} System vorbereiten (Abhängigkeiten installieren)" echo -e " → Installiert Python 3, pip und alle benötigten Pakete" echo -e " → Verwendet: pip install --break-system-packages" echo -e " → Mercedes SSL-Zertifikate werden konfiguriert" echo -e " → Node.js und npm für Frontend-Build" echo "" echo -e "${GREEN}2)${NC} VOLLSTÄNDIGER HTTPS KIOSK-MODUS installieren" echo -e " → ${RED}ENTFERNT ALLE DESKTOP-ENVIRONMENTS!${NC}" echo -e " → Installiert minimale X11-Umgebung" echo -e " → Erstellt Self-Signed SSL-Zertifikate für localhost" echo -e " → Erstellt HTTPS Backend-Service (Port 443)" echo -e " → Konfiguriert Autologin und HTTPS Kiosk-Browser" echo -e " → Browser öffnet: ${BLUE}https://localhost:443${NC}" echo -e " → ${YELLOW}NEUSTART ERFORDERLICH!${NC}" echo "" echo -e "${RED}0)${NC} Beenden" echo "" echo -e "${RED}⚠️ WARNUNG: Option 2 macht Raspberry Pi zu reinem HTTPS-Kiosk-System!${NC}" echo -e "${GREEN}🔐 HTTPS: Automatische SSL-Zertifikat-Generierung für localhost${NC}" echo -e "${BLUE}=================================================================${NC}" echo -n "Ihre Wahl [0-2]: " } # ========================== MAIN LOGIC ========================== main() { # System-Checks check_root check_debian_system # Erstelle Log-Datei mkdir -p "$(dirname "$INSTALL_LOG")" touch "$INSTALL_LOG" log "=== MYP HTTPS KIOSK-INSTALLER GESTARTET ===" log "Arbeitsverzeichnis: $CURRENT_DIR" log "Zielverzeichnis: $APP_DIR" log "HTTPS-Service: $SERVICE_NAME" log "Kiosk-Benutzer: $KIOSK_USER" log "SSL-Zertifikate: $APP_DIR/certs/localhost/" log "System: $(uname -a)" log "Debian-Version: $(cat /etc/debian_version 2>/dev/null || echo 'Unbekannt')" while true; do show_menu read -r choice case $choice in 1) clear log "=== OPTION 1: SYSTEM VORBEREITEN ===" install_system_dependencies echo "" echo -e "${GREEN}✅ System-Vorbereitung abgeschlossen!${NC}" echo -e "${YELLOW}Drücken Sie Enter, um fortzufahren...${NC}" read -r ;; 2) clear echo -e "${RED}⚠️ WARNUNG: Sie sind dabei, alle Desktop-Environments zu entfernen!${NC}" echo -e "${YELLOW}Der Raspberry Pi wird zu einem reinen HTTPS-Kiosk-System umgebaut.${NC}" echo -e "${BLUE}Nach der Installation startet automatisch der HTTPS-Kiosk-Browser.${NC}" echo -e "${GREEN}SSL-Zertifikate für localhost werden automatisch generiert.${NC}" echo "" echo -n "Sind Sie sicher? [ja/NEIN]: " read -r confirm if [ "$confirm" = "ja" ] || [ "$confirm" = "JA" ]; then clear log "=== OPTION 2: VOLLSTÄNDIGER HTTPS KIOSK-MODUS ===" setup_production_kiosk echo "" echo -e "${GREEN}✅ HTTPS KIOSK-MODUS ERFOLGREICH EINGERICHTET!${NC}" echo -e "${RED}🔄 NEUSTART JETZT ERFORDERLICH: sudo reboot${NC}" echo -e "${BLUE}🔐 HTTPS-URL: https://localhost:443${NC}" echo -e "${YELLOW}Drücken Sie Enter, um fortzufahren...${NC}" read -r else echo -e "${BLUE}Installation abgebrochen.${NC}" sleep 2 fi ;; 0) log "=== HTTPS KIOSK-INSTALLER BEENDET ===" echo -e "${GREEN}Auf Wiedersehen!${NC}" echo -e "${BLUE}Log-Datei: $INSTALL_LOG${NC}" exit 0 ;; *) echo -e "${RED}Ungültige Eingabe. Bitte wählen Sie 0-2.${NC}" sleep 2 ;; esac done } # Script starten main "$@"