diff --git a/nginx/nginx-http-only.conf b/nginx/nginx-http-only.conf new file mode 100644 index 0000000..905a144 --- /dev/null +++ b/nginx/nginx-http-only.conf @@ -0,0 +1,139 @@ +# Nginx HTTP-only Konfiguration für ersten Start (vor SSL-Zertifikatserstellung) +# Optimiert für Produktionsumgebung + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + + # Performance-Optimierungen + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 10M; + + # Gzip-Kompression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Sicherheits-Header (ohne HSTS für HTTP) + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self';" always; + + # Rate Limiting + limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; + + # Upstream für die Anwendung + upstream stargirlnails { + server stargirlnails:3000; + } + + # HTTP-Server (ohne HTTPS-Redirect) + server { + listen 80; + server_name _; + + # Let's Encrypt Challenge + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # Proxy-Einstellungen + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + # Timeouts + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + + # API-Endpunkte mit Rate Limiting + location /rpc { + limit_req zone=api burst=20 nodelay; + proxy_pass http://stargirlnails; + } + + # Login-Endpunkte mit strengerem Rate Limiting + location ~ ^/(login|auth) { + limit_req zone=login burst=5 nodelay; + proxy_pass http://stargirlnails; + } + + # Statische Dateien (Assets) + location /static/ { + proxy_pass http://stargirlnails; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Assets-Verzeichnis + location /assets/ { + proxy_pass http://stargirlnails; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Favicon + location /favicon.ico { + proxy_pass http://stargirlnails; + expires 1y; + add_header Cache-Control "public"; + } + + # Health Check + location /health { + proxy_pass http://stargirlnails; + access_log off; + } + + # Security.txt + location /.well-known/security.txt { + proxy_pass http://stargirlnails; + expires 1d; + } + + # Alle anderen Anfragen + location / { + proxy_pass http://stargirlnails; + } + } +} diff --git a/scripts/setup-ssl-improved.sh b/scripts/setup-ssl-improved.sh new file mode 100644 index 0000000..682cc4c --- /dev/null +++ b/scripts/setup-ssl-improved.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# Farben für Output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}🔧 Stargirlnails Kiel - SSL-Setup (Verbessert)${NC}" +echo "==================================================" + +# Prüfe ob .env-Datei existiert +if [ ! -f .env ]; then + echo -e "${RED}❌ .env-Datei nicht gefunden!${NC}" + echo "Bitte erstelle eine .env-Datei mit DOMAIN und ADMIN_EMAIL" + exit 1 +fi + +# Extrahiere DOMAIN und ADMIN_EMAIL robust aus .env +DOMAIN=$(grep -E '^DOMAIN=' .env | cut -d '=' -f2- | tr -d '"') +ADMIN_EMAIL=$(grep -E '^ADMIN_EMAIL=' .env | cut -d '=' -f2- | tr -d '"') + +# Prüfe ob Variablen gesetzt sind +if [ -z "$DOMAIN" ]; then + echo -e "${RED}❌ DOMAIN nicht in .env gefunden!${NC}" + exit 1 +fi + +if [ -z "$ADMIN_EMAIL" ]; then + echo -e "${RED}❌ ADMIN_EMAIL nicht in .env gefunden!${NC}" + exit 1 +fi + +echo -e "${GREEN}✅ Domain: $DOMAIN${NC}" +echo -e "${GREEN}✅ Admin E-Mail: $ADMIN_EMAIL${NC}" + +# Erkenne Docker Compose-Version (docker-compose vs docker compose) +if command -v docker-compose >/dev/null 2>&1; then + DOCKER_COMPOSE="docker-compose" +elif docker compose version >/dev/null 2>&1; then + DOCKER_COMPOSE="docker compose" +else + echo -e "${RED}❌ Docker Compose nicht gefunden!${NC}" + echo "Bitte installiere Docker Compose" + exit 1 +fi + +# Prüfe, ob Docker Root-Rechte benötigt +SUDO="" +if ! docker info >/dev/null 2>&1; then + if command -v sudo >/dev/null 2>&1; then + SUDO="sudo " + echo -e "${YELLOW}⚠️ Docker benötigt Root-Rechte. Verwende 'sudo'.${NC}" + else + echo -e "${RED}❌ Docker läuft nicht und 'sudo' ist nicht verfügbar.${NC}" + echo "Bitte stelle sicher, dass Docker korrekt installiert ist und der Benutzer zur Docker-Gruppe gehört oder 'sudo' verfügbar ist." + exit 1 + fi +fi + +echo -e "${GREEN}✅ Verwende: ${SUDO}${DOCKER_COMPOSE}${NC}" + +# Erstelle Docker Volumes +echo -e "${YELLOW}📦 Erstelle Docker Volumes...${NC}" +${SUDO}docker volume create certbot-certs 2>/dev/null || true +${SUDO}docker volume create certbot-webroot 2>/dev/null || true +${SUDO}docker volume create storage-data 2>/dev/null || true + +# Verwende HTTP-only Nginx-Konfiguration für ersten Start +echo -e "${YELLOW}📝 Verwende HTTP-only Nginx-Konfiguration...${NC}" +cp nginx/nginx-http-only.conf nginx/nginx.conf + +# Starte nur die Anwendung (ohne Nginx) +echo -e "${YELLOW}🚀 Starte Anwendung...${NC}" +${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml up -d stargirlnails + +# Warte bis die Anwendung läuft +echo -e "${YELLOW}⏳ Warte auf Anwendung...${NC}" +sleep 10 + +# Prüfe ob die Anwendung läuft +if ! ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml ps | grep -q "stargirlnails.*Up"; then + echo -e "${RED}❌ Anwendung ist nicht gestartet!${NC}" + echo "Prüfe die Logs: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml logs stargirlnails" + exit 1 +fi + +echo -e "${GREEN}✅ Anwendung läuft!${NC}" + +# Starte temporären HTTP-Server für Domain-Validierung +echo -e "${YELLOW}🌐 Starte temporären HTTP-Server...${NC}" +${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml up -d nginx + +# Warte bis Nginx läuft +echo -e "${YELLOW}⏳ Warte auf Nginx...${NC}" +sleep 10 + +# Erstelle SSL-Zertifikat +echo -e "${YELLOW}🔐 Erstelle SSL-Zertifikat für $DOMAIN...${NC}" +${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml run --rm certbot certbot certonly \ + --webroot \ + --webroot-path=/var/www/certbot \ + --email $ADMIN_EMAIL \ + --agree-tos \ + --no-eff-email \ + --force-renewal \ + -d $DOMAIN + +if [ $? -eq 0 ]; then + echo -e "${GREEN}✅ SSL-Zertifikat erfolgreich erstellt!${NC}" +else + echo -e "${RED}❌ SSL-Zertifikat-Erstellung fehlgeschlagen!${NC}" + echo "Mögliche Ursachen:" + echo "- Domain ist nicht erreichbar" + echo "- Port 80 ist blockiert" + echo "- DNS-Einträge sind nicht korrekt" + exit 1 +fi + +# Erstelle HTTPS Nginx-Konfiguration +echo -e "${YELLOW}📝 Erstelle HTTPS Nginx-Konfiguration...${NC}" +sed "s/\${DOMAIN}/$DOMAIN/g" nginx/nginx.conf > nginx/nginx.conf.tmp +mv nginx/nginx.conf.tmp nginx/nginx.conf + +# Starte alle Services +echo -e "${YELLOW}🚀 Starte alle Services...${NC}" +${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml up -d + +# Prüfe Status +echo -e "${YELLOW}🔍 Prüfe Service-Status...${NC}" +sleep 5 + +if ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml ps | grep -q "Up"; then + echo -e "${GREEN}✅ Alle Services laufen!${NC}" + echo "" + echo -e "${BLUE}🌐 Deine Anwendung ist jetzt verfügbar unter:${NC}" + echo -e "${GREEN} https://$DOMAIN${NC}" + echo "" + echo -e "${BLUE}📋 Nützliche Befehle:${NC}" + echo " Status anzeigen: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml ps" + echo " Logs anzeigen: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml logs -f" + echo " Services stoppen: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml down" + echo " Zertifikat erneuern: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml run --rm certbot certbot renew" + echo "" + echo -e "${YELLOW}⚠️ Wichtig:${NC}" + echo " - SSL-Zertifikate werden automatisch alle 12 Stunden erneuert" + echo " - Überwache die Logs regelmäßig" + echo " - Stelle sicher, dass Port 80 und 443 erreichbar sind" +else + echo -e "${RED}❌ Einige Services sind nicht gestartet!${NC}" + echo "Prüfe die Logs: ${SUDO}${DOCKER_COMPOSE} -f docker-compose-prod.yml logs" + exit 1 +fi