3154 lines
108 KiB
Bash
3154 lines
108 KiB
Bash
#!/bin/bash
|
||
|
||
# ===================================================================
|
||
# MYP Druckerverwaltung - KONSOLIDIERTES SETUP-SKRIPT (OPTIMIERT)
|
||
# Kombiniert alle Installationsfunktionen in einer einzigen Datei
|
||
# Optimiert für Debian/Linux (Raspberry Pi OS) - KEIN Windows-Support
|
||
# HTTPS auf Port 443 mit automatischer SSL-Zertifikat-Generierung
|
||
# Kiosk-Modus mit Chromium-Autostart ohne Desktop-Environment
|
||
# Version: 4.1.0 - Robuste Installation mit Retry-Mechanismen
|
||
# ===================================================================
|
||
|
||
set -euo pipefail
|
||
|
||
# =========================== GLOBALE KONFIGURATION ===========================
|
||
readonly APP_NAME="MYP Druckerverwaltung"
|
||
readonly APP_VERSION="4.1.0"
|
||
readonly APP_DIR="/opt/myp"
|
||
readonly HTTPS_SERVICE_NAME="myp-https"
|
||
readonly KIOSK_SERVICE_NAME="myp-kiosk"
|
||
readonly WATCHDOG_SERVICE_NAME="kiosk-watchdog"
|
||
readonly WATCHDOG_PYTHON_SERVICE_NAME="kiosk-watchdog-python"
|
||
readonly FIREWALL_SERVICE_NAME="myp-firewall"
|
||
readonly KIOSK_USER="kiosk"
|
||
readonly CURRENT_DIR="$(pwd)"
|
||
readonly INSTALL_LOG="logs/myp-install.log"
|
||
readonly ERROR_LOG="logs/myp-install-errors.log"
|
||
readonly WARNING_LOG="logs/myp-install-warnings.log"
|
||
readonly DEBUG_LOG="logs/myp-install-debug.log"
|
||
readonly HTTPS_PORT="443"
|
||
readonly HTTPS_URL="https://localhost:${HTTPS_PORT}"
|
||
readonly SYSTEMD_DIR="$CURRENT_DIR/systemd"
|
||
readonly SYSTEM_SYSTEMD_DIR="/etc/systemd/system"
|
||
|
||
# Retry-Konfiguration
|
||
readonly MAX_RETRIES=3
|
||
readonly RETRY_DELAY=5
|
||
|
||
# Farben für Ausgabe
|
||
readonly RED='\033[0;31m'
|
||
readonly GREEN='\033[0;32m'
|
||
readonly YELLOW='\033[1;33m'
|
||
readonly BLUE='\033[0;34m'
|
||
readonly PURPLE='\033[0;35m'
|
||
readonly CYAN='\033[0;36m'
|
||
readonly NC='\033[0m'
|
||
|
||
# =========================== VERBESSERTE LOGGING-FUNKTIONEN ===========================
|
||
# Globale Variablen für Fehler-Tracking
|
||
ERROR_COUNT=0
|
||
WARNING_COUNT=0
|
||
|
||
# Log-Dateien initialisieren
|
||
init_logging() {
|
||
# Erstelle logs-Verzeichnis falls nötig
|
||
mkdir -p "logs" 2>/dev/null || true
|
||
|
||
# Initialisiere alle Log-Dateien
|
||
{
|
||
echo "================================================================="
|
||
echo "MYP Installation Log - $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "Script Version: $APP_VERSION"
|
||
echo "System: $(uname -a)"
|
||
echo "================================================================="
|
||
echo ""
|
||
} > "$INSTALL_LOG"
|
||
|
||
{
|
||
echo "================================================================="
|
||
echo "MYP Installation FEHLER Log - $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "================================================================="
|
||
echo ""
|
||
} > "$ERROR_LOG"
|
||
|
||
{
|
||
echo "================================================================="
|
||
echo "MYP Installation WARNUNGEN Log - $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "================================================================="
|
||
echo ""
|
||
} > "$WARNING_LOG"
|
||
|
||
{
|
||
echo "================================================================="
|
||
echo "MYP Installation DEBUG Log - $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "================================================================="
|
||
echo ""
|
||
} > "$DEBUG_LOG"
|
||
}
|
||
|
||
log() {
|
||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||
echo -e "${GREEN}${message}${NC}" | tee -a "$INSTALL_LOG"
|
||
}
|
||
|
||
error() {
|
||
local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||
local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}"
|
||
local message="[FEHLER] $1"
|
||
|
||
# Erhöhe Fehler-Zähler
|
||
((ERROR_COUNT++))
|
||
|
||
# Ausgabe auf Konsole
|
||
echo -e "${RED}${message}${NC}" | tee -a "$INSTALL_LOG"
|
||
|
||
# Detaillierte Fehler-Information in Fehler-Log
|
||
{
|
||
echo "[$timestamp] FEHLER #$ERROR_COUNT"
|
||
echo "Quelle: $caller"
|
||
echo "Nachricht: $1"
|
||
echo "Arbeitsverzeichnis: $(pwd)"
|
||
echo "Benutzer: $(whoami)"
|
||
echo "---"
|
||
echo ""
|
||
} >> "$ERROR_LOG"
|
||
|
||
# Debug-Informationen sammeln
|
||
{
|
||
echo "[$timestamp] FEHLER AUFGETRETEN - Debug-Info:"
|
||
echo "Caller: $caller"
|
||
echo "PWD: $(pwd)"
|
||
echo "User: $(whoami)"
|
||
echo "Disk Space: $(df -h / | tail -1)"
|
||
echo "Memory: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}')"
|
||
echo "Load Average: $(uptime | awk -F'load average:' '{print $2}')"
|
||
echo "Recent commands from history:"
|
||
history | tail -5 2>/dev/null || echo "History nicht verfügbar"
|
||
echo "==============================================="
|
||
echo ""
|
||
} >> "$DEBUG_LOG"
|
||
|
||
exit 1
|
||
}
|
||
|
||
warning() {
|
||
local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||
local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}"
|
||
local message="[WARNUNG] $1"
|
||
|
||
# Erhöhe Warnungs-Zähler
|
||
((WARNING_COUNT++))
|
||
|
||
# Ausgabe auf Konsole
|
||
echo -e "${YELLOW}${message}${NC}" | tee -a "$INSTALL_LOG"
|
||
|
||
# Detaillierte Warnungs-Information in Warnungs-Log
|
||
{
|
||
echo "[$timestamp] WARNUNG #$WARNING_COUNT"
|
||
echo "Quelle: $caller"
|
||
echo "Nachricht: $1"
|
||
echo "---"
|
||
echo ""
|
||
} >> "$WARNING_LOG"
|
||
}
|
||
|
||
info() {
|
||
echo -e "${BLUE}[INFO] $1${NC}" | tee -a "$INSTALL_LOG"
|
||
}
|
||
|
||
progress() {
|
||
echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG"
|
||
}
|
||
|
||
success() {
|
||
echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG"
|
||
}
|
||
|
||
debug() {
|
||
local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
|
||
local caller="${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]}"
|
||
|
||
# Debug sowohl in normales Log als auch Debug-Log
|
||
echo -e "${BLUE}[DEBUG] $1${NC}" >> "$INSTALL_LOG"
|
||
|
||
{
|
||
echo "[$timestamp] DEBUG von $caller"
|
||
echo "$1"
|
||
echo "---"
|
||
echo ""
|
||
} >> "$DEBUG_LOG"
|
||
}
|
||
|
||
# Fehler-Zusammenfassung anzeigen
|
||
show_error_summary() {
|
||
echo ""
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
echo -e "${CYAN} INSTALLATION ABGESCHLOSSEN - FEHLER-ZUSAMMENFASSUNG${NC}"
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
echo ""
|
||
|
||
echo -e "${BLUE}📊 Statistiken:${NC}"
|
||
echo -e " ${RED}❌ Fehler: $ERROR_COUNT${NC}"
|
||
echo -e " ${YELLOW}⚠️ Warnungen: $WARNING_COUNT${NC}"
|
||
echo ""
|
||
|
||
echo -e "${BLUE}📁 Log-Dateien:${NC}"
|
||
echo -e " 📄 Vollständiges Log: $INSTALL_LOG"
|
||
echo -e " 🚨 Fehler-Log: $ERROR_LOG"
|
||
echo -e " ⚠️ Warnungs-Log: $WARNING_LOG"
|
||
echo -e " 🔍 Debug-Log: $DEBUG_LOG"
|
||
echo ""
|
||
|
||
if [ $ERROR_COUNT -gt 0 ]; then
|
||
echo -e "${RED}⚠️ Es sind $ERROR_COUNT Fehler aufgetreten!${NC}"
|
||
echo -e "${RED} Bitte prüfen Sie: $ERROR_LOG${NC}"
|
||
echo ""
|
||
|
||
# Zeige die letzten 3 Fehler an
|
||
if [ -f "$ERROR_LOG" ] && [ -s "$ERROR_LOG" ]; then
|
||
echo -e "${RED}🔍 Letzte Fehler:${NC}"
|
||
tail -n 20 "$ERROR_LOG" | head -n 15
|
||
echo ""
|
||
fi
|
||
fi
|
||
|
||
if [ $WARNING_COUNT -gt 0 ]; then
|
||
echo -e "${YELLOW}⚠️ Es sind $WARNING_COUNT Warnungen aufgetreten${NC}"
|
||
echo -e "${YELLOW} Bitte prüfen Sie: $WARNING_LOG${NC}"
|
||
echo ""
|
||
fi
|
||
|
||
if [ $ERROR_COUNT -eq 0 ] && [ $WARNING_COUNT -eq 0 ]; then
|
||
echo -e "${GREEN}✅ Installation ohne Fehler oder Warnungen abgeschlossen!${NC}"
|
||
elif [ $ERROR_COUNT -eq 0 ]; then
|
||
echo -e "${GREEN}✅ Installation erfolgreich mit $WARNING_COUNT Warnungen${NC}"
|
||
fi
|
||
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
|
||
# Erstelle automatische Log-Zusammenfassung
|
||
create_log_summary
|
||
}
|
||
|
||
# Automatische Log-Zusammenfassung erstellen
|
||
create_log_summary() {
|
||
local summary_file="logs/myp-install-summary.txt"
|
||
|
||
{
|
||
echo "================================================================="
|
||
echo "MYP INSTALLATION ZUSAMMENFASSUNG"
|
||
echo "Erstellt: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "================================================================="
|
||
echo ""
|
||
echo "STATISTIKEN:"
|
||
echo "- Fehler: $ERROR_COUNT"
|
||
echo "- Warnungen: $WARNING_COUNT"
|
||
echo "- Script Version: $APP_VERSION"
|
||
echo "- System: $(uname -a 2>/dev/null || echo 'Unbekannt')"
|
||
echo "- Hostname: $(hostname 2>/dev/null || echo 'Unbekannt')"
|
||
echo "- User: $(whoami 2>/dev/null || echo 'Unbekannt')"
|
||
echo ""
|
||
echo "SYSTEM-INFORMATIONEN:"
|
||
echo "- Festplattenspeicher: $(df -h / | tail -1 2>/dev/null || echo 'Nicht verfügbar')"
|
||
echo "- Arbeitsspeicher: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}' 2>/dev/null || echo 'Nicht verfügbar')"
|
||
echo "- Python Version: $(python3 --version 2>&1 || echo 'Nicht installiert')"
|
||
echo "- Node.js Version: $(node --version 2>&1 || echo 'Nicht installiert')"
|
||
echo ""
|
||
echo "LOG-DATEIEN:"
|
||
echo "- Vollständiges Log: $INSTALL_LOG"
|
||
echo "- Fehler-Log: $ERROR_LOG"
|
||
echo "- Warnungs-Log: $WARNING_LOG"
|
||
echo "- Debug-Log: $DEBUG_LOG"
|
||
echo ""
|
||
|
||
if [ $ERROR_COUNT -gt 0 ] && [ -f "$ERROR_LOG" ] && [ -s "$ERROR_LOG" ]; then
|
||
echo "FEHLER-ÜBERSICHT:"
|
||
echo "================="
|
||
tail -n 50 "$ERROR_LOG"
|
||
echo ""
|
||
fi
|
||
|
||
if [ $WARNING_COUNT -gt 0 ] && [ -f "$WARNING_LOG" ] && [ -s "$WARNING_LOG" ]; then
|
||
echo "WARNUNGS-ÜBERSICHT:"
|
||
echo "==================="
|
||
tail -n 30 "$WARNING_LOG"
|
||
echo ""
|
||
fi
|
||
|
||
echo "INSTALLATION ABGESCHLOSSEN: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "================================================================="
|
||
|
||
} > "$summary_file"
|
||
|
||
# Berechtigung für Zusammenfassungs-Datei setzen
|
||
chmod 644 "$summary_file" 2>/dev/null || true
|
||
|
||
debug "Log-Zusammenfassung erstellt: $summary_file"
|
||
}
|
||
|
||
# =========================== RETRY-MECHANISMEN ===========================
|
||
retry_command() {
|
||
local cmd="$1"
|
||
local description="$2"
|
||
local attempts=0
|
||
|
||
while [ $attempts -lt $MAX_RETRIES ]; do
|
||
if eval "$cmd"; then
|
||
return 0
|
||
fi
|
||
|
||
attempts=$((attempts + 1))
|
||
if [ $attempts -lt $MAX_RETRIES ]; then
|
||
warning "$description fehlgeschlagen (Versuch $attempts/$MAX_RETRIES) - wiederhole in ${RETRY_DELAY}s..."
|
||
|
||
# Debug-Information für jeden fehlgeschlagenen Versuch
|
||
debug "Retry-Versuch für '$description': $attempts/$MAX_RETRIES"
|
||
debug "Fehlgeschlagener Befehl: $cmd"
|
||
debug "Aktuelles Arbeitsverzeichnis: $(pwd)"
|
||
debug "Verfügbarer Speicher: $(free -m | grep '^Mem:' | awk '{print $3"/"$2" MB"}' 2>/dev/null || echo 'Unbekannt')"
|
||
debug "Exit-Code des letzten Befehls: $?"
|
||
|
||
sleep $RETRY_DELAY
|
||
fi
|
||
done
|
||
|
||
# Detaillierte Fehler-Information vor dem Beenden
|
||
debug "CRITICAL: Retry-Mechanismus erschöpft für '$description'"
|
||
debug "Letzter Befehl: $cmd"
|
||
debug "Versuche: $MAX_RETRIES"
|
||
debug "System-Status zum Zeitpunkt des kritischen Fehlers:"
|
||
debug " - Disk Usage: $(df -h / | tail -1 2>/dev/null || echo 'Nicht verfügbar')"
|
||
debug " - Memory Usage: $(free -m 2>/dev/null || echo 'Nicht verfügbar')"
|
||
debug " - Load Average: $(uptime 2>/dev/null | awk -F'load average:' '{print $2}' || echo 'Nicht verfügbar')"
|
||
debug " - Network Status: $(ip addr show 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' || echo 'Nicht verfügbar')"
|
||
debug " - APT Status: $(ps aux | grep -i apt | grep -v grep || echo 'Keine APT-Prozesse')"
|
||
debug " - Python Status: $(python3 --version 2>&1 || echo 'Python nicht verfügbar')"
|
||
|
||
error "$description nach $MAX_RETRIES Versuchen fehlgeschlagen!"
|
||
}
|
||
|
||
# APT-Pakete mit Retry installieren
|
||
apt_install_retry() {
|
||
local packages="$*"
|
||
local cmd="DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $packages"
|
||
|
||
progress "Installiere Pakete: $packages"
|
||
retry_command "$cmd" "APT Installation für: $packages"
|
||
}
|
||
|
||
# =========================== ERWEITERTE SYSTEM-VALIDIERUNG ===========================
|
||
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"
|
||
log "✅ Root-Berechtigung bestätigt"
|
||
}
|
||
|
||
check_system_resources() {
|
||
log "=== SYSTEM-RESSOURCEN PRÜFUNG ==="
|
||
|
||
# RAM prüfen
|
||
local ram_mb=$(free -m | awk '/^Mem:/{print $2}')
|
||
progress "Verfügbarer RAM: ${ram_mb}MB"
|
||
|
||
if [ -n "$ram_mb" ] && [ "$ram_mb" -eq "$ram_mb" ] 2>/dev/null && [ "$ram_mb" -lt 512 ]; then
|
||
warning "⚠️ Wenig RAM verfügbar (${ram_mb}MB) - Installation könnte langsam sein"
|
||
elif [ -n "$ram_mb" ] && [ "$ram_mb" -eq "$ram_mb" ] 2>/dev/null; then
|
||
success "✅ Ausreichend RAM verfügbar (${ram_mb}MB)"
|
||
else
|
||
warning "⚠️ RAM-Größe konnte nicht ermittelt werden"
|
||
fi
|
||
|
||
# Festplattenplatz prüfen
|
||
local disk_free_gb=$(df / | awk 'NR==2{printf "%.1f", $4/1024/1024}')
|
||
progress "Verfügbarer Festplattenplatz: ${disk_free_gb}GB"
|
||
|
||
# Konvertiere zu Ganzzahl für Vergleich (GB * 10 für eine Dezimalstelle)
|
||
local disk_free_int=$(echo "$disk_free_gb * 10" | bc 2>/dev/null | cut -d. -f1)
|
||
local min_required_int=20 # 2.0 GB * 10
|
||
|
||
if [ -z "$disk_free_int" ] || [ "$disk_free_int" -lt "$min_required_int" ]; then
|
||
warning "⚠️ Wenig Festplattenplatz verfügbar (${disk_free_gb}GB)"
|
||
else
|
||
success "✅ Ausreichend Festplattenplatz verfügbar (${disk_free_gb}GB)"
|
||
fi
|
||
|
||
# CPU prüfen
|
||
local cpu_count=$(nproc)
|
||
local cpu_model=$(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)
|
||
progress "CPU: $cpu_count Kern(e) - $cpu_model"
|
||
|
||
log "✅ System-Ressourcen validiert"
|
||
}
|
||
|
||
check_debian_system() {
|
||
if [ ! -f /etc/debian_version ]; then
|
||
error "Dieses Skript ist nur für Debian/Raspbian-Systeme geeignet!"
|
||
fi
|
||
|
||
local debian_version=$(cat /etc/debian_version 2>/dev/null || echo "Unbekannt")
|
||
log "✅ Debian/Raspbian-System erkannt (Version: $debian_version)"
|
||
|
||
# Prüfe auf Raspberry Pi
|
||
if [ -f /proc/device-tree/model ]; then
|
||
local pi_model=$(cat /proc/device-tree/model 2>/dev/null | tr -d '\0' || echo "Unbekannt")
|
||
info "Raspberry Pi Modell: $pi_model"
|
||
|
||
# Raspberry Pi spezifische Optimierungen
|
||
if [[ "$pi_model" =~ "Raspberry Pi" ]]; then
|
||
progress "Aktiviere Raspberry Pi spezifische Optimierungen..."
|
||
export RASPBERRY_PI_DETECTED=1
|
||
fi
|
||
fi
|
||
|
||
# Architektur prüfen
|
||
local arch=$(uname -m)
|
||
info "System-Architektur: $arch"
|
||
|
||
# Kernel-Version prüfen
|
||
local kernel=$(uname -r)
|
||
info "Kernel-Version: $kernel"
|
||
}
|
||
|
||
check_internet_connection() {
|
||
progress "Prüfe Internetverbindung (erweiterte Methoden)..."
|
||
|
||
local connection_ok=false
|
||
local test_method=""
|
||
|
||
# Methode 1: DNS-Auflösung zuerst (schnellster Test)
|
||
progress "Teste DNS-Auflösung..."
|
||
local dns_hosts=("google.com" "cloudflare.com" "github.com" "8.8.8.8")
|
||
|
||
for host in "${dns_hosts[@]}"; do
|
||
if nslookup "$host" >/dev/null 2>&1; then
|
||
connection_ok=true
|
||
test_method="DNS-Auflösung (nslookup: $host)"
|
||
break
|
||
elif getent hosts "$host" >/dev/null 2>&1; then
|
||
connection_ok=true
|
||
test_method="DNS-Auflösung (getent: $host)"
|
||
break
|
||
fi
|
||
done
|
||
|
||
# Methode 2: HTTP/HTTPS-Tests
|
||
if [ "$connection_ok" = false ]; then
|
||
progress "Teste HTTP/HTTPS-Verbindungen..."
|
||
local http_urls=("http://detectportal.firefox.com/success.txt" "https://www.google.com" "http://httpbin.org/get")
|
||
|
||
for url in "${http_urls[@]}"; do
|
||
if command -v curl >/dev/null 2>&1; then
|
||
if curl -s --connect-timeout 10 --max-time 15 "$url" >/dev/null 2>&1; then
|
||
connection_ok=true
|
||
test_method="HTTP/HTTPS (curl: $url)"
|
||
break
|
||
fi
|
||
elif command -v wget >/dev/null 2>&1; then
|
||
if wget -q --timeout=10 --tries=1 "$url" -O /dev/null 2>/dev/null; then
|
||
connection_ok=true
|
||
test_method="HTTP/HTTPS (wget: $url)"
|
||
break
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Methode 3: APT-Repository-Test
|
||
if [ "$connection_ok" = false ]; then
|
||
progress "Teste APT-Repository-Zugang..."
|
||
if apt-get update -qq 2>/dev/null; then
|
||
connection_ok=true
|
||
test_method="APT-Repository-Zugang"
|
||
fi
|
||
fi
|
||
|
||
# Ergebnis-Bewertung
|
||
if [ "$connection_ok" = true ]; then
|
||
success "✅ Internetverbindung verfügbar"
|
||
info " 🔍 Erkannt via: $test_method"
|
||
|
||
# Zusätzliche Informationen
|
||
if command -v curl >/dev/null 2>&1; then
|
||
local external_ip=$(curl -s --connect-timeout 5 ifconfig.me 2>/dev/null || curl -s --connect-timeout 5 ipinfo.io/ip 2>/dev/null || echo "Unbekannt")
|
||
if [ "$external_ip" != "Unbekannt" ]; then
|
||
info " 🌐 Externe IP: $external_ip"
|
||
fi
|
||
fi
|
||
else
|
||
warning "⚠️ Keine Internetverbindung erkannt"
|
||
info " → Installation wird fortgesetzt, aber Downloads könnten fehlschlagen"
|
||
warning " → Bitte prüfen Sie die Netzwerkverbindung!"
|
||
fi
|
||
}
|
||
|
||
# =========================== ROBUSTE SYSTEM-VORBEREITUNG ===========================
|
||
update_system() {
|
||
log "=== ROBUSTE SYSTEM-UPDATE ==="
|
||
|
||
progress "Konfiguriere APT für bessere Zuverlässigkeit..."
|
||
|
||
# APT-Konfiguration optimieren
|
||
cat > /etc/apt/apt.conf.d/99myp-optimized << 'EOF'
|
||
APT::Acquire::Retries "3";
|
||
APT::Acquire::http::Timeout "30";
|
||
APT::Acquire::https::Timeout "30";
|
||
APT::Acquire::ftp::Timeout "30";
|
||
APT::Install-Recommends "false";
|
||
APT::Install-Suggests "false";
|
||
Dpkg::Options {
|
||
"--force-confdef";
|
||
"--force-confold";
|
||
}
|
||
EOF
|
||
|
||
# Repository-Listen korrigieren falls nötig
|
||
progress "Validiere APT-Repositories..."
|
||
if [ -f /etc/apt/sources.list ]; then
|
||
# Backup erstellen
|
||
cp /etc/apt/sources.list /etc/apt/sources.list.backup
|
||
|
||
# Prüfe auf problematische Einträge
|
||
if grep -q "deb-src" /etc/apt/sources.list; then
|
||
sed -i 's/^deb-src/#deb-src/g' /etc/apt/sources.list
|
||
log "✅ Source-Repositories deaktiviert (nicht benötigt)"
|
||
fi
|
||
fi
|
||
|
||
progress "Aktualisiere Paketlisten mit Retry..."
|
||
retry_command "apt-get update" "APT Update"
|
||
|
||
progress "Führe System-Upgrade durch..."
|
||
retry_command "DEBIAN_FRONTEND=noninteractive apt-get upgrade -y" "System Upgrade"
|
||
|
||
progress "Installiere essenzielle System-Tools..."
|
||
|
||
# Grundlegende Tools in optimierter Reihenfolge
|
||
local essential_packages=(
|
||
"ca-certificates"
|
||
"gnupg"
|
||
"curl"
|
||
"wget"
|
||
"git"
|
||
"nano"
|
||
"htop"
|
||
"rsync"
|
||
"unzip"
|
||
"sudo"
|
||
"systemd"
|
||
"lsb-release"
|
||
"apt-transport-https"
|
||
"software-properties-common"
|
||
"bc"
|
||
"dbus"
|
||
"systemd-timesyncd"
|
||
)
|
||
|
||
for package in "${essential_packages[@]}"; do
|
||
apt_install_retry "$package"
|
||
done
|
||
|
||
# Zeitserver synchronisieren
|
||
progress "Synchronisiere Systemzeit..."
|
||
systemctl enable systemd-timesyncd 2>/dev/null || true
|
||
systemctl start systemd-timesyncd 2>/dev/null || true
|
||
|
||
log "✅ Robustes System-Update abgeschlossen"
|
||
}
|
||
|
||
# =========================== VERBESSERTE PYTHON-INSTALLATION ===========================
|
||
install_python_dependencies() {
|
||
log "=== ROBUSTE PYTHON-INSTALLATION ==="
|
||
|
||
progress "Installiere Python 3 und Build-Abhängigkeiten..."
|
||
|
||
local python_packages=(
|
||
"python3"
|
||
"python3-pip"
|
||
"python3-dev"
|
||
"python3-setuptools"
|
||
"python3-venv"
|
||
"python3-wheel"
|
||
"build-essential"
|
||
"libssl-dev"
|
||
"libffi-dev"
|
||
"libbz2-dev"
|
||
"libreadline-dev"
|
||
"libsqlite3-dev"
|
||
"libncurses5-dev"
|
||
"libncursesw5-dev"
|
||
"zlib1g-dev"
|
||
"sqlite3"
|
||
)
|
||
|
||
for package in "${python_packages[@]}"; do
|
||
apt_install_retry "$package"
|
||
done
|
||
|
||
# Python-Version validieren
|
||
progress "Validiere Python-Installation..."
|
||
local python_version=$(python3 --version 2>&1 | cut -d' ' -f2)
|
||
log "✅ Python Version: $python_version"
|
||
|
||
# pip konfigurieren und aktualisieren
|
||
progress "Konfiguriere pip für bessere Zuverlässigkeit..."
|
||
|
||
# Root pip-Konfiguration
|
||
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 = 120
|
||
retries = 5
|
||
no-cache-dir = false
|
||
disable-pip-version-check = true
|
||
no-warn-script-location = true
|
||
break-system-packages = true
|
||
|
||
[install]
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
user = false
|
||
break-system-packages = true
|
||
EOF
|
||
|
||
# Systemweite pip-Konfiguration für alle Benutzer
|
||
progress "Erstelle systemweite pip-Konfiguration..."
|
||
mkdir -p /etc/pip
|
||
cat > /etc/pip/pip.conf << 'EOF'
|
||
[global]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
timeout = 120
|
||
retries = 5
|
||
disable-pip-version-check = true
|
||
|
||
[install]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
user = false
|
||
EOF
|
||
|
||
# pip-Konfiguration für existierende Benutzer erstellen
|
||
progress "Konfiguriere pip für alle Benutzer..."
|
||
for user_home in "/home/"*; do
|
||
if [ -d "$user_home" ] && [ "$user_home" != "/home/lost+found" ]; then
|
||
local username=$(basename "$user_home")
|
||
if id "$username" &>/dev/null; then
|
||
mkdir -p "$user_home/.pip" 2>/dev/null || true
|
||
cat > "$user_home/.pip/pip.conf" << 'EOF'
|
||
[global]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
timeout = 60
|
||
retries = 3
|
||
|
||
[install]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
EOF
|
||
chown "$username:$username" "$user_home/.pip/pip.conf" 2>/dev/null || true
|
||
log "✅ pip konfiguriert für Benutzer: $username"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# pip selbst aktualisieren
|
||
progress "Aktualisiere pip mit Retry..."
|
||
retry_command "python3 -m pip install --break-system-packages --upgrade pip setuptools wheel" "pip Upgrade"
|
||
|
||
# pip-Version validieren
|
||
local pip_version=$(python3 -m pip --version | cut -d' ' -f2)
|
||
log "✅ pip Version: $pip_version"
|
||
|
||
log "✅ Robuste Python-Umgebung installiert"
|
||
}
|
||
|
||
install_python_packages() {
|
||
log "=== ROBUSTE PYTHON-PAKETE INSTALLATION ==="
|
||
|
||
progress "Installiere Python-Pakete mit verbesserter Strategie..."
|
||
|
||
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
||
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
||
fi
|
||
|
||
# Kopiere requirements.txt
|
||
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
||
|
||
# Strategie 1: Direkte Installation mit pip
|
||
progress "Versuche direkte requirements.txt Installation..."
|
||
|
||
local pip_opts="--break-system-packages --timeout 120 --retries 5 --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org"
|
||
|
||
if python3 -m pip install $pip_opts -r "$CURRENT_DIR/requirements.txt"; then
|
||
success "✅ requirements.txt erfolgreich installiert"
|
||
else
|
||
warning "⚠️ Direkte Installation fehlgeschlagen - verwende Schritt-für-Schritt Installation"
|
||
|
||
# Strategie 2: Pakete in optimierter Reihenfolge installieren
|
||
progress "Installiere Core-Abhängigkeiten zuerst..."
|
||
|
||
# Grundlegende Abhängigkeiten zuerst
|
||
local core_deps=(
|
||
"MarkupSafe==3.0.2"
|
||
"itsdangerous==2.2.0"
|
||
"click==8.1.7"
|
||
"Werkzeug==3.1.3"
|
||
"Jinja2==3.1.4"
|
||
"Flask==3.1.1"
|
||
)
|
||
|
||
for dep in "${core_deps[@]}"; do
|
||
progress "Installiere: $dep"
|
||
retry_command "python3 -m pip install $pip_opts '$dep'" "$dep Installation"
|
||
done
|
||
|
||
# Flask-Extensions
|
||
progress "Installiere Flask-Extensions..."
|
||
local flask_extensions=(
|
||
"WTForms==3.1.2"
|
||
"Flask-WTF==1.2.1"
|
||
"Flask-Login==0.6.3"
|
||
)
|
||
|
||
for ext in "${flask_extensions[@]}"; do
|
||
progress "Installiere: $ext"
|
||
retry_command "python3 -m pip install $pip_opts '$ext'" "$ext Installation"
|
||
done
|
||
|
||
# Datenbank und Sicherheit
|
||
progress "Installiere Datenbank und Sicherheits-Komponenten..."
|
||
local db_security=(
|
||
"SQLAlchemy==2.0.36"
|
||
"cryptography==44.0.0"
|
||
"bcrypt==4.2.1"
|
||
)
|
||
|
||
for comp in "${db_security[@]}"; do
|
||
progress "Installiere: $comp"
|
||
retry_command "python3 -m pip install $pip_opts '$comp'" "$comp Installation"
|
||
done
|
||
|
||
# System-Abhängigkeiten
|
||
progress "Installiere System-Abhängigkeiten..."
|
||
local system_deps=(
|
||
"requests==2.32.3"
|
||
"psutil==6.1.1"
|
||
"python-dateutil==2.9.0"
|
||
"colorlog==6.9.0"
|
||
"gunicorn==23.0.0"
|
||
)
|
||
|
||
for dep in "${system_deps[@]}"; do
|
||
progress "Installiere: $dep"
|
||
retry_command "python3 -m pip install $pip_opts '$dep'" "$dep Installation" || warning "⚠️ $dep Installation fehlgeschlagen (optional)"
|
||
done
|
||
fi
|
||
|
||
# Umfassende Validierung
|
||
progress "Validiere kritische Python-Abhängigkeiten..."
|
||
|
||
# Erstelle Validierungs-Skript
|
||
cat > /tmp/validate_python.py << 'EOF'
|
||
#!/usr/bin/env python3
|
||
import sys
|
||
import importlib
|
||
|
||
required_modules = [
|
||
('flask', 'Flask'),
|
||
('werkzeug', 'Werkzeug'),
|
||
('jinja2', 'Jinja2'),
|
||
('sqlalchemy', 'SQLAlchemy'),
|
||
('bcrypt', 'bcrypt'),
|
||
('cryptography', 'cryptography'),
|
||
('requests', 'requests'),
|
||
('psutil', 'psutil'),
|
||
('flask_login', 'Flask-Login'),
|
||
('flask_wtf', 'Flask-WTF'),
|
||
('wtforms', 'WTForms'),
|
||
('itsdangerous', 'itsdangerous'),
|
||
('markupsafe', 'MarkupSafe'),
|
||
('click', 'click')
|
||
]
|
||
|
||
failed_imports = []
|
||
successful_imports = []
|
||
|
||
for module_name, display_name in required_modules:
|
||
try:
|
||
module = importlib.import_module(module_name)
|
||
version = getattr(module, '__version__', 'Version unbekannt')
|
||
print(f"✅ {display_name}: {version}")
|
||
successful_imports.append(display_name)
|
||
except ImportError as e:
|
||
print(f"❌ {display_name}: Import fehlgeschlagen - {e}")
|
||
failed_imports.append(display_name)
|
||
except Exception as e:
|
||
print(f"⚠️ {display_name}: Unerwarteter Fehler - {e}")
|
||
failed_imports.append(display_name)
|
||
|
||
print(f"\nErgebnis: {len(successful_imports)} erfolgreich, {len(failed_imports)} fehlgeschlagen")
|
||
|
||
if failed_imports:
|
||
print("Fehlgeschlagene Module:", ", ".join(failed_imports))
|
||
sys.exit(1)
|
||
else:
|
||
print("Alle kritischen Module erfolgreich importiert!")
|
||
sys.exit(0)
|
||
EOF
|
||
|
||
if python3 /tmp/validate_python.py; then
|
||
success "✅ Alle kritischen Python-Abhängigkeiten erfolgreich validiert"
|
||
else
|
||
warning "⚠️ Einige Python-Abhängigkeiten fehlen - versuche Reparatur..."
|
||
|
||
# Repair-Versuch
|
||
retry_command "python3 -m pip install --break-system-packages --upgrade --force-reinstall Flask Werkzeug SQLAlchemy bcrypt" "Critical Package Repair"
|
||
fi
|
||
|
||
rm -f /tmp/validate_python.py
|
||
|
||
log "✅ Python-Pakete Installation abgeschlossen"
|
||
}
|
||
|
||
# =========================== ROBUSTE NODE.JS INSTALLATION ===========================
|
||
install_nodejs_npm() {
|
||
log "=== ROBUSTE NODE.JS UND NPM INSTALLATION ==="
|
||
|
||
# Alte Installationen entfernen
|
||
progress "Bereinige alte Node.js-Installationen..."
|
||
apt-get remove --purge -y nodejs npm 2>/dev/null || true
|
||
apt-get autoremove -y 2>/dev/null || true
|
||
rm -rf /usr/local/bin/node /usr/local/bin/npm 2>/dev/null || true
|
||
|
||
# NodeSource Repository mit Fallback
|
||
progress "Installiere Node.js mit Fallback-Strategie..."
|
||
|
||
local nodejs_installed=false
|
||
|
||
# Strategie 1: NodeSource Repository
|
||
if curl -fsSL https://deb.nodesource.com/setup_lts.x 2>/dev/null | bash - 2>/dev/null; then
|
||
if apt-get update -y && apt_install_retry nodejs; then
|
||
nodejs_installed=true
|
||
log "✅ Node.js via NodeSource Repository installiert"
|
||
fi
|
||
fi
|
||
|
||
# Strategie 2: Snap (falls verfügbar)
|
||
if [ "$nodejs_installed" = false ] && command -v snap >/dev/null 2>&1; then
|
||
progress "Versuche Node.js Installation via Snap..."
|
||
if snap install node --classic 2>/dev/null; then
|
||
nodejs_installed=true
|
||
log "✅ Node.js via Snap installiert"
|
||
fi
|
||
fi
|
||
|
||
# Strategie 3: Debian Repository (Fallback)
|
||
if [ "$nodejs_installed" = false ]; then
|
||
progress "Verwende Debian Repository als Fallback..."
|
||
apt_install_retry nodejs npm
|
||
nodejs_installed=true
|
||
log "✅ Node.js via Debian Repository installiert"
|
||
fi
|
||
|
||
# Validierung
|
||
progress "Validiere Node.js Installation..."
|
||
if command -v node >/dev/null 2>&1; then
|
||
local node_version=$(node --version)
|
||
log "✅ Node.js Version: $node_version"
|
||
else
|
||
error "❌ Node.js nicht verfügbar nach Installation"
|
||
fi
|
||
|
||
if command -v npm >/dev/null 2>&1; then
|
||
local npm_version=$(npm --version)
|
||
log "✅ npm Version: $npm_version"
|
||
|
||
# npm optimieren
|
||
progress "Optimiere npm-Konfiguration..."
|
||
npm config set fund false 2>/dev/null || true
|
||
npm config set audit-level moderate 2>/dev/null || true
|
||
npm config set progress false 2>/dev/null || true
|
||
npm config set loglevel warn 2>/dev/null || true
|
||
else
|
||
# Versuche npm separat zu installieren
|
||
progress "Installiere npm separat..."
|
||
apt_install_retry npm
|
||
fi
|
||
|
||
log "✅ Node.js und npm erfolgreich installiert"
|
||
}
|
||
|
||
# =========================== NETZWERK-SICHERHEIT ===========================
|
||
configure_network_security() {
|
||
log "=== KONFIGURIERE ERWEITERTE NETZWERK-SICHERHEIT ==="
|
||
|
||
# IPv6 vollständig deaktivieren
|
||
progress "Deaktiviere IPv6..."
|
||
|
||
# IPv6 in GRUB deaktivieren
|
||
if [ -f /etc/default/grub ]; then
|
||
cp /etc/default/grub /etc/default/grub.backup
|
||
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="[^"]*/& ipv6.disable=1/' /etc/default/grub
|
||
sed -i 's/GRUB_CMDLINE_LINUX="[^"]*/& ipv6.disable=1/' /etc/default/grub
|
||
update-grub 2>/dev/null || true
|
||
fi
|
||
|
||
# IPv6 in sysctl deaktivieren
|
||
cat >> /etc/sysctl.conf << 'EOF'
|
||
|
||
# ===================================================================
|
||
# MYP Netzwerk-Sicherheitskonfiguration
|
||
# ===================================================================
|
||
|
||
# IPv6 vollständig deaktivieren
|
||
net.ipv6.conf.all.disable_ipv6 = 1
|
||
net.ipv6.conf.default.disable_ipv6 = 1
|
||
net.ipv6.conf.lo.disable_ipv6 = 1
|
||
|
||
# IP-Spoofing-Schutz aktivieren
|
||
net.ipv4.conf.all.rp_filter = 1
|
||
net.ipv4.conf.default.rp_filter = 1
|
||
|
||
# Source-Routing deaktivieren (verhindert IP-Spoofing)
|
||
net.ipv4.conf.all.accept_source_route = 0
|
||
net.ipv4.conf.default.accept_source_route = 0
|
||
|
||
# ICMP-Redirects ignorieren (verhindert Man-in-the-Middle)
|
||
net.ipv4.conf.all.accept_redirects = 0
|
||
net.ipv4.conf.default.accept_redirects = 0
|
||
net.ipv4.conf.all.send_redirects = 0
|
||
|
||
# Secure ICMP-Redirects ignorieren
|
||
net.ipv4.conf.all.secure_redirects = 0
|
||
net.ipv4.conf.default.secure_redirects = 0
|
||
|
||
# SYN-Flood-Schutz aktivieren
|
||
net.ipv4.tcp_syncookies = 1
|
||
net.ipv4.tcp_max_syn_backlog = 2048
|
||
net.ipv4.tcp_synack_retries = 2
|
||
net.ipv4.tcp_syn_retries = 5
|
||
|
||
# TCP-RFC-Compliance (verhindert aggressive Paketwiederholungen)
|
||
net.ipv4.tcp_retries1 = 3
|
||
net.ipv4.tcp_retries2 = 15
|
||
net.ipv4.tcp_orphan_retries = 3
|
||
|
||
# TCP-Window-Skalierung optimieren (geringere Netzwerkauslastung)
|
||
net.ipv4.tcp_window_scaling = 1
|
||
net.ipv4.tcp_rmem = 4096 65536 16777216
|
||
net.ipv4.tcp_wmem = 4096 65536 16777216
|
||
net.core.rmem_max = 16777216
|
||
net.core.wmem_max = 16777216
|
||
net.core.netdev_max_backlog = 5000
|
||
|
||
# Broadcast-Pings ignorieren (verhindert Smurf-Angriffe)
|
||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||
|
||
# Bogus ICMP-Antworten ignorieren
|
||
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||
|
||
# Paketweiterleitung verhindern (Router-Funktionalität deaktivieren)
|
||
net.ipv4.ip_forward = 0
|
||
|
||
# Martian-Pakete loggen (verdächtige Pakete)
|
||
net.ipv4.conf.all.log_martians = 1
|
||
net.ipv4.conf.default.log_martians = 1
|
||
|
||
# TCP-Timestamps deaktivieren (verhindert Fingerprinting)
|
||
net.ipv4.tcp_timestamps = 0
|
||
|
||
# TCP-SACK deaktivieren (verhindert bestimmte Angriffe)
|
||
net.ipv4.tcp_sack = 0
|
||
|
||
# TCP-Keepalive optimieren
|
||
net.ipv4.tcp_keepalive_time = 7200
|
||
net.ipv4.tcp_keepalive_probes = 9
|
||
net.ipv4.tcp_keepalive_intvl = 75
|
||
|
||
# Memory-Limits für Netzwerk-Buffers
|
||
net.core.rmem_default = 262144
|
||
net.core.wmem_default = 262144
|
||
|
||
# Schutz vor Time-Wait-Assassination
|
||
net.ipv4.tcp_rfc1337 = 1
|
||
|
||
# ===================================================================
|
||
# RASPBERRY PI PERFORMANCE-OPTIMIERUNGEN FÜR WEBAPP
|
||
# ===================================================================
|
||
|
||
# Memory Management für schwache Hardware optimieren
|
||
vm.swappiness = 10
|
||
vm.dirty_ratio = 15
|
||
vm.dirty_background_ratio = 5
|
||
vm.vfs_cache_pressure = 50
|
||
vm.min_free_kbytes = 8192
|
||
vm.overcommit_memory = 1
|
||
|
||
# CPU Scheduler für bessere Responsivität
|
||
kernel.sched_min_granularity_ns = 10000000
|
||
kernel.sched_wakeup_granularity_ns = 15000000
|
||
kernel.sched_migration_cost_ns = 5000000
|
||
|
||
# Filesystem Performance
|
||
vm.dirty_expire_centisecs = 500
|
||
vm.dirty_writeback_centisecs = 100
|
||
|
||
# Memory Compaction für bessere Speichernutzung
|
||
vm.compact_memory = 1
|
||
|
||
# OOM Killer weniger aggressiv
|
||
vm.oom_kill_allocating_task = 0
|
||
vm.panic_on_oom = 0
|
||
|
||
# Kernel Preemption für bessere Interaktivität
|
||
kernel.sched_rt_runtime_us = 950000
|
||
kernel.sched_rt_period_us = 1000000
|
||
|
||
# I/O Scheduler Optimierungen für SD-Karte
|
||
# (wird später per udev-Regel angewendet)
|
||
|
||
EOF
|
||
|
||
# Sysctl-Einstellungen sofort anwenden
|
||
sysctl -p || warning "Einige sysctl-Einstellungen konnten nicht angewendet werden"
|
||
|
||
# IPv6 in Netzwerk-Interfaces deaktivieren
|
||
progress "Deaktiviere IPv6 in Netzwerk-Interfaces..."
|
||
|
||
# Für systemd-networkd
|
||
if systemctl is-enabled systemd-networkd 2>/dev/null; then
|
||
mkdir -p /etc/systemd/network
|
||
cat > /etc/systemd/network/99-disable-ipv6.network << 'EOF'
|
||
[Match]
|
||
Name=*
|
||
|
||
[Network]
|
||
IPv6AcceptRA=no
|
||
LinkLocalAddressing=no
|
||
EOF
|
||
systemctl restart systemd-networkd 2>/dev/null || true
|
||
fi
|
||
|
||
# Für NetworkManager
|
||
if systemctl is-enabled NetworkManager 2>/dev/null; then
|
||
cat > /etc/NetworkManager/conf.d/99-disable-ipv6.conf << 'EOF'
|
||
[main]
|
||
plugins=keyfile
|
||
|
||
[keyfile]
|
||
unmanaged-devices=none
|
||
|
||
[connection]
|
||
ipv6.method=ignore
|
||
EOF
|
||
systemctl restart NetworkManager 2>/dev/null || true
|
||
fi
|
||
|
||
# IPv6 in /etc/hosts auskommentieren
|
||
sed -i 's/^::1/#::1/' /etc/hosts 2>/dev/null || true
|
||
|
||
log "✅ Erweiterte Netzwerk-Sicherheit konfiguriert:"
|
||
log " 🚫 IPv6 vollständig deaktiviert"
|
||
log " 🛡️ IP-Spoofing-Schutz aktiviert"
|
||
log " 🔒 SYN-Flood-Schutz aktiviert"
|
||
log " 📝 Verdächtige Pakete werden geloggt"
|
||
log " 🚫 Paketweiterleitung deaktiviert"
|
||
log " ⚡ TCP-Performance optimiert"
|
||
log " 🔐 RFC-Compliance für TCP aktiviert"
|
||
}
|
||
|
||
# =========================== DESKTOP-ENVIRONMENT ENTFERNUNG ===========================
|
||
remove_desktop_environments() {
|
||
log "=== ENTFERNE DESKTOP ENVIRONMENTS FÜR KIOSK-MODUS ==="
|
||
|
||
progress "Stoppe alle Desktop-Services..."
|
||
local desktop_services=("lightdm" "gdm3" "sddm" "xdm" "nodm")
|
||
|
||
for service in "${desktop_services[@]}"; do
|
||
systemctl stop "$service" 2>/dev/null || true
|
||
systemctl disable "$service" 2>/dev/null || true
|
||
done
|
||
|
||
progress "Entferne Desktop-Pakete vollständig..."
|
||
|
||
# Raspberry Pi OS Desktop-Pakete
|
||
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 Anwendungen entfernen
|
||
apt-get remove --purge -y \
|
||
libreoffice* \
|
||
thunderbird* \
|
||
firefox* \
|
||
vlc* \
|
||
gimp* \
|
||
scratch* \
|
||
minecraft-pi \
|
||
sonic-pi \
|
||
2>/dev/null || true
|
||
|
||
# Aufräumen
|
||
apt-get autoremove --purge -y
|
||
apt-get autoclean
|
||
|
||
log "✅ Desktop Environments vollständig entfernt"
|
||
}
|
||
|
||
# =========================== MINIMALE X11-UMGEBUNG ===========================
|
||
install_minimal_x11() {
|
||
log "=== INSTALLIERE MINIMALE X11-UMGEBUNG FÜR KIOSK ==="
|
||
|
||
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"
|
||
|
||
# Browser-Installation mit Fallback-Mechanismus
|
||
progress "Installiere Browser für Kiosk-Modus..."
|
||
local browser_installed=false
|
||
|
||
# Versuche Chromium zu installieren
|
||
if apt-get install -y chromium 2>/dev/null; then
|
||
log "✅ Chromium erfolgreich installiert"
|
||
browser_installed=true
|
||
elif apt-get install -y chromium-browser 2>/dev/null; then
|
||
log "✅ Chromium-Browser erfolgreich installiert"
|
||
browser_installed=true
|
||
elif apt-get install -y firefox-esr 2>/dev/null; then
|
||
warning "⚠️ Chromium nicht verfügbar - Firefox ESR als Fallback installiert"
|
||
browser_installed=true
|
||
fi
|
||
|
||
if [ "$browser_installed" = false ]; then
|
||
error "❌ Kein Browser verfügbar (chromium, chromium-browser, firefox-esr)"
|
||
fi
|
||
|
||
log "✅ Minimale X11-Umgebung installiert"
|
||
}
|
||
|
||
# =========================== KIOSK-BENUTZER MANAGEMENT ===========================
|
||
create_kiosk_user() {
|
||
log "=== KIOSK-BENUTZER SETUP ==="
|
||
|
||
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
|
||
|
||
# pip-Konfiguration für Kiosk-Benutzer
|
||
local kiosk_home="/home/$KIOSK_USER"
|
||
mkdir -p "$kiosk_home/.pip" 2>/dev/null || true
|
||
cat > "$kiosk_home/.pip/pip.conf" << 'EOF'
|
||
[global]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
timeout = 60
|
||
retries = 3
|
||
|
||
[install]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
EOF
|
||
chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.pip/pip.conf" 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 konfiguriert: $KIOSK_USER"
|
||
}
|
||
|
||
configure_autologin() {
|
||
log "=== KONFIGURIERE AUTOLOGIN FÜR KIOSK-BENUTZER ==="
|
||
|
||
# Getty-Service für automatischen Login konfigurieren
|
||
progress "Konfiguriere automatischen Login auf tty1..."
|
||
|
||
local getty_override_dir="/etc/systemd/system/getty@tty1.service.d"
|
||
mkdir -p "$getty_override_dir"
|
||
|
||
cat > "$getty_override_dir/override.conf" << EOF
|
||
[Service]
|
||
ExecStart=
|
||
ExecStart=-/sbin/agetty --autologin $KIOSK_USER --noclear %I \$TERM
|
||
EOF
|
||
|
||
# Systemd-Konfiguration neu laden
|
||
systemctl daemon-reload
|
||
systemctl enable getty@tty1.service
|
||
|
||
log "✅ Autologin für $KIOSK_USER konfiguriert"
|
||
}
|
||
|
||
configure_kiosk_autostart() {
|
||
log "=== KONFIGURIERE AUTOMATISCHEN KIOSK-START ==="
|
||
|
||
# Erstelle .bashrc für automatischen X-Server und Browser-Start
|
||
progress "Konfiguriere automatischen Kiosk-Start für $KIOSK_USER..."
|
||
|
||
local kiosk_home="/home/$KIOSK_USER"
|
||
|
||
# .bashrc für automatischen Start erstellen
|
||
cat > "$kiosk_home/.bashrc" << 'EOF'
|
||
# Automatischer Kiosk-Start beim Login
|
||
if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" = "1" ]; then
|
||
echo "Starte Kiosk-Modus..."
|
||
|
||
# X-Server im Hintergrund starten
|
||
startx /home/kiosk/.xinitrc -- :0 vt1 &
|
||
|
||
# Warte bis X-Server bereit ist
|
||
sleep 5
|
||
|
||
# Setze DISPLAY-Variable
|
||
export DISPLAY=:0
|
||
|
||
# Warte auf HTTPS-Backend
|
||
echo "Warte auf HTTPS-Backend..."
|
||
for i in {1..60}; do
|
||
if curl -k -s https://localhost:443 >/dev/null 2>&1; then
|
||
echo "HTTPS-Backend erreichbar"
|
||
break
|
||
fi
|
||
echo "Warte... ($i/60)"
|
||
sleep 2
|
||
done
|
||
|
||
# Bildschirmschoner deaktivieren
|
||
xset s off
|
||
xset s noblank
|
||
xset -dpms
|
||
|
||
# Mauszeiger verstecken
|
||
unclutter -idle 0.1 -root -noevents &
|
||
|
||
# Browser im Kiosk-Modus starten
|
||
if command -v chromium >/dev/null 2>&1; then
|
||
BROWSER="chromium"
|
||
elif command -v chromium-browser >/dev/null 2>&1; then
|
||
BROWSER="chromium-browser"
|
||
else
|
||
BROWSER="firefox-esr"
|
||
fi
|
||
|
||
echo "Starte $BROWSER im Kiosk-Modus..."
|
||
|
||
if [[ "$BROWSER" == "chromium"* ]]; then
|
||
exec $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 \
|
||
--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
|
||
else
|
||
exec firefox-esr \
|
||
--kiosk \
|
||
https://localhost:443
|
||
fi
|
||
fi
|
||
EOF
|
||
|
||
# .xinitrc für X-Server-Konfiguration erstellen
|
||
cat > "$kiosk_home/.xinitrc" << 'EOF'
|
||
#!/bin/bash
|
||
# Minimale X-Session für Kiosk-Modus
|
||
exec openbox-session
|
||
EOF
|
||
|
||
# Berechtigungen setzen
|
||
chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.bashrc"
|
||
chown "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.xinitrc"
|
||
chmod +x "$kiosk_home/.xinitrc"
|
||
|
||
# Erstelle Kiosk-Verzeichnisse
|
||
mkdir -p "$kiosk_home/.chromium-kiosk"
|
||
chown -R "$KIOSK_USER:$KIOSK_USER" "$kiosk_home/.chromium-kiosk"
|
||
|
||
log "✅ Automatischer Kiosk-Start konfiguriert"
|
||
info "Der Kiosk-Modus startet automatisch beim Login des $KIOSK_USER"
|
||
}
|
||
|
||
# =========================== ROBUSTE SSL-ZERTIFIKATE INSTALLATION ===========================
|
||
install_ssl_certificates() {
|
||
log "=== ROBUSTE SSL-ZERTIFIKATE KONFIGURATION ==="
|
||
|
||
progress "Installiere SSL-Grundkomponenten..."
|
||
apt_install_retry ca-certificates openssl
|
||
|
||
progress "Aktualisiere CA-Zertifikate..."
|
||
retry_command "update-ca-certificates" "CA-Zertifikate Update"
|
||
|
||
# SSL-Verzeichnisse sicherstellen
|
||
mkdir -p /usr/local/share/ca-certificates/myp
|
||
|
||
# Mercedes Corporate Zertifikate (robuster)
|
||
if [ -d "$CURRENT_DIR/certs/mercedes" ] && [ "$(ls -A $CURRENT_DIR/certs/mercedes 2>/dev/null)" ]; then
|
||
progress "Installiere Mercedes Corporate Zertifikate (robust)..."
|
||
|
||
local cert_count=0
|
||
local installed_count=0
|
||
|
||
find "$CURRENT_DIR/certs/mercedes" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) | while read cert_file; do
|
||
((cert_count++))
|
||
local cert_basename=$(basename "$cert_file")
|
||
local cert_name="${cert_basename%.*}"
|
||
local target_file="/usr/local/share/ca-certificates/myp/${cert_name}.crt"
|
||
|
||
progress "Verarbeite Mercedes-Zertifikat: $cert_basename"
|
||
|
||
# Robuste Zertifikat-Validierung und Installation
|
||
if openssl x509 -in "$cert_file" -text -noout >/dev/null 2>&1; then
|
||
# PEM Format
|
||
if cp "$cert_file" "$target_file" 2>/dev/null; then
|
||
log "✅ PEM-Zertifikat installiert: ${cert_name}.crt"
|
||
((installed_count++))
|
||
fi
|
||
elif openssl x509 -in "$cert_file" -inform DER -text -noout >/dev/null 2>&1; then
|
||
# DER Format - zu PEM konvertieren
|
||
if openssl x509 -in "$cert_file" -inform DER -out "$target_file" -outform PEM 2>/dev/null; then
|
||
log "✅ DER-Zertifikat konvertiert und installiert: ${cert_name}.crt"
|
||
((installed_count++))
|
||
fi
|
||
else
|
||
warning "⚠️ Ungültiges Zertifikat übersprungen: $cert_file"
|
||
fi
|
||
done
|
||
|
||
if [ $installed_count -gt 0 ]; then
|
||
progress "Lade CA-Zertifikate nach Mercedes-Import neu..."
|
||
retry_command "update-ca-certificates" "Mercedes Zertifikate Update"
|
||
log "✅ $installed_count von $cert_count Mercedes-Zertifikaten erfolgreich installiert"
|
||
fi
|
||
fi
|
||
|
||
# SSL-Umgebungsvariablen systemweit setzen
|
||
progress "Konfiguriere SSL-Umgebungsvariablen..."
|
||
cat >> /etc/environment << 'EOF'
|
||
|
||
# SSL Certificate Configuration für MYP
|
||
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
|
||
CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
|
||
EOF
|
||
|
||
# SSL-Umgebungsvariablen für aktuelle Session
|
||
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"
|
||
|
||
# Validiere SSL-Setup
|
||
progress "Validiere SSL-Konfiguration..."
|
||
if [ -f "/etc/ssl/certs/ca-certificates.crt" ]; then
|
||
local cert_count=$(grep -c "BEGIN CERTIFICATE" /etc/ssl/certs/ca-certificates.crt 2>/dev/null || echo "0")
|
||
log "✅ SSL-Zertifikate verfügbar: $cert_count CA-Zertifikate"
|
||
else
|
||
warning "⚠️ CA-Zertifikate-Datei nicht gefunden"
|
||
fi
|
||
|
||
log "✅ SSL-Zertifikate robust konfiguriert"
|
||
}
|
||
|
||
# =========================== ROBUSTES ANWENDUNGS-DEPLOYMENT ===========================
|
||
deploy_application() {
|
||
log "=== ROBUSTES ANWENDUNGS-DEPLOYMENT ==="
|
||
|
||
progress "Erstelle sicheres Zielverzeichnis: $APP_DIR"
|
||
mkdir -p "$APP_DIR" || error "Konnte Zielverzeichnis nicht erstellen"
|
||
|
||
# Validiere Source-Verzeichnis
|
||
progress "Validiere Source-Dateien..."
|
||
if [ ! -f "$CURRENT_DIR/app.py" ]; then
|
||
error "Kritische Datei nicht gefunden: $CURRENT_DIR/app.py"
|
||
fi
|
||
|
||
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
||
error "Kritische Datei nicht gefunden: $CURRENT_DIR/requirements.txt"
|
||
fi
|
||
|
||
progress "Kopiere Anwendungsdateien (robust)..."
|
||
|
||
# Kritische Dateien zuerst (mit Validierung)
|
||
local critical_files=(
|
||
"app.py"
|
||
"models.py"
|
||
"requirements.txt"
|
||
)
|
||
|
||
for file in "${critical_files[@]}"; do
|
||
if [ -f "$CURRENT_DIR/$file" ]; then
|
||
progress "Kopiere kritische Datei: $file"
|
||
if cp "$CURRENT_DIR/$file" "$APP_DIR/" 2>/dev/null; then
|
||
success "✅ $file erfolgreich kopiert"
|
||
else
|
||
error "❌ Fehler beim Kopieren der kritischen Datei: $file"
|
||
fi
|
||
else
|
||
error "❌ Kritische Datei fehlt: $file"
|
||
fi
|
||
done
|
||
|
||
# Verzeichnisse mit robuster Behandlung
|
||
local directories=(
|
||
"blueprints"
|
||
"config"
|
||
"database"
|
||
"static"
|
||
"templates"
|
||
"uploads"
|
||
"utils"
|
||
"logs"
|
||
"certs"
|
||
)
|
||
|
||
for dir in "${directories[@]}"; do
|
||
if [ -d "$CURRENT_DIR/$dir" ]; then
|
||
progress "Kopiere Verzeichnis: $dir"
|
||
if cp -r "$CURRENT_DIR/$dir" "$APP_DIR/" 2>/dev/null; then
|
||
success "✅ $dir erfolgreich kopiert"
|
||
else
|
||
warning "⚠️ Fehler beim Kopieren von $dir (möglicherweise nicht kritisch)"
|
||
fi
|
||
else
|
||
info "Verzeichnis nicht vorhanden: $dir"
|
||
fi
|
||
done
|
||
|
||
# Optionale Dateien
|
||
local optional_files=(
|
||
"package.json"
|
||
"package-lock.json"
|
||
"tailwind.config.js"
|
||
"postcss.config.js"
|
||
"README.md"
|
||
".gitignore"
|
||
)
|
||
|
||
for file in "${optional_files[@]}"; do
|
||
if [ -f "$CURRENT_DIR/$file" ]; then
|
||
progress "Kopiere optionale Datei: $file"
|
||
cp "$CURRENT_DIR/$file" "$APP_DIR/" 2>/dev/null || warning "⚠️ Kopieren von $file fehlgeschlagen"
|
||
fi
|
||
done
|
||
|
||
# Erstelle alle notwendigen Verzeichnisse mit korrekter Struktur
|
||
progress "Erstelle Verzeichnisstruktur..."
|
||
local required_dirs=(
|
||
"database/backups"
|
||
"logs/app"
|
||
"logs/auth"
|
||
"logs/errors"
|
||
"logs/system"
|
||
"uploads/temp"
|
||
"uploads/assets"
|
||
"uploads/avatars"
|
||
"uploads/backups"
|
||
"uploads/jobs"
|
||
"certs/localhost"
|
||
"instance"
|
||
"instance/ssl"
|
||
)
|
||
|
||
for dir in "${required_dirs[@]}"; do
|
||
mkdir -p "$APP_DIR/$dir" 2>/dev/null || warning "⚠️ Verzeichnis $dir konnte nicht erstellt werden"
|
||
done
|
||
|
||
# Sichere Berechtigungen setzen (robuster)
|
||
progress "Setze sichere Berechtigungen..."
|
||
|
||
# Basis-Berechtigungen
|
||
chown -R root:root "$APP_DIR" 2>/dev/null || warning "⚠️ Ownership konnte nicht gesetzt werden"
|
||
chmod 755 "$APP_DIR" 2>/dev/null || warning "⚠️ Verzeichnis-Permissions konnten nicht gesetzt werden"
|
||
|
||
# Ausführbare Dateien
|
||
if [ -f "$APP_DIR/app.py" ]; then
|
||
chmod +x "$APP_DIR/app.py" 2>/dev/null || warning "⚠️ app.py Ausführberechtigung konnte nicht gesetzt werden"
|
||
fi
|
||
|
||
# Sensitive Verzeichnisse
|
||
chmod 750 "$APP_DIR/database" 2>/dev/null || true
|
||
chmod 750 "$APP_DIR/logs" 2>/dev/null || true
|
||
chmod 750 "$APP_DIR/certs" 2>/dev/null || true
|
||
chmod 750 "$APP_DIR/instance" 2>/dev/null || true
|
||
|
||
# Upload-Verzeichnisse
|
||
chmod 755 "$APP_DIR/uploads" 2>/dev/null || true
|
||
chmod 755 "$APP_DIR/static" 2>/dev/null || true
|
||
|
||
# Python-Umgebung robust konfigurieren
|
||
progress "Konfiguriere robuste Python-Umgebung..."
|
||
|
||
# Ermittle Python site-packages Verzeichnis (robust)
|
||
local python_site_packages=""
|
||
for possible_path in \
|
||
"$(python3 -c "import site; print(site.getsitepackages()[0])" 2>/dev/null)" \
|
||
"/usr/local/lib/python3/dist-packages" \
|
||
"/usr/lib/python3/dist-packages" \
|
||
"/usr/local/lib/python$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)/dist-packages"; do
|
||
|
||
if [ -d "$possible_path" ]; then
|
||
python_site_packages="$possible_path"
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [ -n "$python_site_packages" ]; then
|
||
echo "$APP_DIR" > "$python_site_packages/myp-app.pth" 2>/dev/null || warning "⚠️ Python-Pfad konnte nicht konfiguriert werden"
|
||
log "✅ Python-Pfad konfiguriert: $python_site_packages/myp-app.pth"
|
||
else
|
||
warning "⚠️ Python site-packages Verzeichnis nicht gefunden"
|
||
fi
|
||
|
||
# Systemweite Umgebungsvariablen robust setzen
|
||
progress "Konfiguriere Umgebungsvariablen..."
|
||
cat >> /etc/environment << EOF
|
||
|
||
# MYP Application Environment
|
||
MYP_APP_DIR=$APP_DIR
|
||
PYTHONPATH=$APP_DIR:\${PYTHONPATH:-}
|
||
FLASK_APP=$APP_DIR/app.py
|
||
FLASK_ENV=production
|
||
EOF
|
||
|
||
# Bash-Profile für alle User aktualisieren (robust)
|
||
progress "Aktualisiere Bash-Profile..."
|
||
local profile_updated=0
|
||
|
||
for user_home in "/root" "/home/"*; do
|
||
if [ -d "$user_home" ] && [ "$user_home" != "/home/lost+found" ] && [ -w "$user_home" ]; then
|
||
if ! grep -q "MYP Application Environment" "$user_home/.bashrc" 2>/dev/null; then
|
||
cat >> "$user_home/.bashrc" << 'EOF'
|
||
|
||
# MYP Application Environment
|
||
if [ -d "/opt/myp" ]; then
|
||
export MYP_APP_DIR="/opt/myp"
|
||
export FLASK_APP="/opt/myp/app.py"
|
||
export FLASK_ENV="production"
|
||
if [ -z "${PYTHONPATH:-}" ]; then
|
||
export PYTHONPATH="/opt/myp"
|
||
else
|
||
export PYTHONPATH="/opt/myp:$PYTHONPATH"
|
||
fi
|
||
fi
|
||
EOF
|
||
((profile_updated++))
|
||
log "✅ Bash-Profile aktualisiert: $user_home/.bashrc"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Validiere Deployment
|
||
progress "Validiere Application Deployment..."
|
||
local validation_errors=0
|
||
|
||
# Prüfe kritische Dateien
|
||
for file in "app.py" "models.py" "requirements.txt"; do
|
||
if [ ! -f "$APP_DIR/$file" ]; then
|
||
warning "❌ Kritische Datei fehlt: $file"
|
||
((validation_errors++))
|
||
fi
|
||
done
|
||
|
||
# Prüfe wichtige Verzeichnisse
|
||
for dir in "static" "templates" "blueprints"; do
|
||
if [ ! -d "$APP_DIR/$dir" ]; then
|
||
warning "❌ Wichtiges Verzeichnis fehlt: $dir"
|
||
((validation_errors++))
|
||
fi
|
||
done
|
||
|
||
if [ $validation_errors -eq 0 ]; then
|
||
success "✅ Application Deployment vollständig validiert"
|
||
else
|
||
warning "⚠️ $validation_errors Probleme beim Deployment gefunden"
|
||
fi
|
||
|
||
log "✅ Robustes Anwendungs-Deployment abgeschlossen"
|
||
log " 📁 App-Verzeichnis: $APP_DIR"
|
||
log " 🐍 Python-Pfad konfiguriert"
|
||
log " 🔧 $profile_updated Bash-Profile aktualisiert"
|
||
log " 🛡️ Sichere Berechtigungen gesetzt"
|
||
}
|
||
|
||
install_npm_dependencies() {
|
||
log "=== NPM-ABHÄNGIGKEITEN INSTALLATION ==="
|
||
|
||
if [ -f "$APP_DIR/package.json" ]; then
|
||
progress "Installiere npm-Abhängigkeiten..."
|
||
|
||
cd "$APP_DIR"
|
||
|
||
# npm install mit verschiedenen Fallback-Strategien
|
||
if npm install --no-optional --no-audit --no-fund 2>/dev/null; then
|
||
log "✅ npm install erfolgreich (Standard)"
|
||
elif npm install --legacy-peer-deps --no-optional 2>/dev/null; then
|
||
log "✅ npm install erfolgreich (Legacy-Modus)"
|
||
elif npm install --force 2>/dev/null; then
|
||
log "✅ npm install erfolgreich (Force-Modus)"
|
||
else
|
||
warning "⚠️ npm install fehlgeschlagen - überspringe"
|
||
fi
|
||
|
||
cd "$CURRENT_DIR"
|
||
else
|
||
info "Keine package.json gefunden - überspringe npm-Installation"
|
||
fi
|
||
|
||
log "✅ NPM-Abhängigkeiten verarbeitet"
|
||
}
|
||
|
||
# =========================== SSL-ZERTIFIKAT GENERIERUNG ===========================
|
||
generate_ssl_certificate() {
|
||
log "=== SSL-ZERTIFIKAT GENERIERUNG ==="
|
||
|
||
progress "Generiere selbstsigniertes SSL-Zertifikat für localhost..."
|
||
|
||
local cert_dir="$APP_DIR/certs/localhost"
|
||
mkdir -p "$cert_dir"
|
||
|
||
# Generiere privaten Schlüssel
|
||
openssl genrsa -out "$cert_dir/localhost.key" 2048 || error "Fehler beim Generieren des privaten Schlüssels"
|
||
|
||
# Generiere Zertifikat
|
||
openssl req -new -x509 -key "$cert_dir/localhost.key" -out "$cert_dir/localhost.crt" -days 365 \
|
||
-subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=IT/CN=localhost" \
|
||
|| error "Fehler beim Generieren des SSL-Zertifikats"
|
||
|
||
# Berechtigungen setzen
|
||
chmod 600 "$cert_dir/localhost.key"
|
||
chmod 644 "$cert_dir/localhost.crt"
|
||
|
||
log "✅ SSL-Zertifikat erfolgreich generiert"
|
||
}
|
||
|
||
# =========================== ROBUSTE SYSTEMD-SERVICES INSTALLATION ===========================
|
||
install_systemd_services() {
|
||
log "=== ROBUSTE SYSTEMD-SERVICES INSTALLATION ==="
|
||
|
||
# Validiere systemd-Verzeichnis
|
||
if [ ! -d "$SYSTEMD_DIR" ]; then
|
||
error "systemd-Verzeichnis nicht gefunden: $SYSTEMD_DIR"
|
||
fi
|
||
|
||
progress "Validiere und kopiere Service-Dateien..."
|
||
|
||
# Definiere Service-Dateien mit Priorität
|
||
local essential_services=(
|
||
"$HTTPS_SERVICE_NAME.service"
|
||
)
|
||
|
||
local optional_services=(
|
||
"$KIOSK_SERVICE_NAME.service"
|
||
"$WATCHDOG_SERVICE_NAME.service"
|
||
"$WATCHDOG_PYTHON_SERVICE_NAME.service"
|
||
"$FIREWALL_SERVICE_NAME.service"
|
||
)
|
||
|
||
local installed_services=0
|
||
local essential_errors=0
|
||
|
||
# Essenzielle Services zuerst
|
||
for service_file in "${essential_services[@]}"; do
|
||
if [ -f "$SYSTEMD_DIR/$service_file" ]; then
|
||
progress "Kopiere essenziellen Service: $service_file"
|
||
if cp "$SYSTEMD_DIR/$service_file" "$SYSTEM_SYSTEMD_DIR/" 2>/dev/null; then
|
||
success "✅ $service_file erfolgreich installiert"
|
||
((installed_services++))
|
||
else
|
||
error "❌ Fehler beim Kopieren des essenziellen Service: $service_file"
|
||
((essential_errors++))
|
||
fi
|
||
else
|
||
error "❌ Essenzieller Service nicht gefunden: $service_file"
|
||
((essential_errors++))
|
||
fi
|
||
done
|
||
|
||
# Optionale Services
|
||
for service_file in "${optional_services[@]}"; do
|
||
if [ -f "$SYSTEMD_DIR/$service_file" ]; then
|
||
progress "Kopiere optionalen Service: $service_file"
|
||
if cp "$SYSTEMD_DIR/$service_file" "$SYSTEM_SYSTEMD_DIR/" 2>/dev/null; then
|
||
success "✅ $service_file erfolgreich installiert"
|
||
((installed_services++))
|
||
else
|
||
warning "⚠️ Fehler beim Kopieren des optionalen Service: $service_file"
|
||
fi
|
||
else
|
||
info "Optionaler Service nicht gefunden: $service_file"
|
||
fi
|
||
done
|
||
|
||
# Prüfe auf kritische Fehler
|
||
if [ $essential_errors -gt 0 ]; then
|
||
error "❌ $essential_errors essenzielle Services konnten nicht installiert werden!"
|
||
fi
|
||
|
||
# Validiere kopierte Service-Dateien
|
||
progress "Validiere Service-Dateien..."
|
||
for service_file in "${essential_services[@]}" "${optional_services[@]}"; do
|
||
if [ -f "$SYSTEM_SYSTEMD_DIR/$service_file" ]; then
|
||
# Syntaxprüfung für systemd-Services
|
||
if systemd-analyze verify "$SYSTEM_SYSTEMD_DIR/$service_file" 2>/dev/null; then
|
||
success "✅ $service_file Syntax-Validierung erfolgreich"
|
||
else
|
||
warning "⚠️ $service_file hat möglicherweise Syntax-Probleme"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Systemd-Konfiguration neu laden
|
||
progress "Lade systemd-Konfiguration neu..."
|
||
retry_command "systemctl daemon-reload" "systemd daemon-reload"
|
||
|
||
log "✅ Systemd-Services installiert: $installed_services Services"
|
||
}
|
||
|
||
enable_and_start_services() {
|
||
log "=== ROBUSTE SERVICES AKTIVIERUNG UND START ==="
|
||
|
||
# Service-Status tracking
|
||
local successful_services=0
|
||
local failed_services=0
|
||
|
||
# HTTPS-Service (kritisch)
|
||
progress "Aktiviere und starte HTTPS-Service (kritisch)..."
|
||
|
||
if systemctl enable "$HTTPS_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ HTTPS-Service erfolgreich aktiviert"
|
||
|
||
if systemctl start "$HTTPS_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ HTTPS-Service erfolgreich gestartet"
|
||
|
||
# Warte und prüfe Status gründlich
|
||
local startup_timeout=15
|
||
local check_interval=2
|
||
local elapsed=0
|
||
|
||
while [ $elapsed -lt $startup_timeout ]; do
|
||
if systemctl is-active --quiet "$HTTPS_SERVICE_NAME"; then
|
||
success "✅ HTTPS-Service läuft stabil nach ${elapsed}s"
|
||
((successful_services++))
|
||
break
|
||
fi
|
||
sleep $check_interval
|
||
elapsed=$((elapsed + check_interval))
|
||
progress "Warte auf HTTPS-Service Startup... (${elapsed}/${startup_timeout}s)"
|
||
done
|
||
|
||
if [ $elapsed -ge $startup_timeout ]; then
|
||
error "❌ HTTPS-Service Timeout nach ${startup_timeout}s - Service nicht verfügbar"
|
||
|
||
# Debugging-Informationen
|
||
info "HTTPS-Service Status-Debug:"
|
||
systemctl status "$HTTPS_SERVICE_NAME" --no-pager -l || true
|
||
journalctl -u "$HTTPS_SERVICE_NAME" --no-pager -n 10 || true
|
||
((failed_services++))
|
||
fi
|
||
else
|
||
error "❌ HTTPS-Service konnte nicht gestartet werden"
|
||
((failed_services++))
|
||
fi
|
||
else
|
||
error "❌ HTTPS-Service konnte nicht aktiviert werden"
|
||
((failed_services++))
|
||
fi
|
||
|
||
# Kiosk-Service (für Produktionsinstallation)
|
||
if [ -f "$SYSTEM_SYSTEMD_DIR/$KIOSK_SERVICE_NAME.service" ]; then
|
||
progress "Aktiviere Kiosk-Service (startet beim nächsten Boot)..."
|
||
if systemctl enable "$KIOSK_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ Kiosk-Service erfolgreich aktiviert"
|
||
((successful_services++))
|
||
else
|
||
warning "⚠️ Kiosk-Service konnte nicht aktiviert werden"
|
||
((failed_services++))
|
||
fi
|
||
fi
|
||
|
||
# Watchdog-Service (optional)
|
||
if [ -f "$SYSTEM_SYSTEMD_DIR/$WATCHDOG_SERVICE_NAME.service" ]; then
|
||
progress "Aktiviere und starte Watchdog-Service..."
|
||
if systemctl enable "$WATCHDOG_SERVICE_NAME" 2>/dev/null; then
|
||
if systemctl start "$WATCHDOG_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ Watchdog-Service erfolgreich aktiviert und gestartet"
|
||
((successful_services++))
|
||
else
|
||
warning "⚠️ Watchdog-Service aktiviert, aber Start fehlgeschlagen"
|
||
fi
|
||
else
|
||
warning "⚠️ Watchdog-Service konnte nicht aktiviert werden"
|
||
fi
|
||
fi
|
||
|
||
# Python Watchdog-Service (optional)
|
||
if [ -f "$SYSTEM_SYSTEMD_DIR/$WATCHDOG_PYTHON_SERVICE_NAME.service" ]; then
|
||
progress "Aktiviere Python Watchdog-Service..."
|
||
if systemctl enable "$WATCHDOG_PYTHON_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ Python Watchdog-Service erfolgreich aktiviert"
|
||
((successful_services++))
|
||
else
|
||
warning "⚠️ Python Watchdog-Service konnte nicht aktiviert werden"
|
||
fi
|
||
fi
|
||
|
||
# Firewall-Service (optional)
|
||
if [ -f "$SYSTEM_SYSTEMD_DIR/$FIREWALL_SERVICE_NAME.service" ]; then
|
||
progress "Aktiviere Firewall-Service..."
|
||
if systemctl enable "$FIREWALL_SERVICE_NAME" 2>/dev/null; then
|
||
success "✅ Firewall-Service erfolgreich aktiviert"
|
||
((successful_services++))
|
||
else
|
||
warning "⚠️ Firewall-Service konnte nicht aktiviert werden"
|
||
fi
|
||
fi
|
||
|
||
# Zusammenfassung
|
||
log "📊 Service-Aktivierung Zusammenfassung:"
|
||
log " ✅ Erfolgreich: $successful_services Services"
|
||
log " ❌ Fehlgeschlagen: $failed_services Services"
|
||
|
||
if [ $failed_services -eq 0 ]; then
|
||
success "✅ Alle verfügbaren Services erfolgreich konfiguriert"
|
||
elif [ $successful_services -gt 0 ]; then
|
||
warning "⚠️ $failed_services Services fehlgeschlagen, aber $successful_services Services funktionieren"
|
||
info "→ System ist grundsätzlich funktionsfähig"
|
||
else
|
||
error "❌ Alle Services fehlgeschlagen - System möglicherweise nicht funktionsfähig"
|
||
fi
|
||
}
|
||
|
||
# =========================== ROBUSTE SYSTEM-TESTS ===========================
|
||
test_application() {
|
||
log "=== UMFASSENDE SYSTEM-TESTS ==="
|
||
|
||
local test_errors=0
|
||
local test_warnings=0
|
||
|
||
# Test 1: Service-Status prüfen
|
||
progress "Teste Service-Status..."
|
||
if systemctl is-active --quiet "$HTTPS_SERVICE_NAME"; then
|
||
success "✅ HTTPS-Service ist aktiv"
|
||
else
|
||
warning "⚠️ HTTPS-Service ist nicht aktiv"
|
||
((test_warnings++))
|
||
|
||
# Debug-Informationen
|
||
info "Service-Status Debug:"
|
||
systemctl status "$HTTPS_SERVICE_NAME" --no-pager -l || true
|
||
fi
|
||
|
||
# Test 2: Port-Verfügbarkeit
|
||
progress "Teste Port-Verfügbarkeit..."
|
||
if ss -tlnp | grep -q ":443 "; then
|
||
success "✅ Port 443 ist geöffnet"
|
||
else
|
||
warning "⚠️ Port 443 ist nicht geöffnet"
|
||
((test_warnings++))
|
||
fi
|
||
|
||
# Test 3: HTTPS-Verbindung (robust mit mehreren Methoden)
|
||
progress "Teste HTTPS-Verbindung (robust)..."
|
||
|
||
local max_attempts=20
|
||
local attempt=1
|
||
local connection_successful=false
|
||
|
||
while [ $attempt -le $max_attempts ]; do
|
||
# Methode 1: curl mit verschiedenen Optionen
|
||
if curl -k -s --connect-timeout 3 --max-time 8 "$HTTPS_URL" >/dev/null 2>&1; then
|
||
connection_successful=true
|
||
break
|
||
fi
|
||
|
||
# Methode 2: wget als Fallback
|
||
if command -v wget >/dev/null 2>&1; then
|
||
if wget -q --no-check-certificate --timeout=3 --tries=1 "$HTTPS_URL" -O /dev/null 2>/dev/null; then
|
||
connection_successful=true
|
||
break
|
||
fi
|
||
fi
|
||
|
||
# Methode 3: openssl s_client als direkter Test
|
||
if echo "GET / HTTP/1.0" | openssl s_client -connect localhost:443 -quiet 2>/dev/null | grep -q "HTTP"; then
|
||
connection_successful=true
|
||
break
|
||
fi
|
||
|
||
progress "Warte auf HTTPS-Backend... ($attempt/$max_attempts)"
|
||
sleep 3
|
||
((attempt++))
|
||
done
|
||
|
||
if [ "$connection_successful" = true ]; then
|
||
success "✅ HTTPS-Backend erreichbar unter $HTTPS_URL"
|
||
|
||
# Erweiterte Verbindungstests
|
||
progress "Führe erweiterte HTTPS-Tests durch..."
|
||
|
||
# Test Antwortzeit
|
||
local response_time=$(curl -k -s -w "%{time_total}" -o /dev/null "$HTTPS_URL" 2>/dev/null || echo "timeout")
|
||
if [ "$response_time" != "timeout" ]; then
|
||
info "🕐 HTTPS Antwortzeit: ${response_time}s"
|
||
|
||
# Bewerte Antwortzeit
|
||
if [ "$(echo "$response_time < 2.0" | bc 2>/dev/null || echo "0")" -eq 1 ]; then
|
||
success "✅ Gute Antwortzeit"
|
||
elif [ "$(echo "$response_time < 5.0" | bc 2>/dev/null || echo "0")" -eq 1 ]; then
|
||
info "ℹ️ Akzeptable Antwortzeit"
|
||
else
|
||
warning "⚠️ Langsame Antwortzeit"
|
||
((test_warnings++))
|
||
fi
|
||
fi
|
||
|
||
# Test HTTP-Status
|
||
local http_status=$(curl -k -s -o /dev/null -w "%{http_code}" "$HTTPS_URL" 2>/dev/null || echo "000")
|
||
if [ "$http_status" = "200" ]; then
|
||
success "✅ HTTP Status 200 OK"
|
||
else
|
||
info "ℹ️ HTTP Status: $http_status (möglicherweise Redirect oder Login-Seite)"
|
||
fi
|
||
|
||
else
|
||
error "❌ HTTPS-Backend nicht erreichbar nach $max_attempts Versuchen"
|
||
((test_errors++))
|
||
|
||
# Debugging-Informationen
|
||
info "HTTPS-Debug Informationen:"
|
||
netstat -tlnp | grep ":443" || info "Port 443 nicht gefunden"
|
||
ss -tlnp | grep ":443" || info "Port 443 nicht in ss gefunden"
|
||
fi
|
||
|
||
# Test 4: SSL-Zertifikat (erweitert)
|
||
progress "Teste SSL-Zertifikat (erweitert)..."
|
||
|
||
# Methode 1: openssl s_client
|
||
if echo | openssl s_client -connect localhost:443 -servername localhost 2>/dev/null | openssl x509 -noout -text >/dev/null 2>&1; then
|
||
success "✅ SSL-Zertifikat ist gültig"
|
||
|
||
# Zertifikat-Details extrahieren
|
||
local cert_info=$(echo | openssl s_client -connect localhost:443 -servername localhost 2>/dev/null | openssl x509 -noout -subject -dates 2>/dev/null || echo "Nicht verfügbar")
|
||
info "📜 Zertifikat-Info: $cert_info"
|
||
|
||
else
|
||
warning "⚠️ SSL-Zertifikat-Test fehlgeschlagen"
|
||
((test_warnings++))
|
||
|
||
# Alternative: Teste ob Zertifikat-Dateien existieren
|
||
if [ -f "$APP_DIR/certs/localhost/localhost.crt" ] && [ -f "$APP_DIR/certs/localhost/localhost.key" ]; then
|
||
info "📁 SSL-Zertifikat-Dateien sind vorhanden"
|
||
|
||
# Teste Zertifikat-Datei direkt
|
||
if openssl x509 -in "$APP_DIR/certs/localhost/localhost.crt" -noout -text >/dev/null 2>&1; then
|
||
success "✅ SSL-Zertifikat-Datei ist gültig"
|
||
else
|
||
warning "⚠️ SSL-Zertifikat-Datei ist ungültig"
|
||
((test_warnings++))
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Test 5: Python-Anwendung Import-Test
|
||
progress "Teste Python-Anwendung Import..."
|
||
|
||
cd "$APP_DIR" 2>/dev/null || true
|
||
|
||
# Setze Test-Umgebung
|
||
export FLASK_ENV=testing
|
||
export MYP_TESTING=1
|
||
export PYTHONPATH="$APP_DIR:${PYTHONPATH:-}"
|
||
|
||
if timeout 15 python3 -c "
|
||
import sys
|
||
import os
|
||
sys.path.insert(0, '$APP_DIR')
|
||
os.chdir('$APP_DIR')
|
||
|
||
try:
|
||
import app
|
||
print('✅ Flask-App Import erfolgreich')
|
||
|
||
# Test ob Flask-App-Objekt verfügbar
|
||
if hasattr(app, 'app'):
|
||
print('✅ Flask-App-Objekt verfügbar')
|
||
else:
|
||
print('⚠️ Flask-App-Objekt nicht gefunden')
|
||
|
||
except Exception as e:
|
||
print(f'⚠️ App-Import-Problem: {e}')
|
||
exit(1)
|
||
" 2>&1; then
|
||
success "✅ Python-Anwendung kann erfolgreich importiert werden"
|
||
else
|
||
warning "⚠️ Python-Anwendung Import-Probleme (möglicherweise nicht kritisch)"
|
||
((test_warnings++))
|
||
fi
|
||
|
||
cd "$CURRENT_DIR" 2>/dev/null || true
|
||
|
||
# Test 6: Verzeichnisstruktur-Validierung
|
||
progress "Validiere Verzeichnisstruktur..."
|
||
|
||
local structure_ok=true
|
||
local required_files=("$APP_DIR/app.py" "$APP_DIR/models.py" "$APP_DIR/requirements.txt")
|
||
local required_dirs=("$APP_DIR/static" "$APP_DIR/templates" "$APP_DIR/blueprints")
|
||
|
||
for file in "${required_files[@]}"; do
|
||
if [ ! -f "$file" ]; then
|
||
warning "❌ Wichtige Datei fehlt: $file"
|
||
structure_ok=false
|
||
((test_warnings++))
|
||
fi
|
||
done
|
||
|
||
for dir in "${required_dirs[@]}"; do
|
||
if [ ! -d "$dir" ]; then
|
||
warning "❌ Wichtiges Verzeichnis fehlt: $dir"
|
||
structure_ok=false
|
||
((test_warnings++))
|
||
fi
|
||
done
|
||
|
||
if [ "$structure_ok" = true ]; then
|
||
success "✅ Verzeichnisstruktur vollständig"
|
||
fi
|
||
|
||
# Zusammenfassung der Tests
|
||
log "📊 System-Test Zusammenfassung:"
|
||
log " ❌ Fehler: $test_errors"
|
||
log " ⚠️ Warnungen: $test_warnings"
|
||
|
||
if [ $test_errors -eq 0 ] && [ $test_warnings -eq 0 ]; then
|
||
success "✅ Alle System-Tests erfolgreich - System vollständig funktionsfähig"
|
||
return 0
|
||
elif [ $test_errors -eq 0 ]; then
|
||
warning "⚠️ System-Tests mit $test_warnings Warnungen abgeschlossen - System grundsätzlich funktionsfähig"
|
||
return 0
|
||
else
|
||
error "❌ System-Tests mit $test_errors Fehlern fehlgeschlagen - System möglicherweise nicht funktionsfähig"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# =========================== AUFRÄUMEN ===========================
|
||
cleanup_old_files() {
|
||
log "=== AUFRÄUMEN ALTE DATEIEN ==="
|
||
|
||
progress "Entferne alte Shell-Skripte..."
|
||
|
||
# Entferne alte Skripte (falls vorhanden)
|
||
local old_scripts=("combined.sh" "installer.sh")
|
||
|
||
for script in "${old_scripts[@]}"; do
|
||
if [ -f "$CURRENT_DIR/$script" ]; then
|
||
progress "Entferne: $script"
|
||
rm -f "$CURRENT_DIR/$script" || warning "Fehler beim Entfernen von $script"
|
||
fi
|
||
done
|
||
|
||
log "✅ Aufräumen abgeschlossen"
|
||
}
|
||
|
||
# =========================== HAUPTMENÜ ===========================
|
||
show_menu() {
|
||
clear
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
echo -e "${CYAN} $APP_NAME - Setup-Skript v$APP_VERSION${NC}"
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
echo ""
|
||
echo -e "${YELLOW}Bitte wählen Sie eine Option:${NC}"
|
||
echo ""
|
||
echo -e "${GREEN}1)${NC} Abhängigkeiten installieren und System für manuelles Testen vorbereiten"
|
||
echo -e " ${BLUE}→ Python, Node.js, SSL-Zertifikate, Anwendung deployed, minimaler Test${NC}"
|
||
echo -e " ${BLUE}→ System bereit für manuelle Tests und Entwicklung${NC}"
|
||
echo ""
|
||
echo -e "${GREEN}2)${NC} Vollständige Kiosk-Installation mit Remote-Zugang"
|
||
echo -e " ${BLUE}→ Komplette Produktionsinstallation mit automatischem Kiosk-Start${NC}"
|
||
echo -e " ${BLUE}→ RDP (root:744563017196A), SSH (user:raspberry), Firewall${NC}"
|
||
echo -e " ${BLUE}→ Automatischer Login und Kiosk-Modus beim Boot${NC}"
|
||
echo ""
|
||
echo -e "${GREEN}3)${NC} Beenden"
|
||
echo ""
|
||
echo -e "${CYAN}=================================================================${NC}"
|
||
echo -n "Ihre Wahl [1-3]: "
|
||
}
|
||
|
||
# =========================== INSTALLATIONS-MODI ===========================
|
||
install_dependencies_only() {
|
||
# Logging initialisieren
|
||
init_logging
|
||
|
||
log "=== MODUS: ROBUSTE ABHÄNGIGKEITEN-INSTALLATION FÜR MANUELLES TESTEN ==="
|
||
|
||
# Grundlegende System-Validierung
|
||
check_root
|
||
check_system_resources
|
||
check_debian_system
|
||
check_internet_connection
|
||
|
||
# System-Konfiguration
|
||
configure_hostname
|
||
update_system
|
||
configure_network_security
|
||
|
||
# Core-Abhängigkeiten installieren
|
||
install_python_dependencies
|
||
install_nodejs_npm
|
||
install_ssl_certificates
|
||
install_python_packages
|
||
|
||
# Anwendung deployen
|
||
deploy_application
|
||
install_npm_dependencies
|
||
generate_ssl_certificate
|
||
|
||
# Services für manuelles Testen vorbereiten
|
||
install_systemd_services
|
||
enable_and_start_services
|
||
|
||
# Performance-Optimierungen auch für manuelles Testen
|
||
optimize_webapp_performance
|
||
optimize_static_assets
|
||
|
||
# Umfassende System-Tests
|
||
progress "Starte umfassende System-Tests..."
|
||
if test_application; then
|
||
success "✅ Alle System-Tests erfolgreich!"
|
||
else
|
||
warning "⚠️ System-Tests mit Problemen - System möglicherweise eingeschränkt funktionsfähig"
|
||
fi
|
||
|
||
# Cleanup
|
||
cleanup_old_files
|
||
|
||
success "✅ Robuste Abhängigkeiten-Installation abgeschlossen!"
|
||
log "📋 Installation Zusammenfassung:"
|
||
log " 🖥️ Hostname: raspberrypi"
|
||
log " 🐍 Python-Umgebung: Vollständig konfiguriert"
|
||
log " 🌐 Node.js: Installiert und optimiert"
|
||
log " 🔒 SSL-Zertifikate: Generiert und konfiguriert"
|
||
log " 📁 Anwendung: In $APP_DIR deployed"
|
||
log " ⚡ Performance: Optimiert für Raspberry Pi"
|
||
log " 🔧 Services: Installiert und gestartet"
|
||
info ""
|
||
info "🚀 System bereit für manuelle Tests und Entwicklung!"
|
||
info "🌐 HTTPS-Backend sollte verfügbar sein: $HTTPS_URL"
|
||
info "⚙️ Manuelle App-Start Alternative: cd /opt/myp && python3 app.py"
|
||
|
||
# Fehler-Zusammenfassung anzeigen
|
||
show_error_summary
|
||
}
|
||
|
||
install_full_production_system() {
|
||
# Logging initialisieren
|
||
init_logging
|
||
|
||
log "=== MODUS: VOLLSTÄNDIGE ROBUSTE KIOSK-INSTALLATION MIT REMOTE-ZUGANG ==="
|
||
|
||
# Umfassende System-Validierung
|
||
check_root
|
||
check_system_resources
|
||
check_debian_system
|
||
check_internet_connection
|
||
|
||
# System-Grundkonfiguration
|
||
configure_hostname
|
||
|
||
# Intelligente Abhängigkeiten-Installation
|
||
if [ ! -d "$APP_DIR" ] || [ ! -f "$APP_DIR/app.py" ]; then
|
||
warning "Anwendung noch nicht deployed - führe robuste Abhängigkeiten-Installation durch..."
|
||
|
||
# Vollständige Basis-Installation
|
||
update_system
|
||
configure_network_security
|
||
install_python_dependencies
|
||
install_nodejs_npm
|
||
install_ssl_certificates
|
||
install_python_packages
|
||
deploy_application
|
||
install_npm_dependencies
|
||
generate_ssl_certificate
|
||
else
|
||
info "Anwendung bereits deployed - überspringe Basis-Installation"
|
||
# Trotzdem Netzwerk-Sicherheit aktualisieren
|
||
configure_network_security
|
||
fi
|
||
|
||
# Desktop-Environments entfernen und minimale X11 installieren
|
||
remove_desktop_environments
|
||
install_minimal_x11
|
||
|
||
# Performance-Optimierungen für Raspberry Pi Webapp
|
||
optimize_webapp_performance
|
||
optimize_static_assets
|
||
|
||
# Remote-Zugang konfigurieren (robust)
|
||
install_remote_access
|
||
configure_firewall
|
||
|
||
# Kiosk-System konfigurieren
|
||
create_kiosk_user
|
||
configure_autologin
|
||
configure_kiosk_autostart
|
||
|
||
# Services installieren und aktivieren (robust)
|
||
install_systemd_services
|
||
enable_and_start_services
|
||
|
||
# Umfassende System-Tests
|
||
progress "Führe umfassende Produktions-System-Tests durch..."
|
||
local system_tests_passed=true
|
||
|
||
if test_application; then
|
||
success "✅ Anwendungs-Tests erfolgreich"
|
||
else
|
||
warning "⚠️ Anwendungs-Tests mit Problemen"
|
||
system_tests_passed=false
|
||
fi
|
||
|
||
if test_remote_access; then
|
||
success "✅ Remote-Zugang-Tests erfolgreich"
|
||
else
|
||
warning "⚠️ Remote-Zugang-Tests mit Problemen"
|
||
system_tests_passed=false
|
||
fi
|
||
|
||
# Aufräumen
|
||
cleanup_old_files
|
||
|
||
# Finale Status-Bewertung
|
||
if [ "$system_tests_passed" = true ]; then
|
||
success "✅ Vollständige robuste Kiosk-Installation erfolgreich abgeschlossen!"
|
||
else
|
||
warning "⚠️ Vollständige Kiosk-Installation mit Einschränkungen abgeschlossen"
|
||
info "→ Grundfunktionalität sollte verfügbar sein, manuelle Überprüfung empfohlen"
|
||
fi
|
||
|
||
log "📋 Vollständige Installation Zusammenfassung:"
|
||
log " 🖥️ Hostname: raspberrypi"
|
||
log " 🔐 Kiosk-Modus: Konfiguriert (startet beim nächsten Boot)"
|
||
log " 📡 SSH-Zugang: user:raspberry (Port 22)"
|
||
log " 🖥️ RDP-Zugang: root:744563017196A (Port 3389)"
|
||
log " 🔒 Firewall: Konfiguriert und aktiv"
|
||
log " ⚡ Performance: Optimiert für Raspberry Pi"
|
||
log " 🌐 HTTPS-Backend: $HTTPS_URL"
|
||
log " 🛡️ Sicherheit: IPv6 deaktiviert, erweiterte Netzwerk-Sicherheit"
|
||
info ""
|
||
success "🚀 Produktionssystem vollständig einsatzbereit!"
|
||
warning "⚠️ NEUSTART ERFORDERLICH für automatischen Kiosk-Start: sudo reboot"
|
||
|
||
# Fehler-Zusammenfassung anzeigen
|
||
show_error_summary
|
||
}
|
||
|
||
# =========================== RDP & SSH ZUGANG ===========================
|
||
install_remote_access() {
|
||
log "=== INSTALLIERE REMOTE-ZUGANG (RDP & SSH) ==="
|
||
|
||
# SSH-Server installieren und konfigurieren
|
||
progress "Installiere und konfiguriere SSH-Server..."
|
||
apt-get install -y openssh-server || error "SSH-Server Installation fehlgeschlagen"
|
||
|
||
# SSH-Service aktivieren
|
||
systemctl enable ssh
|
||
systemctl start ssh
|
||
|
||
# SSH-Benutzer 'user' erstellen (falls nicht vorhanden)
|
||
if ! id "user" &>/dev/null; then
|
||
progress "Erstelle SSH-Benutzer: user"
|
||
useradd -m -s /bin/bash user || error "Kann SSH-Benutzer nicht erstellen"
|
||
echo "user:raspberry" | chpasswd || error "Kann Passwort für SSH-Benutzer nicht setzen"
|
||
usermod -aG sudo user 2>/dev/null || true
|
||
|
||
# pip-Konfiguration für SSH-Benutzer
|
||
mkdir -p "/home/user/.pip" 2>/dev/null || true
|
||
cat > "/home/user/.pip/pip.conf" << 'EOF'
|
||
[global]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
timeout = 60
|
||
retries = 3
|
||
|
||
[install]
|
||
break-system-packages = true
|
||
trusted-host = pypi.org
|
||
pypi.python.org
|
||
files.pythonhosted.org
|
||
EOF
|
||
chown "user:user" "/home/user/.pip/pip.conf" 2>/dev/null || true
|
||
|
||
log "✅ SSH-Benutzer 'user' erstellt mit Passwort 'raspberry'"
|
||
else
|
||
info "SSH-Benutzer 'user' existiert bereits"
|
||
echo "user:raspberry" | chpasswd || warning "Konnte Passwort für SSH-Benutzer nicht aktualisieren"
|
||
fi
|
||
|
||
# RDP-Server (xrdp) installieren - vereinfachter Ansatz
|
||
progress "Installiere RDP-Server (xrdp) - vereinfachte Installation..."
|
||
|
||
# Alle bestehenden xrdp-Installationen entfernen
|
||
progress "Entferne vorherige xrdp-Installationen..."
|
||
systemctl stop xrdp xrdp-sesman 2>/dev/null || true
|
||
systemctl disable xrdp xrdp-sesman 2>/dev/null || true
|
||
apt-get remove --purge -y xrdp 2>/dev/null || true
|
||
rm -rf /etc/xrdp /var/log/xrdp* 2>/dev/null || true
|
||
|
||
# XFCE Desktop installieren (minimal)
|
||
progress "Installiere minimale XFCE-Umgebung..."
|
||
if ! apt-get install -y xfce4-session xfce4-panel xfce4-terminal xfce4-settings xfdesktop4 dbus-x11; then
|
||
warning "Minimale XFCE-Installation fehlgeschlagen - verwende Fallback..."
|
||
apt-get install -y xfce4 dbus-x11 || error "XFCE Installation fehlgeschlagen"
|
||
fi
|
||
|
||
# xrdp neu installieren
|
||
progress "Installiere xrdp neu..."
|
||
apt-get update
|
||
apt-get install -y xrdp || error "xrdp Installation fehlgeschlagen"
|
||
|
||
# Benutzer zur xrdp-Gruppe hinzufügen
|
||
usermod -aG xrdp root 2>/dev/null || true
|
||
if id "user" &>/dev/null; then
|
||
usermod -aG xrdp user 2>/dev/null || true
|
||
fi
|
||
|
||
# Erstelle minimale xrdp-Konfiguration
|
||
progress "Erstelle minimale xrdp-Konfiguration..."
|
||
|
||
# Backup der Original-Konfiguration
|
||
cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.original 2>/dev/null || true
|
||
|
||
# Sehr einfache xrdp.ini
|
||
cat > /etc/xrdp/xrdp.ini << 'EOF'
|
||
[Globals]
|
||
ini_version=1
|
||
fork=true
|
||
port=3389
|
||
tcp_nodelay=true
|
||
tcp_keepalive=true
|
||
security_layer=rdp
|
||
autorun=
|
||
allow_channels=true
|
||
allow_multimon=false
|
||
bitmap_cache=true
|
||
bitmap_compression=true
|
||
bulk_compression=false
|
||
max_bpp=24
|
||
new_cursors=true
|
||
use_fastpath=both
|
||
require_credentials=true
|
||
ask_for_reconnect_reason=false
|
||
enable_token_login=false
|
||
|
||
[Xorg]
|
||
name=Xorg
|
||
lib=libxup.so
|
||
username=ask
|
||
password=ask
|
||
ip=127.0.0.1
|
||
port=-1
|
||
code=20
|
||
EOF
|
||
|
||
# Einfache sesman.ini
|
||
cp /etc/xrdp/sesman.ini /etc/xrdp/sesman.ini.original 2>/dev/null || true
|
||
|
||
cat > /etc/xrdp/sesman.ini << 'EOF'
|
||
[Globals]
|
||
ListenAddress=127.0.0.1
|
||
ListenPort=3350
|
||
EnableUserWindowManager=true
|
||
UserWindowManager=startxfce4
|
||
DefaultWindowManager=startxfce4
|
||
|
||
[Security]
|
||
AllowRootLogin=true
|
||
MaxLoginRetry=4
|
||
AlwaysGroupCheck=false
|
||
|
||
[Sessions]
|
||
X11DisplayOffset=10
|
||
MaxSessions=10
|
||
KillDisconnected=false
|
||
IdleTimeLimit=0
|
||
DisconnectedTimeLimit=0
|
||
|
||
[Logging]
|
||
LogFile=xrdp-sesman.log
|
||
LogLevel=INFO
|
||
EnableSyslog=true
|
||
SyslogLevel=INFO
|
||
|
||
[Xorg]
|
||
param1=-bs
|
||
param2=-nolisten
|
||
param3=tcp
|
||
param4=-dpi
|
||
param5=96
|
||
EOF
|
||
|
||
# Erstelle .xsession für XFCE
|
||
progress "Konfiguriere XFCE-Sessions..."
|
||
|
||
# Root .xsession
|
||
cat > /root/.xsession << 'EOF'
|
||
#!/bin/bash
|
||
export XDG_SESSION_DESKTOP=xfce
|
||
export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share
|
||
export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg
|
||
exec startxfce4
|
||
EOF
|
||
chmod +x /root/.xsession
|
||
|
||
# User .xsession (falls user existiert)
|
||
if id "user" &>/dev/null; then
|
||
cat > /home/user/.xsession << 'EOF'
|
||
#!/bin/bash
|
||
export XDG_SESSION_DESKTOP=xfce
|
||
export XDG_DATA_DIRS=/usr/share/xfce4:/usr/local/share:/usr/share
|
||
export XDG_CONFIG_DIRS=/etc/xdg/xdg-xfce:/etc/xdg
|
||
exec startxfce4
|
||
EOF
|
||
chown user:user /home/user/.xsession
|
||
chmod +x /home/user/.xsession
|
||
fi
|
||
|
||
# Root-Passwort setzen
|
||
progress "Setze Root-Passwort für RDP..."
|
||
echo "root:744563017196A" | chpasswd || error "Kann Root-Passwort nicht setzen"
|
||
|
||
# Log-Verzeichnisse erstellen mit korrekten Berechtigungen
|
||
progress "Erstelle Log-Verzeichnisse..."
|
||
mkdir -p /var/log
|
||
touch /var/log/xrdp.log /var/log/xrdp-sesman.log 2>/dev/null || true
|
||
chown xrdp:xrdp /var/log/xrdp*.log 2>/dev/null || true
|
||
chmod 644 /var/log/xrdp*.log 2>/dev/null || true
|
||
|
||
# Erstelle xrdp-Konfigurationsverzeichnisse
|
||
mkdir -p /etc/xrdp/cert /var/run/xrdp
|
||
chown xrdp:xrdp /etc/xrdp/cert /var/run/xrdp 2>/dev/null || true
|
||
|
||
# Services aktivieren und starten
|
||
progress "Starte xrdp-Services..."
|
||
|
||
# systemd daemon reload
|
||
systemctl daemon-reload
|
||
|
||
# Services aktivieren
|
||
systemctl enable xrdp-sesman
|
||
systemctl enable xrdp
|
||
|
||
# Services starten (sesman zuerst)
|
||
systemctl start xrdp-sesman
|
||
sleep 3
|
||
|
||
# Prüfe sesman-Status
|
||
if systemctl is-active --quiet xrdp-sesman; then
|
||
success "✅ xrdp-sesman erfolgreich gestartet"
|
||
|
||
# Jetzt xrdp starten
|
||
systemctl start xrdp
|
||
sleep 3
|
||
|
||
if systemctl is-active --quiet xrdp; then
|
||
success "✅ xrdp erfolgreich gestartet"
|
||
else
|
||
warning "⚠️ xrdp konnte nicht gestartet werden"
|
||
journalctl -u xrdp --no-pager -l | tail -10
|
||
fi
|
||
else
|
||
warning "⚠️ xrdp-sesman konnte nicht gestartet werden"
|
||
journalctl -u xrdp-sesman --no-pager -l | tail -10
|
||
fi
|
||
|
||
cd "$CURRENT_DIR"
|
||
|
||
# Finaler Status-Check
|
||
if systemctl is-active --quiet xrdp && systemctl is-active --quiet xrdp-sesman; then
|
||
log "✅ Remote-Zugang vollständig konfiguriert:"
|
||
log " 📡 SSH: user:raspberry (Port 22)"
|
||
log " 🖥️ RDP: root:744563017196A (Port 3389)"
|
||
log " 🖥️ RDP: user:raspberry (Port 3389)"
|
||
elif systemctl is-active --quiet ssh; then
|
||
log "✅ SSH-Zugang konfiguriert:"
|
||
log " 📡 SSH: user:raspberry (Port 22)"
|
||
warning "⚠️ RDP-Installation unvollständig"
|
||
info "Manuelle Überprüfung erforderlich:"
|
||
info " systemctl status xrdp"
|
||
info " systemctl status xrdp-sesman"
|
||
info " journalctl -u xrdp -f"
|
||
else
|
||
error "❌ Weder SSH noch RDP konnten konfiguriert werden"
|
||
fi
|
||
}
|
||
|
||
# =========================== FIREWALL KONFIGURATION ===========================
|
||
configure_firewall() {
|
||
log "=== KONFIGURIERE FIREWALL (firewalld) ==="
|
||
|
||
# firewalld installieren
|
||
progress "Installiere firewalld..."
|
||
apt-get install -y firewalld || error "firewalld Installation fehlgeschlagen"
|
||
|
||
# firewalld aktivieren und starten
|
||
systemctl enable firewalld
|
||
systemctl start firewalld
|
||
|
||
# Warte kurz bis firewalld vollständig gestartet ist
|
||
sleep 5
|
||
|
||
progress "Konfiguriere firewalld-Zonen und -Regeln..."
|
||
|
||
# Firewall-Status prüfen
|
||
if ! firewall-cmd --state >/dev/null 2>&1; then
|
||
error "firewalld ist nicht aktiv oder reagiert nicht"
|
||
fi
|
||
|
||
# Bestehende Zone entfernen falls vorhanden
|
||
progress "Entferne bestehende myp-backend Zone falls vorhanden..."
|
||
if firewall-cmd --permanent --get-zones | grep -q "myp-backend"; then
|
||
log "Entferne bestehende myp-backend Zone..."
|
||
firewall-cmd --permanent --delete-zone=myp-backend 2>/dev/null || true
|
||
firewall-cmd --reload
|
||
sleep 2
|
||
fi
|
||
|
||
# Zone neu erstellen
|
||
progress "Erstelle neue myp-backend Zone..."
|
||
if ! firewall-cmd --permanent --new-zone=myp-backend; then
|
||
error "Fehler beim Erstellen der myp-backend Zone"
|
||
fi
|
||
|
||
# Konfiguration neu laden
|
||
firewall-cmd --reload
|
||
sleep 2
|
||
|
||
# Erweiterte Netzwerk-Quellen definieren (nur IPv4)
|
||
progress "Füge Netzwerk-Quellen hinzu..."
|
||
firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/16 || error "Fehler beim Hinzufügen des 192.168.0.0/16 Netzwerks"
|
||
firewall-cmd --permanent --zone=myp-backend --add-source=127.0.0.1/32 || error "Fehler beim Hinzufügen von localhost"
|
||
|
||
# Lokaler Hostname "raspberrypi" hinzufügen
|
||
local local_hostname="raspberrypi"
|
||
progress "Füge lokalen Hostname hinzu: $local_hostname"
|
||
local local_ip=$(getent hosts "$local_hostname" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
||
if [ -n "$local_ip" ]; then
|
||
firewall-cmd --permanent --zone=myp-backend --add-source="$local_ip/32" 2>/dev/null || warning "Konnte lokalen Hostname nicht hinzufügen"
|
||
log "✅ Lokaler Hostname $local_hostname hinzugefügt: $local_ip"
|
||
else
|
||
info "Lokaler Hostname $local_hostname nicht auflösbar - wird beim nächsten Boot verfügbar sein"
|
||
fi
|
||
|
||
# Frontend-Server m040tbaraspi001 hinzufügen (falls auflösbar)
|
||
progress "Füge Frontend-Server hinzu: m040tbaraspi001"
|
||
local frontend_ip=$(getent hosts "m040tbaraspi001" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
||
if [ -n "$frontend_ip" ]; then
|
||
firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_ip/32" 2>/dev/null || warning "Konnte Frontend-Server IP nicht hinzufügen"
|
||
log "✅ Frontend-Server m040tbaraspi001 hinzugefügt: $frontend_ip"
|
||
else
|
||
# Versuche auch mit FQDN
|
||
local frontend_fqdn_ip=$(getent hosts "m040tbaraspi001.de040.corpintra.net" | awk '{print $1}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 2>/dev/null || true)
|
||
if [ -n "$frontend_fqdn_ip" ]; then
|
||
firewall-cmd --permanent --zone=myp-backend --add-source="$frontend_fqdn_ip/32" 2>/dev/null || warning "Konnte Frontend-Server FQDN nicht hinzufügen"
|
||
log "✅ Frontend-Server m040tbaraspi001.de040.corpintra.net hinzugefügt: $frontend_fqdn_ip"
|
||
else
|
||
info "Frontend-Server m040tbaraspi001 nicht auflösbar - überspringe"
|
||
fi
|
||
fi
|
||
|
||
# Ports und Services hinzufügen
|
||
progress "Konfiguriere Ports und Services..."
|
||
|
||
# HTTPS für API & Kiosk zulassen
|
||
firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp || error "Fehler beim Hinzufügen von Port 443"
|
||
|
||
# SSH für Wartung
|
||
firewall-cmd --permanent --zone=myp-backend --add-service=ssh || error "Fehler beim Hinzufügen des SSH-Service"
|
||
|
||
# RDP für Remote-Desktop
|
||
firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp || error "Fehler beim Hinzufügen von Port 3389"
|
||
|
||
# IPv6 in firewalld deaktivieren
|
||
progress "Deaktiviere IPv6 in firewalld..."
|
||
firewall-cmd --permanent --set-target=DROP --zone=public --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in public Zone deaktiviert werden"
|
||
firewall-cmd --permanent --set-target=DROP --zone=myp-backend --family=ipv6 2>/dev/null || warning "IPv6 konnte nicht in myp-backend Zone deaktiviert werden"
|
||
|
||
# Default-Zone setzen
|
||
progress "Setze Default-Zone..."
|
||
firewall-cmd --set-default-zone=myp-backend || error "Fehler beim Setzen der Default-Zone"
|
||
|
||
# Änderungen übernehmen
|
||
progress "Lade Firewall-Konfiguration neu..."
|
||
firewall-cmd --reload || error "Fehler beim Neuladen der Firewall-Konfiguration"
|
||
|
||
# Kurz warten und Status prüfen
|
||
sleep 3
|
||
|
||
# Firewall-Status anzeigen
|
||
progress "Firewall-Konfiguration:"
|
||
if firewall-cmd --list-all --zone=myp-backend 2>/dev/null; then
|
||
log "✅ Firewall-Konfiguration erfolgreich angezeigt"
|
||
else
|
||
warning "⚠️ Firewall-Status konnte nicht angezeigt werden"
|
||
fi
|
||
|
||
# Finale Validierung
|
||
progress "Validiere Firewall-Konfiguration..."
|
||
local validation_errors=0
|
||
|
||
# Prüfe ob Zone existiert
|
||
if ! firewall-cmd --get-zones | grep -q "myp-backend"; then
|
||
error "Zone myp-backend wurde nicht korrekt erstellt"
|
||
((validation_errors++))
|
||
fi
|
||
|
||
# Prüfe Default-Zone
|
||
if [ "$(firewall-cmd --get-default-zone)" != "myp-backend" ]; then
|
||
warning "Default-Zone ist nicht myp-backend"
|
||
((validation_errors++))
|
||
fi
|
||
|
||
# Prüfe Ports
|
||
if ! firewall-cmd --zone=myp-backend --query-port=443/tcp 2>/dev/null; then
|
||
warning "Port 443 nicht korrekt konfiguriert"
|
||
((validation_errors++))
|
||
fi
|
||
|
||
if ! firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then
|
||
warning "Port 3389 nicht korrekt konfiguriert"
|
||
((validation_errors++))
|
||
fi
|
||
|
||
if ! firewall-cmd --zone=myp-backend --query-service=ssh 2>/dev/null; then
|
||
warning "SSH-Service nicht korrekt konfiguriert"
|
||
((validation_errors++))
|
||
fi
|
||
|
||
if [ $validation_errors -eq 0 ]; then
|
||
log "✅ Firewall konfiguriert und validiert:"
|
||
log " 🔒 Zone: myp-backend (als Default gesetzt)"
|
||
log " 🌐 Netzwerk: 192.168.0.0/16 (nur IPv4)"
|
||
log " 🏠 Localhost: 127.0.0.1"
|
||
log " 🖥️ Lokaler Host: raspberrypi"
|
||
log " 📡 Frontend-Server: m040tbaraspi001"
|
||
log " 🔌 Ports: 443/tcp (HTTPS), 22/tcp (SSH), 3389/tcp (RDP)"
|
||
log " 🚫 IPv6 vollständig blockiert"
|
||
else
|
||
warning "⚠️ Firewall-Konfiguration mit $validation_errors Fehlern abgeschlossen"
|
||
info "System funktioniert möglicherweise trotzdem - manuelle Überprüfung empfohlen"
|
||
fi
|
||
}
|
||
|
||
# =========================== REMOTE-ZUGANG TESTEN ===========================
|
||
test_remote_access() {
|
||
log "=== TESTE REMOTE-ZUGANG ==="
|
||
|
||
# SSH-Service testen
|
||
progress "Teste SSH-Service..."
|
||
if systemctl is-active --quiet ssh; then
|
||
success "✅ SSH-Service läuft"
|
||
|
||
# SSH-Port testen
|
||
if ss -tlnp | grep -q ":22 "; then
|
||
success "✅ SSH-Port 22 ist offen"
|
||
else
|
||
warning "⚠️ SSH-Port 22 nicht erreichbar"
|
||
fi
|
||
else
|
||
warning "⚠️ SSH-Service läuft nicht"
|
||
fi
|
||
|
||
# RDP-Services testen
|
||
progress "Teste RDP-Services..."
|
||
|
||
local xrdp_sesman_status="❌"
|
||
local xrdp_status="❌"
|
||
|
||
if systemctl is-active --quiet xrdp-sesman; then
|
||
xrdp_sesman_status="✅"
|
||
success "✅ xrdp-sesman läuft"
|
||
else
|
||
warning "⚠️ xrdp-sesman läuft nicht"
|
||
fi
|
||
|
||
if systemctl is-active --quiet xrdp; then
|
||
xrdp_status="✅"
|
||
success "✅ xrdp läuft"
|
||
|
||
# RDP-Port testen
|
||
if ss -tlnp | grep -q ":3389 "; then
|
||
success "✅ RDP-Port 3389 ist offen"
|
||
else
|
||
warning "⚠️ RDP-Port 3389 nicht erreichbar"
|
||
fi
|
||
else
|
||
warning "⚠️ xrdp läuft nicht"
|
||
fi
|
||
|
||
# Firewall-Status testen
|
||
progress "Teste Firewall-Status..."
|
||
if systemctl is-active --quiet firewalld; then
|
||
success "✅ Firewall läuft"
|
||
|
||
# Prüfe ob Ports offen sind
|
||
if firewall-cmd --zone=myp-backend --query-port=22/tcp 2>/dev/null; then
|
||
success "✅ SSH-Port in Firewall freigegeben"
|
||
else
|
||
warning "⚠️ SSH-Port nicht in Firewall freigegeben"
|
||
fi
|
||
|
||
if firewall-cmd --zone=myp-backend --query-port=3389/tcp 2>/dev/null; then
|
||
success "✅ RDP-Port in Firewall freigegeben"
|
||
else
|
||
warning "⚠️ RDP-Port nicht in Firewall freigegeben"
|
||
fi
|
||
else
|
||
warning "⚠️ Firewall läuft nicht"
|
||
fi
|
||
|
||
# Netzwerk-Interface testen
|
||
progress "Teste Netzwerk-Konfiguration..."
|
||
local ip_address=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1 || echo "Unbekannt")
|
||
if [ "$ip_address" != "Unbekannt" ]; then
|
||
success "✅ Netzwerk-Interface aktiv: $ip_address"
|
||
info "Zugang-URLs:"
|
||
info " SSH: ssh user@$ip_address"
|
||
if [ "$xrdp_status" = "✅" ]; then
|
||
info " RDP: $ip_address:3389 (root:744563017196A oder user:raspberry)"
|
||
fi
|
||
else
|
||
warning "⚠️ Keine Netzwerk-IP ermittelt"
|
||
fi
|
||
|
||
# Status-Zusammenfassung
|
||
log "📊 Service-Status:"
|
||
log " SSH: $(systemctl is-active --quiet ssh && echo "✅ Aktiv" || echo "❌ Inaktiv")"
|
||
log " xrdp-sesman: $xrdp_sesman_status $(systemctl is-active --quiet xrdp-sesman && echo "Aktiv" || echo "Inaktiv")"
|
||
log " xrdp: $xrdp_status $(systemctl is-active --quiet xrdp && echo "Aktiv" || echo "Inaktiv")"
|
||
|
||
log "✅ Remote-Zugang-Test abgeschlossen"
|
||
}
|
||
|
||
# =========================== HOSTNAME KONFIGURATION ===========================
|
||
configure_hostname() {
|
||
log "=== KONFIGURIERE HOSTNAME ==="
|
||
|
||
local target_hostname="raspberrypi"
|
||
local current_hostname=$(hostname)
|
||
|
||
if [ "$current_hostname" != "$target_hostname" ]; then
|
||
progress "Setze Hostname von '$current_hostname' auf '$target_hostname'..."
|
||
|
||
# Hostname sofort setzen
|
||
hostnamectl set-hostname "$target_hostname" || error "Fehler beim Setzen des Hostnames"
|
||
|
||
# /etc/hostname aktualisieren
|
||
echo "$target_hostname" > /etc/hostname
|
||
|
||
# /etc/hosts aktualisieren
|
||
cp /etc/hosts /etc/hosts.backup
|
||
sed -i "s/127.0.1.1.*/127.0.1.1\t$target_hostname/" /etc/hosts
|
||
|
||
# Falls kein 127.0.1.1 Eintrag existiert, hinzufügen
|
||
if ! grep -q "127.0.1.1" /etc/hosts; then
|
||
echo "127.0.1.1 $target_hostname" >> /etc/hosts
|
||
fi
|
||
|
||
log "✅ Hostname erfolgreich auf '$target_hostname' gesetzt"
|
||
else
|
||
log "✅ Hostname bereits korrekt: '$target_hostname'"
|
||
fi
|
||
|
||
# Hostname-Auflösung testen
|
||
if getent hosts "$target_hostname" >/dev/null 2>&1; then
|
||
local resolved_ip=$(getent hosts "$target_hostname" | awk '{print $1}' | head -1)
|
||
log "✅ Hostname-Auflösung funktioniert: $target_hostname -> $resolved_ip"
|
||
else
|
||
warning "⚠️ Hostname-Auflösung für '$target_hostname' fehlgeschlagen"
|
||
fi
|
||
}
|
||
|
||
# =========================== WEBAPP PERFORMANCE-OPTIMIERUNG ===========================
|
||
optimize_webapp_performance() {
|
||
log "=== WEBAPP PERFORMANCE-OPTIMIERUNG FÜR RASPBERRY PI ==="
|
||
|
||
# Python/Flask spezifische Optimierungen
|
||
progress "Konfiguriere Python-Performance-Optimierungen..."
|
||
|
||
# Python Bytecode Optimierung aktivieren
|
||
cat > /etc/environment << 'EOF'
|
||
# Python Performance Optimierungen
|
||
PYTHONOPTIMIZE=2
|
||
PYTHONDONTWRITEBYTECODE=1
|
||
PYTHONUNBUFFERED=1
|
||
PYTHONHASHSEED=random
|
||
|
||
# Flask/SQLite Optimierungen
|
||
FLASK_ENV=production
|
||
FLASK_DEBUG=0
|
||
SQLITE_TMPDIR=/tmp
|
||
|
||
# Memory Optimierungen
|
||
MALLOC_ARENA_MAX=2
|
||
MALLOC_MMAP_THRESHOLD=131072
|
||
MALLOC_TRIM_THRESHOLD=131072
|
||
|
||
EOF
|
||
|
||
# Systemd Service-Optimierungen
|
||
progress "Optimiere Systemd-Services für bessere Performance..."
|
||
|
||
# Stoppe unnötige Services
|
||
local unnecessary_services=(
|
||
"bluetooth.service"
|
||
"hciuart.service"
|
||
"avahi-daemon.service"
|
||
"cups.service"
|
||
"cups-browsed.service"
|
||
"ModemManager.service"
|
||
"wpa_supplicant.service"
|
||
)
|
||
|
||
for service in "${unnecessary_services[@]}"; do
|
||
if systemctl is-enabled "$service" 2>/dev/null; then
|
||
systemctl disable "$service" 2>/dev/null || true
|
||
systemctl stop "$service" 2>/dev/null || true
|
||
log "✅ Service deaktiviert: $service"
|
||
fi
|
||
done
|
||
|
||
# Tmpfs für temporäre Dateien
|
||
progress "Konfiguriere tmpfs für bessere I/O Performance..."
|
||
|
||
cat >> /etc/fstab << 'EOF'
|
||
|
||
# MYP Performance Optimierungen - tmpfs für temporäre Dateien
|
||
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=256m 0 0
|
||
tmpfs /var/tmp tmpfs defaults,noatime,nosuid,size=128m 0 0
|
||
tmpfs /var/log tmpfs defaults,noatime,nosuid,size=64m 0 0
|
||
|
||
EOF
|
||
|
||
# Logrotate für tmpfs-Logs konfigurieren
|
||
cat > /etc/logrotate.d/myp-tmpfs << 'EOF'
|
||
/var/log/*.log {
|
||
daily
|
||
missingok
|
||
rotate 2
|
||
compress
|
||
notifempty
|
||
create 0644 root root
|
||
copytruncate
|
||
}
|
||
EOF
|
||
|
||
# Systemd Journal Einstellungen optimieren
|
||
progress "Optimiere systemd Journal für bessere Performance..."
|
||
|
||
mkdir -p /etc/systemd/journald.conf.d
|
||
cat > /etc/systemd/journald.conf.d/myp-performance.conf << 'EOF'
|
||
[Journal]
|
||
# Journal Optimierungen für Raspberry Pi
|
||
Storage=volatile
|
||
RuntimeMaxUse=32M
|
||
RuntimeKeepFree=16M
|
||
RuntimeMaxFileSize=8M
|
||
RuntimeMaxFiles=4
|
||
MaxRetentionSec=1day
|
||
MaxFileSec=1hour
|
||
ForwardToSyslog=no
|
||
ForwardToKMsg=no
|
||
ForwardToConsole=no
|
||
ForwardToWall=no
|
||
EOF
|
||
|
||
# Crontab für regelmäßige Cache-Bereinigung
|
||
progress "Installiere automatische Cache-Bereinigung..."
|
||
|
||
cat > /etc/cron.d/myp-cache-cleanup << 'EOF'
|
||
# MYP Cache und Memory Cleanup
|
||
# Alle 6 Stunden Cache bereinigen
|
||
0 */6 * * * root /bin/echo 3 > /proc/sys/vm/drop_caches
|
||
# Täglich um 3 Uhr temporäre Dateien bereinigen
|
||
0 3 * * * root /usr/bin/find /tmp -type f -atime +1 -delete 2>/dev/null
|
||
# Wöchentlich Python Cache bereinigen
|
||
0 2 * * 0 root /usr/bin/find /opt/myp -name "*.pyc" -delete 2>/dev/null
|
||
0 2 * * 0 root /usr/bin/find /opt/myp -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null
|
||
EOF
|
||
|
||
# Limits für bessere Ressourcen-Verwaltung
|
||
progress "Konfiguriere System-Limits..."
|
||
|
||
cat >> /etc/security/limits.conf << 'EOF'
|
||
|
||
# MYP Performance Limits
|
||
* soft nofile 65536
|
||
* hard nofile 65536
|
||
* soft nproc 32768
|
||
* hard nproc 32768
|
||
root soft nofile 65536
|
||
root hard nofile 65536
|
||
|
||
EOF
|
||
|
||
# Apache/Nginx entfernen falls vorhanden (Konflikt mit Flask)
|
||
progress "Entferne konfliktbehaftete Webserver..."
|
||
|
||
local webservers=("apache2" "nginx" "lighttpd")
|
||
for webserver in "${webservers[@]}"; do
|
||
if systemctl is-enabled "$webserver" 2>/dev/null; then
|
||
systemctl stop "$webserver" 2>/dev/null || true
|
||
systemctl disable "$webserver" 2>/dev/null || true
|
||
apt-get remove --purge -y "$webserver" 2>/dev/null || true
|
||
log "✅ Webserver entfernt: $webserver"
|
||
fi
|
||
done
|
||
|
||
log "✅ Webapp Performance-Optimierung abgeschlossen:"
|
||
log " 🚀 Python Bytecode-Optimierung aktiviert"
|
||
log " 💾 tmpfs für temporäre Dateien konfiguriert"
|
||
log " 📝 Journal-Logging optimiert"
|
||
log " 🧹 Automatische Cache-Bereinigung installiert"
|
||
log " ⚡ Unnötige Services deaktiviert"
|
||
log " 📊 System-Limits für bessere Performance gesetzt"
|
||
}
|
||
|
||
# =========================== CSS/JS OPTIMIERUNG ===========================
|
||
optimize_static_assets() {
|
||
log "=== STATISCHE DATEIEN OPTIMIERUNG ==="
|
||
|
||
if [ ! -d "$APP_DIR/static" ]; then
|
||
warning "Static-Ordner nicht gefunden - überspringe Asset-Optimierung"
|
||
return
|
||
fi
|
||
|
||
progress "Analysiere und optimiere CSS/JS Dateien..."
|
||
|
||
cd "$APP_DIR/static"
|
||
|
||
# Erstelle optimierte CSS-Datei durch Kombination kritischer Styles
|
||
progress "Erstelle optimierte CSS-Kombination..."
|
||
|
||
cat > css/critical.min.css << 'EOF'
|
||
/* Kritische Styles für ersten Seitenaufbau - Inline-optimiert */
|
||
*{box-sizing:border-box}body{margin:0;font-family:system-ui,-apple-system,sans-serif;line-height:1.5}
|
||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}
|
||
.btn{display:inline-flex;align-items:center;padding:0.5rem 1rem;border:none;border-radius:0.375rem;font-weight:500;text-decoration:none;cursor:pointer;transition:all 0.15s}
|
||
.btn-primary{background:#3b82f6;color:white}.btn-primary:hover{background:#2563eb}
|
||
.card{background:white;border-radius:0.5rem;padding:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,0.1)}
|
||
.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}
|
||
.hidden{display:none}.block{display:block}.inline-block{display:inline-block}
|
||
.text-sm{font-size:0.875rem}.text-lg{font-size:1.125rem}
|
||
.font-medium{font-weight:500}.font-bold{font-weight:700}
|
||
.text-gray-600{color:#4b5563}.text-gray-900{color:#111827}
|
||
.mb-4{margin-bottom:1rem}.mt-6{margin-top:1.5rem}.p-4{padding:1rem}
|
||
.w-full{width:100%}.h-full{height:100%}
|
||
@media(max-width:768px){.container{padding:0 0.5rem}.card{padding:1rem}}
|
||
EOF
|
||
|
||
# Erstelle minimale JavaScript-Loader
|
||
progress "Erstelle optimierten JavaScript-Loader..."
|
||
|
||
cat > js/loader.min.js << 'EOF'
|
||
/*Minimaler Async Loader für bessere Performance*/
|
||
(function(){var d=document,w=window;function loadCSS(href){var l=d.createElement('link');l.rel='stylesheet';l.href=href;l.media='print';l.onload=function(){this.media='all'};d.head.appendChild(l)}function loadJS(src,cb){var s=d.createElement('script');s.async=true;s.src=src;if(cb)s.onload=cb;d.head.appendChild(s)}w.loadAssets=function(){if(w.assetsLoaded)return;w.assetsLoaded=true;loadCSS('/static/css/tailwind.min.css');loadJS('/static/js/app.min.js')};if(d.readyState==='loading'){d.addEventListener('DOMContentLoaded',w.loadAssets)}else{w.loadAssets()}})();
|
||
EOF
|
||
|
||
# Service Worker für besseres Caching
|
||
progress "Erstelle optimierten Service Worker..."
|
||
|
||
cat > sw-optimized.js << 'EOF'
|
||
const CACHE_NAME = 'myp-webapp-v1';
|
||
const ASSETS_TO_CACHE = [
|
||
'/',
|
||
'/static/css/critical.min.css',
|
||
'/static/js/loader.min.js',
|
||
'/static/favicon.svg'
|
||
];
|
||
|
||
self.addEventListener('install', event => {
|
||
event.waitUntil(
|
||
caches.open(CACHE_NAME)
|
||
.then(cache => cache.addAll(ASSETS_TO_CACHE))
|
||
);
|
||
});
|
||
|
||
self.addEventListener('fetch', event => {
|
||
if (event.request.destination === 'image' ||
|
||
event.request.url.includes('/static/')) {
|
||
event.respondWith(
|
||
caches.match(event.request)
|
||
.then(response => response || fetch(event.request))
|
||
);
|
||
}
|
||
});
|
||
EOF
|
||
|
||
# Gzip-Kompression für statische Dateien
|
||
progress "Komprimiere statische Dateien..."
|
||
|
||
find . -name "*.css" -o -name "*.js" -o -name "*.html" | while read file; do
|
||
if [ -f "$file" ] && [ ! -f "$file.gz" ]; then
|
||
gzip -c "$file" > "$file.gz" 2>/dev/null || true
|
||
fi
|
||
done
|
||
|
||
cd "$CURRENT_DIR"
|
||
|
||
log "✅ Statische Dateien optimiert:"
|
||
log " 📦 Kritische CSS-Styles kombiniert"
|
||
log " ⚡ Asynchroner Asset-Loader erstellt"
|
||
log " 🗜️ Gzip-Kompression angewendet"
|
||
log " 🔄 Service Worker für Caching installiert"
|
||
}
|
||
|
||
# =========================== HAUPTPROGRAMM ===========================
|
||
main() {
|
||
# Erstelle logs-Verzeichnis im aktuellen Projektverzeichnis
|
||
mkdir -p "logs" 2>/dev/null || true
|
||
|
||
while true; do
|
||
show_menu
|
||
read -r choice
|
||
|
||
case $choice in
|
||
1)
|
||
install_dependencies_only
|
||
echo ""
|
||
echo -e "${BLUE}📁 Log-Dateien zur Überprüfung:${NC}"
|
||
echo -e " 📄 Vollständiges Log: $INSTALL_LOG"
|
||
[ $ERROR_COUNT -gt 0 ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG"
|
||
[ $WARNING_COUNT -gt 0 ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG"
|
||
echo -e " 🔍 Debug-Log: $DEBUG_LOG"
|
||
echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt"
|
||
echo ""
|
||
echo -n "Drücken Sie Enter um fortzufahren..."
|
||
read -r
|
||
;;
|
||
2)
|
||
install_full_production_system
|
||
echo ""
|
||
echo -e "${BLUE}📁 Log-Dateien zur Überprüfung:${NC}"
|
||
echo -e " 📄 Vollständiges Log: $INSTALL_LOG"
|
||
[ $ERROR_COUNT -gt 0 ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG"
|
||
[ $WARNING_COUNT -gt 0 ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG"
|
||
echo -e " 🔍 Debug-Log: $DEBUG_LOG"
|
||
echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt"
|
||
echo ""
|
||
echo -n "Drücken Sie Enter um fortzufahren..."
|
||
read -r
|
||
;;
|
||
3)
|
||
echo ""
|
||
echo -e "${CYAN}Setup-Skript beendet${NC}"
|
||
if [ -f "$INSTALL_LOG" ]; then
|
||
echo -e "${BLUE}📁 Verfügbare Log-Dateien:${NC}"
|
||
echo -e " 📄 Vollständiges Log: $INSTALL_LOG"
|
||
[ -f "$ERROR_LOG" ] && echo -e " 🚨 Fehler-Log: $ERROR_LOG"
|
||
[ -f "$WARNING_LOG" ] && echo -e " ⚠️ Warnungs-Log: $WARNING_LOG"
|
||
[ -f "$DEBUG_LOG" ] && echo -e " 🔍 Debug-Log: $DEBUG_LOG"
|
||
[ -f "logs/myp-install-summary.txt" ] && echo -e " 📊 Zusammenfassung: logs/myp-install-summary.txt"
|
||
fi
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo -e "${RED}Ungültige Auswahl. Bitte wählen Sie 1-3.${NC}"
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# Skript starten
|
||
main "$@" |