#!/bin/bash # =================================================================== # MYP Frontend - KONSOLIDIERTES SETUP-SKRIPT # Automatische Installation und Konfiguration des Frontend-Servers # Optimiert für Debian/Linux mit Docker und Caddy Reverse Proxy # HTTPS mit Mercedes SSL-Zertifikaten auf m040tbaraspi001.de040.corpintra.net # Version: 1.0.0 # =================================================================== set -euo pipefail # =========================== GLOBALE KONFIGURATION =========================== readonly APP_NAME="MYP Frontend" readonly APP_VERSION="1.0.0" readonly FRONTEND_DIR="/opt/myp-frontend" readonly SSL_DIR="/etc/ssl/certs/myp" readonly DOCKER_COMPOSE_SERVICE="myp-frontend" readonly DOMAIN="m040tbaraspi001.de040.corpintra.net" readonly CURRENT_DIR="$(pwd)" readonly INSTALL_LOG="/var/log/myp-frontend-install.log" readonly CADDY_LOG_DIR="/var/log/caddy" # 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' # =========================== LOGGING-FUNKTIONEN =========================== log() { echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$INSTALL_LOG" } error() { echo -e "${RED}[FEHLER] $1${NC}" | tee -a "$INSTALL_LOG" exit 1 } warning() { echo -e "${YELLOW}[WARNUNG] $1${NC}" | tee -a "$INSTALL_LOG" } info() { echo -e "${BLUE}[INFO] $1${NC}" | tee -a "$INSTALL_LOG" } progress() { echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG" } success() { echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG" } # =========================== 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_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 || echo "Unbekannt") info "Raspberry Pi Modell: $pi_model" fi } check_internet_connection() { progress "Prüfe Internetverbindung..." local test_urls=("8.8.8.8" "1.1.1.1" "google.com") local connection_ok=false for url in "${test_urls[@]}"; do if ping -c 1 -W 3 "$url" >/dev/null 2>&1; then connection_ok=true break fi done if [ "$connection_ok" = true ]; then log "✅ Internetverbindung verfügbar" else warning "⚠️ Keine Internetverbindung - Installation könnte fehlschlagen" fi } # =========================== SYSTEM-VORBEREITUNG =========================== update_system() { log "=== SYSTEM-UPDATE ===" progress "Aktualisiere Paketlisten..." apt-get update -y || error "APT Update fehlgeschlagen" progress "Installiere grundlegende System-Tools..." apt-get install -y \ curl \ wget \ git \ nano \ htop \ rsync \ unzip \ sudo \ ca-certificates \ gnupg \ lsb-release \ apt-transport-https \ software-properties-common \ openssl \ || error "Grundlegende Tools Installation fehlgeschlagen" log "✅ System-Update abgeschlossen" } # =========================== DOCKER INSTALLATION =========================== install_docker() { log "=== DOCKER INSTALLATION ===" # Prüfe ob Docker bereits installiert ist if command -v docker >/dev/null 2>&1; then info "Docker ist bereits installiert" docker --version else progress "Installiere Docker..." # Docker GPG-Schlüssel hinzufügen curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # Docker Repository hinzufügen echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null # Paketlisten aktualisieren und Docker installieren apt-get update -y apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin # Docker-Service aktivieren systemctl enable docker systemctl start docker log "✅ Docker erfolgreich installiert" fi # Prüfe ob Docker Compose bereits installiert ist if command -v docker compose >/dev/null 2>&1; then info "Docker Compose ist bereits installiert" docker compose version else progress "Installiere Docker Compose..." apt-get install -y docker-compose-plugin || error "Docker Compose Installation fehlgeschlagen" log "✅ Docker Compose erfolgreich installiert" fi # Docker-Service Status prüfen if systemctl is-active --quiet docker; then success "✅ Docker läuft erfolgreich" else error "❌ Docker konnte nicht gestartet werden" fi } # =========================== SSL-ZERTIFIKAT GENERIERUNG =========================== generate_mercedes_ssl_certificate() { log "=== MERCEDES SSL-ZERTIFIKAT GENERIERUNG ===" progress "Erstelle SSL-Verzeichnis..." mkdir -p "$SSL_DIR" progress "Generiere Mercedes SSL-Zertifikat für $DOMAIN..." # Erstelle OpenSSL-Konfigurationsdatei für Subject Alternative Names cat > "$SSL_DIR/openssl.conf" << EOF [req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = DE ST = Baden-Wuerttemberg L = Stuttgart O = Mercedes-Benz AG OU = IT-Abteilung CN = $DOMAIN [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = $DOMAIN DNS.2 = m040tbaraspi001 DNS.3 = localhost DNS.4 = raspberrypi IP.1 = 127.0.0.1 IP.2 = 192.168.0.109 EOF # Generiere privaten Schlüssel progress "Generiere privaten Schlüssel..." openssl genrsa -out "$SSL_DIR/frontend.key" 4096 || error "Fehler beim Generieren des privaten Schlüssels" # Generiere Zertifikat mit SAN (Subject Alternative Names) progress "Generiere SSL-Zertifikat..." openssl req -new -x509 -key "$SSL_DIR/frontend.key" -out "$SSL_DIR/frontend.crt" -days 365 \ -config "$SSL_DIR/openssl.conf" \ -extensions v3_req \ || error "Fehler beim Generieren des SSL-Zertifikats" # Berechtigungen setzen chmod 600 "$SSL_DIR/frontend.key" chmod 644 "$SSL_DIR/frontend.crt" # Zertifikat in System-CA-Store hinzufügen progress "Füge Zertifikat zum System-CA-Store hinzu..." cp "$SSL_DIR/frontend.crt" "/usr/local/share/ca-certificates/$DOMAIN.crt" update-ca-certificates # Zertifikat-Informationen anzeigen info "Zertifikat-Details:" openssl x509 -in "$SSL_DIR/frontend.crt" -text -noout | grep -E "(Subject:|DNS:|IP Address:)" || true log "✅ Mercedes SSL-Zertifikat erfolgreich generiert" } # =========================== ANWENDUNG DEPLOYMENT =========================== deploy_frontend_application() { log "=== FRONTEND-ANWENDUNG DEPLOYMENT ===" progress "Erstelle Frontend-Verzeichnis..." mkdir -p "$FRONTEND_DIR" progress "Kopiere Frontend-Dateien..." rsync -av --exclude=node_modules --exclude=.git --exclude=ssl "$CURRENT_DIR/" "$FRONTEND_DIR/" # Stelle sicher, dass die richtigen Berechtigungen gesetzt sind chown -R root:root "$FRONTEND_DIR" chmod -R 755 "$FRONTEND_DIR" log "✅ Frontend-Anwendung erfolgreich deployed" } # =========================== CADDY LOG-VERZEICHNIS =========================== create_caddy_logs() { log "=== CADDY LOG-VERZEICHNISSE ERSTELLEN ===" progress "Erstelle Caddy Log-Verzeichnisse..." mkdir -p "$CADDY_LOG_DIR" chmod 755 "$CADDY_LOG_DIR" # Erstelle leere Log-Dateien touch "$CADDY_LOG_DIR/access.log" touch "$CADDY_LOG_DIR/error.log" chmod 644 "$CADDY_LOG_DIR"/*.log log "✅ Caddy Log-Verzeichnisse erstellt" } # =========================== DOCKER COMPOSE KONFIGURATION =========================== create_docker_compose_config() { log "=== DOCKER COMPOSE KONFIGURATION ===" progress "Erstelle Docker Compose Konfiguration..." cat > "$FRONTEND_DIR/docker-compose.yml" << EOF version: '3.8' services: # Frontend Next.js Application frontend-app: build: context: . dockerfile: Dockerfile container_name: myp-frontend-app environment: - NODE_ENV=production - NEXT_TELEMETRY_DISABLED=1 networks: - myp-network restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 depends_on: - db # Caddy Reverse Proxy caddy: image: caddy:latest container_name: myp-caddy ports: - "80:80" - "443:443" volumes: - ./docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro - $SSL_DIR:/etc/ssl/certs/myp:ro - $CADDY_LOG_DIR:/var/log/caddy - caddy_data:/data - caddy_config:/config networks: - myp-network restart: unless-stopped healthcheck: test: ["CMD", "caddy", "version"] interval: 30s timeout: 10s retries: 3 depends_on: - frontend-app # Database (SQLite with volume for persistence) db: image: alpine:latest container_name: myp-db volumes: - db_data:/data command: ["sh", "-c", "mkdir -p /data && tail -f /dev/null"] networks: - myp-network restart: unless-stopped volumes: caddy_data: driver: local caddy_config: driver: local db_data: driver: local networks: myp-network: driver: bridge EOF log "✅ Docker Compose Konfiguration erstellt" } # =========================== SYSTEMD SERVICE =========================== create_systemd_service() { log "=== SYSTEMD SERVICE KONFIGURATION ===" progress "Erstelle systemd Service für Frontend..." cat > "/etc/systemd/system/$DOCKER_COMPOSE_SERVICE.service" << EOF [Unit] Description=MYP Frontend Docker Compose Service Requires=docker.service After=docker.service StartLimitIntervalSec=0 [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=$FRONTEND_DIR ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down ExecReload=/usr/bin/docker compose restart TimeoutStartSec=300 TimeoutStopSec=120 Restart=on-failure RestartSec=30 User=root Group=root # Environment Environment=COMPOSE_PROJECT_NAME=myp-frontend # Sicherheitseinstellungen NoNewPrivileges=yes PrivateTmp=yes ProtectHome=yes ProtectSystem=strict ReadWritePaths=$FRONTEND_DIR $SSL_DIR $CADDY_LOG_DIR [Install] WantedBy=multi-user.target EOF # Systemd-Konfiguration neu laden systemctl daemon-reload log "✅ Systemd Service erstellt" } # =========================== SERVICES STARTEN =========================== start_frontend_services() { log "=== FRONTEND-SERVICES STARTEN ===" progress "Wechsle in Frontend-Verzeichnis..." cd "$FRONTEND_DIR" # Docker Images erstellen progress "Erstelle Docker Images..." docker compose build || error "Docker Build fehlgeschlagen" # Services aktivieren und starten progress "Aktiviere und starte Frontend-Service..." systemctl enable "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Aktivieren des Frontend-Service" systemctl start "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Starten des Frontend-Service" # Warte auf Service-Start progress "Warte auf Service-Start..." sleep 30 # Service-Status prüfen if systemctl is-active --quiet "$DOCKER_COMPOSE_SERVICE"; then success "✅ Frontend-Service läuft erfolgreich" else error "❌ Frontend-Service konnte nicht gestartet werden" fi # Docker Container-Status prüfen progress "Prüfe Container-Status..." docker compose ps cd "$CURRENT_DIR" log "✅ Frontend-Services erfolgreich gestartet" } # =========================== SYSTEM-TEST =========================== test_frontend_application() { log "=== FRONTEND-SYSTEM-TEST ===" progress "Teste HTTPS-Verbindung zu $DOMAIN..." # Warte auf Service-Start local max_attempts=60 local attempt=1 while [ $attempt -le $max_attempts ]; do if curl -k -s --connect-timeout 5 "https://$DOMAIN/health" >/dev/null 2>&1; then success "✅ Frontend erreichbar unter https://$DOMAIN" break fi # Teste auch localhost if curl -k -s --connect-timeout 5 "https://localhost/health" >/dev/null 2>&1; then success "✅ Frontend erreichbar unter https://localhost" break fi progress "Warte auf Frontend... ($attempt/$max_attempts)" sleep 5 ((attempt++)) done if [ $attempt -gt $max_attempts ]; then error "❌ Frontend nicht erreichbar nach $max_attempts Versuchen" fi # Teste SSL-Zertifikat progress "Teste SSL-Zertifikat..." if openssl s_client -connect localhost:443 -servername "$DOMAIN" /dev/null | openssl x509 -noout -text >/dev/null 2>&1; then success "✅ SSL-Zertifikat gültig" else warning "⚠️ SSL-Zertifikat-Test fehlgeschlagen" fi # Teste Container-Gesundheit progress "Teste Container-Gesundheit..." cd "$FRONTEND_DIR" local unhealthy_containers=$(docker compose ps --format json | jq -r '.[] | select(.Health == "unhealthy") | .Name' 2>/dev/null || echo "") if [ -n "$unhealthy_containers" ]; then warning "⚠️ Ungesunde Container gefunden: $unhealthy_containers" else success "✅ Alle Container sind gesund" fi cd "$CURRENT_DIR" log "✅ System-Test abgeschlossen" } # =========================== FIREWALL-KONFIGURATION =========================== configure_frontend_firewall() { log "=== FIREWALL-KONFIGURATION ===" progress "Konfiguriere UFW-Firewall für Frontend..." # UFW installieren falls nicht vorhanden if ! command -v ufw >/dev/null 2>&1; then apt-get install -y ufw || error "UFW Installation fehlgeschlagen" fi # UFW zurücksetzen ufw --force reset # Standard-Richtlinien ufw default deny incoming ufw default allow outgoing # SSH erlauben (wichtig für Remote-Zugang) ufw allow ssh # HTTP und HTTPS erlauben ufw allow 80/tcp ufw allow 443/tcp # Lokale Verbindungen erlauben ufw allow from 127.0.0.1 ufw allow from ::1 # Internes Netzwerk erlauben (Mercedes-Netzwerk) ufw allow from 192.168.0.0/16 ufw allow from 10.0.0.0/8 ufw allow from 172.16.0.0/12 # UFW aktivieren ufw --force enable # Firewall-Status anzeigen ufw status verbose log "✅ Firewall für Frontend konfiguriert" } # =========================== 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} Vollständige Frontend-Installation" echo -e " ${BLUE}→ Docker, SSL-Zertifikate, Caddy Reverse Proxy${NC}" echo -e " ${BLUE}→ Frontend verfügbar unter https://$DOMAIN${NC}" echo -e " ${BLUE}→ Automatischer Start beim Boot${NC}" echo "" echo -e "${GREEN}2)${NC} Nur SSL-Zertifikate neu generieren" echo -e " ${BLUE}→ Erstellt neue Mercedes SSL-Zertifikate${NC}" echo -e " ${BLUE}→ Startet Services neu${NC}" echo "" echo -e "${GREEN}3)${NC} Service-Status prüfen" echo -e " ${BLUE}→ Zeigt Status aller Frontend-Services${NC}" echo -e " ${BLUE}→ Container-Logs und Gesundheitsprüfung${NC}" echo "" echo -e "${GREEN}4)${NC} Beenden" echo "" echo -e "${CYAN}=================================================================${NC}" echo -n "Ihre Wahl [1-4]: " } # =========================== INSTALLATIONS-MODI =========================== install_full_frontend() { log "=== VOLLSTÄNDIGE FRONTEND-INSTALLATION ===" check_root check_debian_system check_internet_connection update_system install_docker generate_mercedes_ssl_certificate deploy_frontend_application create_caddy_logs create_docker_compose_config create_systemd_service configure_frontend_firewall start_frontend_services test_frontend_application success "✅ Vollständige Frontend-Installation abgeschlossen!" info "Frontend ist verfügbar unter:" info " 🌐 https://$DOMAIN" info " 🌐 https://localhost" info " 🔒 SSL-Zertifikate: $SSL_DIR" info " 📁 Anwendung: $FRONTEND_DIR" info " 📋 Logs: $CADDY_LOG_DIR" info "" info "Service-Befehle:" info " systemctl status $DOCKER_COMPOSE_SERVICE" info " systemctl restart $DOCKER_COMPOSE_SERVICE" info " docker compose logs -f (in $FRONTEND_DIR)" } regenerate_ssl_certificates() { log "=== SSL-ZERTIFIKATE NEU GENERIEREN ===" check_root # Stoppe Services progress "Stoppe Frontend-Services..." systemctl stop "$DOCKER_COMPOSE_SERVICE" 2>/dev/null || true # Neue Zertifikate generieren generate_mercedes_ssl_certificate # Services neu starten progress "Starte Frontend-Services neu..." systemctl start "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Neustarten der Services" # Test test_frontend_application success "✅ SSL-Zertifikate erfolgreich erneuert!" } check_service_status() { log "=== SERVICE-STATUS PRÜFUNG ===" info "Systemd Service Status:" systemctl status "$DOCKER_COMPOSE_SERVICE" --no-pager || true echo "" info "Docker Container Status:" if [ -d "$FRONTEND_DIR" ]; then cd "$FRONTEND_DIR" docker compose ps 2>/dev/null || true echo "" info "Container-Logs (letzte 20 Zeilen):" docker compose logs --tail=20 2>/dev/null || true cd "$CURRENT_DIR" else warning "Frontend-Verzeichnis nicht gefunden: $FRONTEND_DIR" fi echo "" info "Netzwerk-Tests:" curl -k -s -I "https://$DOMAIN/health" 2>/dev/null | head -1 || echo "❌ $DOMAIN nicht erreichbar" curl -k -s -I "https://localhost/health" 2>/dev/null | head -1 || echo "❌ localhost nicht erreichbar" echo "" info "SSL-Zertifikat Info:" if [ -f "$SSL_DIR/frontend.crt" ]; then openssl x509 -in "$SSL_DIR/frontend.crt" -noout -dates 2>/dev/null || echo "❌ Zertifikat-Lesefehler" else echo "❌ Zertifikat nicht gefunden: $SSL_DIR/frontend.crt" fi } # =========================== HAUPTPROGRAMM =========================== main() { # Erstelle Log-Datei mkdir -p "$(dirname "$INSTALL_LOG")" touch "$INSTALL_LOG" # Zeige Menü while true; do show_menu read -r choice case $choice in 1) install_full_frontend break ;; 2) regenerate_ssl_certificates break ;; 3) check_service_status echo "" echo -e "${YELLOW}Drücken Sie Enter um fortzufahren...${NC}" read -r ;; 4) log "Setup beendet" exit 0 ;; *) echo -e "${RED}Ungültige Auswahl. Bitte wählen Sie 1-4.${NC}" echo "" echo -e "${YELLOW}Drücken Sie Enter um fortzufahren...${NC}" read -r ;; esac done } # =========================== SCRIPT AUSFÜHRUNG =========================== main "$@"