diff --git a/setup_server.sh b/setup_server.sh index dd97507..7793193 100644 --- a/setup_server.sh +++ b/setup_server.sh @@ -5,10 +5,10 @@ # ============================================================ set -e -echo "==> [1/6] System-Pakete aktualisieren" +echo "==> [1/7] System-Pakete aktualisieren" apt-get update && apt-get upgrade -y -echo "==> [2/6] Abhängigkeiten installieren" +echo "==> [2/7] Abhängigkeiten installieren" apt-get install -y \ python3 python3-venv python3-dev python3-pip \ postgresql postgresql-contrib \ @@ -16,7 +16,7 @@ apt-get install -y \ nginx \ git curl build-essential libpq-dev -echo "==> [3/6] PostgreSQL einrichten" +echo "==> [3/7] PostgreSQL einrichten" systemctl enable postgresql && systemctl start postgresql # Datenbank + User anlegen (Produktiv-DB + Test-DB) @@ -36,7 +36,7 @@ GRANT ALL PRIVILEGES ON DATABASE timemaster_db TO timemaster; GRANT ALL PRIVILEGES ON DATABASE timemaster_test TO timemaster; SQL -echo "==> [4/6] Redis einrichten" +echo "==> [4/7] Redis einrichten" systemctl enable redis-server && systemctl start redis-server echo "==> [5/7] Python venv + Abhängigkeiten" @@ -50,35 +50,103 @@ echo "==> [6/7] Alembic Migrations ausführen" alembic upgrade head echo "==> [7/7] nginx für Frontend konfigurieren" -mkdir -p /opt/timemaster/frontend/dist +mkdir -p /opt/timemaster/frontend/dist /opt/timemaster/backend/static cat > /etc/nginx/sites-available/timemaster << 'NGINX' +# HTTP-only Konfiguration (SSL/HTTPS noch nicht eingerichtet) +# Sobald ein TLS-Zertifikat vorhanden ist: +# 1. Listen-Block auf 443 ssl http2 erweitern +# 2. ssl_certificate / ssl_certificate_key einkommentieren +# 3. HSTS-Header hinzufügen +# 4. HTTP->HTTPS-Redirect aktivieren + server { listen 80; server_name _; - root /opt/timemaster/frontend/dist; - index index.html; + client_max_body_size 20M; - # SPA fallback - location / { - try_files $uri $uri/ /index.html; - } - - # API proxy + # API Backend location /api/ { - proxy_pass http://127.0.0.1:8000; - 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_pass http://127.0.0.1:8000; + proxy_http_version 1.1; + 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_read_timeout 60s; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; } - # FastAPI docs (dev) + # FastAPI Docs (nur in dev aktiv – docs_url ist None in Production) location /docs { proxy_pass http://127.0.0.1:8000/docs; - proxy_set_header Host $host; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; } + + location /redoc { + proxy_pass http://127.0.0.1:8000/redoc; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; + } + location /openapi.json { proxy_pass http://127.0.0.1:8000/openapi.json; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; + } + + # React Frontend (statische Dateien) + # HINWEIS: nginx-Regel: add_header in einem location-Block ueberschreibt + # alle add_header-Direktiven des parent server-Blocks. Daher Security-Header + # in jede location wiederholen. + location / { + root /opt/timemaster/frontend/dist; + index index.html; + try_files $uri $uri/ /index.html; + expires 1d; + + add_header Cache-Control "public, must-revalidate"; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; + } + + # Swagger/ReDoc-Assets (app/static/swagger-ui im Repo) + Uploads + location /static/ { + alias /opt/timemaster/backend/static/; + expires 7d; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; } } NGINX @@ -93,7 +161,7 @@ echo "" echo "✓ Setup abgeschlossen!" echo " Backend: sudo systemctl start timemaster" echo " Frontend: http://$(hostname -I | awk '{print $1}')/" -echo " API-Docs: http://$(hostname -I | awk '{print $1}')/docs" +echo " API-Docs: http://$(hostname -I | awk '{print $1}')/docs (und /redoc)" echo "" echo "" echo "Frontend deployen (nach lokalem Build):"