695 lines
20 KiB
Bash

#!/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 2>/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 "$@"