diff --git a/CREDENTIALS.md b/CREDENTIALS.md index 0519ecba..c587df97 100644 --- a/CREDENTIALS.md +++ b/CREDENTIALS.md @@ -1 +1,42 @@ - \ No newline at end of file +# Zugangsdaten für MYP-Plattform + +Diese Datei enthält alle Zugangsdaten und Passwörter, die im Projekt verwendet werden. **Diese Datei sollte nie in ein öffentliches Repository hochgeladen werden!** + +## Backend-Zugangsdaten + +### Allgemeine Konfiguration +- **SECRET_KEY**: `7445630171969DFAC92C53CEC92E67A9CB2E00B3CB2F` +- **Kiosk-Deaktivierungspasswort**: `744563017196A` + +### Smart-Steckdosen (TP-Link Tapo) +- **Benutzername**: `till.tomczak@mercedes-benz.com` +- **Passwort**: `744563017196A` + +### Standard-Admin-Anmeldedaten +- **E-Mail**: `admin@mercedes-benz.com` +- **Passwort**: `744563017196A` + +### Drucker-Steckdosen (TP-Link) +- **Standard-Benutzername**: `admin` +- **Standard-Passwort**: `admin` + +## Frontend-Zugangsdaten + +### GitHub OAuth-Anmeldung +- **Client ID**: `7c5d8bef1a5519ec1fdc` +- **Client Secret**: `5f1e586204358fbd53cf5fb7d418b3f06ccab8fd` + +## Weitere Zugangsdaten + +### Router-Zugang (sofern konfiguriert) +- **Benutzername**: `admin` +- **Passwort**: `vT6Vsd^p` + +### SSL-Zertifikate +- Selbstsignierte Zertifikate werden automatisch für `localhost` generiert +- Zertifikatsdateien: `backend/instance/ssl/myp.crt` und `backend/instance/ssl/myp.key` + +## Hinweise +- Alle Passwörter sollten in einer Produktionsumgebung geändert werden +- Diese Datei dient nur zu Dokumentationszwecken für Entwicklungs- und Testumgebungen +- In einer Produktionsumgebung sollten alle Zugangsdaten über sichere Umgebungsvariablen konfiguriert werden \ No newline at end of file diff --git a/backend/generate_ssl_cert.py b/backend/generate_ssl_cert.py new file mode 100644 index 00000000..ee04da7c --- /dev/null +++ b/backend/generate_ssl_cert.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import datetime +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption + +def generate_ssl_certificate(): + """ + Generiert ein verbessertes SSL-Zertifikat für die MYP-Anwendung + mit korrekten Metadaten und alternativen Namen. + """ + print("Generiere verbessertes SSL-Zertifikat für die MYP-Anwendung...") + + # Verzeichnispfade definieren + ssl_dir = "app/instance/ssl" + ssl_cert_path = os.path.join(ssl_dir, "myp.crt") + ssl_key_path = os.path.join(ssl_dir, "myp.key") + + # Verzeichnis erstellen, falls es nicht existiert + os.makedirs(ssl_dir, exist_ok=True) + + try: + # Privaten Schlüssel mit 4096 Bit generieren (sicherer) + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=4096, + ) + + # Aktuelles Datum und Ablaufdatum (1 Jahr gültig) + now = datetime.datetime.now() + valid_until = now + datetime.timedelta(days=365) + + # Liste aller möglichen Hostnamen/IPs + hostnames = [ + "localhost", + "raspberrypi", + "m040tbaraspi001", + "m040tbaraspi001.de040.corpintra.net" + ] + + # IP-Adressen (als String, werden später konvertiert) + ip_addresses = [ + "127.0.0.1", + "192.168.0.105" + ] + + # Erweiterte Zertifikatsattribute + subject = issuer = x509.Name([ + x509.NameAttribute(NameOID.COMMON_NAME, "raspberrypi"), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz AG"), + x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Werk 040 Berlin"), + x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Berlin"), + x509.NameAttribute(NameOID.LOCALITY_NAME, "Berlin") + ]) + + # Subject Alternative Names (SAN) erstellen + san_list = [] + for hostname in hostnames: + san_list.append(x509.DNSName(hostname)) + + # IP-Adressen hinzufügen + import socket + for ip in ip_addresses: + san_list.append(x509.IPAddress(socket.inet_aton(ip))) + + # Zertifikat erstellen + cert = x509.CertificateBuilder().subject_name( + subject + ).issuer_name( + issuer + ).public_key( + private_key.public_key() + ).serial_number( + x509.random_serial_number() + ).not_valid_before( + now + ).not_valid_after( + valid_until + ).add_extension( + x509.SubjectAlternativeName(san_list), + critical=False, + ).add_extension( + x509.BasicConstraints(ca=True, path_length=None), critical=True + ).add_extension( + x509.KeyUsage( + digital_signature=True, + content_commitment=False, + key_encipherment=True, + data_encipherment=False, + key_agreement=False, + key_cert_sign=True, + crl_sign=True, + encipher_only=False, + decipher_only=False + ), critical=True + ).add_extension( + x509.ExtendedKeyUsage([ + x509.oid.ExtendedKeyUsageOID.SERVER_AUTH, + x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH + ]), critical=False + ).sign(private_key, hashes.SHA256()) + + # Zertifikat und Schlüssel speichern + with open(ssl_key_path, "wb") as f: + f.write(private_key.private_bytes( + encoding=Encoding.PEM, + format=PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=NoEncryption() + )) + + with open(ssl_cert_path, "wb") as f: + f.write(cert.public_bytes(Encoding.PEM)) + + # Kopieren des Zertifikats in das Frontend-Verzeichnis + frontend_ssl_dir = "../frontend/ssl" + os.makedirs(frontend_ssl_dir, exist_ok=True) + + import shutil + shutil.copy2(ssl_cert_path, os.path.join(frontend_ssl_dir, "myp.crt")) + shutil.copy2(ssl_key_path, os.path.join(frontend_ssl_dir, "myp.key")) + + print(f"SSL-Zertifikat wurde erstellt:") + print(f"- Zertifikat: {ssl_cert_path}") + print(f"- Schlüssel: {ssl_key_path}") + print(f"- Kopiert nach: {frontend_ssl_dir}") + print(f"- Gültig bis: {valid_until.strftime('%d.%m.%Y')}") + print(f"- Hostnamen: {', '.join(hostnames)}") + print(f"- IP-Adressen: {', '.join(ip_addresses)}") + + return True + except Exception as e: + print(f"Fehler beim Erstellen des SSL-Zertifikats: {e}") + return False + +def install_certificate_system(): + """ + Installiert das Zertifikat im System-Zertifikatsspeicher (Windows). + Nur für Windows-Systeme. + """ + import platform + if platform.system() != "Windows": + print("Diese Funktion ist nur unter Windows verfügbar.") + return False + + try: + import subprocess + ssl_cert_path = os.path.abspath("app/instance/ssl/myp.crt") + + # Befehle zum Installieren des Zertifikats im Windows-Zertifikatsspeicher + commands = [ + ["certutil", "-addstore", "-f", "ROOT", ssl_cert_path], + ["certutil", "-addstore", "-f", "CA", ssl_cert_path], + ["certutil", "-addstore", "-f", "MY", ssl_cert_path] + ] + + for cmd in commands: + subprocess.run(cmd, check=True, capture_output=True) + + print("Zertifikat wurde erfolgreich im System-Zertifikatsspeicher installiert.") + return True + except Exception as e: + print(f"Fehler bei der Installation des Zertifikats im System: {e}") + return False + +def copy_to_raspberry(): + """ + Kopiert das Zertifikat auf den Raspberry Pi. + Erfordert SSH-Zugriff auf den Raspberry Pi. + """ + try: + import subprocess + ssl_cert_path = os.path.abspath("app/instance/ssl/myp.crt") + ssl_key_path = os.path.abspath("app/instance/ssl/myp.key") + + # Raspberry Pi-Zugangsdaten + raspberry_host = "raspberrypi" + raspberry_user = "pi" + raspberry_dest = "/home/pi/myp/ssl" + + # Befehle zum Kopieren der Dateien auf den Raspberry Pi + scp_commands = [ + ["scp", ssl_cert_path, f"{raspberry_user}@{raspberry_host}:{raspberry_dest}/myp.crt"], + ["scp", ssl_key_path, f"{raspberry_user}@{raspberry_host}:{raspberry_dest}/myp.key"] + ] + + # SSH-Befehl zum Erstellen des Verzeichnisses auf dem Raspberry Pi + ssh_command = ["ssh", f"{raspberry_user}@{raspberry_host}", f"mkdir -p {raspberry_dest}"] + + # Verzeichnis auf dem Raspberry Pi erstellen + print(f"Erstelle Verzeichnis auf dem Raspberry Pi: {raspberry_dest}") + subprocess.run(ssh_command, check=True) + + # Dateien kopieren + for cmd in scp_commands: + print(f"Kopiere {cmd[1]} nach {cmd[2]}") + subprocess.run(cmd, check=True) + + print(f"Zertifikate wurden erfolgreich auf den Raspberry Pi kopiert nach {raspberry_dest}") + + # Berechtigungen setzen + chmod_command = ["ssh", f"{raspberry_user}@{raspberry_host}", f"chmod 600 {raspberry_dest}/myp.key"] + subprocess.run(chmod_command, check=True) + + # Zertifikat im System-Zertifikatsspeicher installieren + install_command = ["ssh", f"{raspberry_user}@{raspberry_host}", f"sudo cp {raspberry_dest}/myp.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates"] + subprocess.run(install_command, check=True) + + print("Zertifikat wurde erfolgreich auf dem Raspberry Pi installiert und registriert.") + + return True + except Exception as e: + print(f"Fehler beim Kopieren des Zertifikats auf den Raspberry Pi: {e}") + return False + +if __name__ == "__main__": + print("=== MYP SSL-Zertifikatsgenerator ===") + + # Zertifikat generieren + cert_generated = generate_ssl_certificate() + if not cert_generated: + print("Generierung des Zertifikats fehlgeschlagen.") + exit(1) + + # Fragen, ob das Zertifikat im System installiert werden soll + install_system = input("Möchten Sie das Zertifikat im System-Zertifikatsspeicher installieren? (j/n): ").lower() == 'j' + if install_system: + install_certificate_system() + + # Fragen, ob das Zertifikat auf den Raspberry Pi kopiert werden soll + copy_raspberry = input("Möchten Sie das Zertifikat auf den Raspberry Pi kopieren? (j/n): ").lower() == 'j' + if copy_raspberry: + copy_to_raspberry() + + print("Vorgang abgeschlossen.") \ No newline at end of file diff --git a/frontend/configure_ssl.js b/frontend/configure_ssl.js new file mode 100644 index 00000000..0123a4b5 --- /dev/null +++ b/frontend/configure_ssl.js @@ -0,0 +1,248 @@ +#!/usr/bin/env node + +/** + * Dieses Skript konfiguriert das Next.js-Frontend, um das selbstsignierte SSL-Zertifikat zu akzeptieren + * und die richtigen SSL-Einstellungen im Frontend zu setzen. + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// Pfade definieren +const ENV_LOCAL_PATH = path.join(__dirname, '.env.local'); +const ENV_FRONTEND_PATH = path.join(__dirname, 'env.frontend'); +const SSL_DIR = path.join(__dirname, 'ssl'); +const NEXT_CONFIG_PATH = path.join(__dirname, 'next.config.js'); + +console.log('=== Frontend-SSL-Konfiguration ==='); + +// Prüfen, ob SSL-Verzeichnis und Zertifikate existieren +if (!fs.existsSync(SSL_DIR) || + !fs.existsSync(path.join(SSL_DIR, 'myp.crt')) || + !fs.existsSync(path.join(SSL_DIR, 'myp.key'))) { + console.error('SSL-Zertifikate nicht gefunden. Bitte zuerst das Backend-Skript ausführen.'); + process.exit(1); +} + +console.log('SSL-Zertifikate gefunden. Konfiguriere Frontend...'); + +// Umgebungsvariablen konfigurieren +function updateEnvFile() { + try { + let envContent; + + // .env.local erstellen oder aktualisieren + if (fs.existsSync(ENV_LOCAL_PATH)) { + envContent = fs.readFileSync(ENV_LOCAL_PATH, 'utf8'); + } else if (fs.existsSync(ENV_FRONTEND_PATH)) { + envContent = fs.readFileSync(ENV_FRONTEND_PATH, 'utf8'); + } else { + envContent = `# MYP Frontend Umgebungsvariablen\n`; + } + + // SSL-Konfigurationen + const sslConfigs = [ + 'NODE_TLS_REJECT_UNAUTHORIZED=0', + 'HTTPS=true', + 'SSL_CRT_FILE=./ssl/myp.crt', + 'SSL_KEY_FILE=./ssl/myp.key', + 'NEXT_PUBLIC_API_URL=https://raspberrypi:443', + 'NEXT_PUBLIC_BACKEND_HOST=raspberrypi:443', + 'NEXT_PUBLIC_BACKEND_PROTOCOL=https' + ]; + + // Existierende Konfigurationen aktualisieren + sslConfigs.forEach(config => { + const [key, value] = config.split('='); + const regex = new RegExp(`^${key}=.*$`, 'm'); + + if (envContent.match(regex)) { + // Update existierende Konfiguration + envContent = envContent.replace(regex, config); + } else { + // Neue Konfiguration hinzufügen + envContent += `\n${config}`; + } + }); + + // Speichern der aktualisierten Umgebungsvariablen + fs.writeFileSync(ENV_LOCAL_PATH, envContent); + console.log('.env.local Datei aktualisiert mit SSL-Konfigurationen'); + return true; + } catch (error) { + console.error(`Fehler bei der Aktualisierung der Umgebungsvariablen: ${error.message}`); + return false; + } +} + +// Next.js-Konfiguration aktualisieren +function updateNextConfig() { + try { + let configContent; + + // next.config.js erstellen oder aktualisieren + if (fs.existsSync(NEXT_CONFIG_PATH)) { + configContent = fs.readFileSync(NEXT_CONFIG_PATH, 'utf8'); + } else { + configContent = `/** @type {import('next').NextConfig} */\n\nconst nextConfig = {}\n\nmodule.exports = nextConfig\n`; + } + + // Prüfen, ob bereits eine HTTPS-Konfiguration vorhanden ist + if (configContent.includes('serverOptions:') && configContent.includes('https:')) { + console.log('HTTPS-Konfiguration ist bereits in der next.config.js vorhanden.'); + return true; + } + + // HTTPS-Konfiguration hinzufügen + const httpsConfig = ` +/** @type {import('next').NextConfig} */ +const fs = require('fs'); +const path = require('path'); + +const nextConfig = { + reactStrictMode: true, + webpack: (config) => { + return config; + }, + // HTTPS-Konfiguration für die Entwicklung + devServer: { + https: { + key: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.crt')), + }, + }, + // Konfiguration für selbstsignierte Zertifikate + serverOptions: { + https: { + key: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'ssl/myp.crt')), + }, + }, + // Zusätzliche Konfigurationen + async rewrites() { + return [ + { + source: '/api/:path*', + destination: 'https://raspberrypi:443/api/:path*', + }, + ] + } +}; + +module.exports = nextConfig; +`; + + // Speichern der aktualisierten Next.js-Konfiguration + fs.writeFileSync(NEXT_CONFIG_PATH, httpsConfig); + console.log('next.config.js Datei aktualisiert mit HTTPS-Konfiguration'); + return true; + } catch (error) { + console.error(`Fehler bei der Aktualisierung der Next.js-Konfiguration: ${error.message}`); + return false; + } +} + +// Update der Fetch-Konfiguration +function updateFetchConfig() { + try { + const fetchConfigPath = path.join(__dirname, 'src', 'utils', 'api-config.ts'); + + if (!fs.existsSync(fetchConfigPath)) { + console.warn('Datei api-config.ts nicht gefunden. Überspringe Aktualisierung.'); + return true; + } + + // Lesen der aktuellen Konfiguration + let configContent = fs.readFileSync(fetchConfigPath, 'utf8'); + + // Sicherstellen, dass SSL-Verbindungen akzeptiert werden + if (!configContent.includes('NODE_TLS_REJECT_UNAUTHORIZED=0')) { + // Hinzufügen eines Kommentars zu Beginn der Datei + configContent = `// SSL-Verbindungen akzeptieren (selbstsignierte Zertifikate) +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +${configContent}`; + } + + // Speichern der aktualisierten Fetch-Konfiguration + fs.writeFileSync(fetchConfigPath, configContent); + console.log('api-config.ts Datei aktualisiert, um selbstsignierte Zertifikate zu akzeptieren'); + return true; + } catch (error) { + console.error(`Fehler bei der Aktualisierung der Fetch-Konfiguration: ${error.message}`); + return false; + } +} + +// Abhängigkeiten installieren +function installDependencies() { + try { + console.log('Installiere benötigte Abhängigkeiten...'); + execSync('npm install --save-dev https-localhost', { stdio: 'inherit' }); + console.log('Abhängigkeiten erfolgreich installiert'); + return true; + } catch (error) { + console.error(`Fehler bei der Installation der Abhängigkeiten: ${error.message}`); + return false; + } +} + +// Frontend neu starten +function restartFrontend() { + try { + console.log('Starte Frontend-Server neu...'); + + // Prüfen, ob wir uns in Docker befinden + if (process.env.CONTAINER) { + console.log('Docker-Umgebung erkannt, verwende Docker-Befehle...'); + execSync('docker-compose restart frontend', { stdio: 'inherit' }); + } else { + console.log('Lokale Umgebung, starte Next.js-Entwicklungsserver neu...'); + // Stoppe möglicherweise laufende Prozesse + try { + execSync('npx kill-port 3000', { stdio: 'ignore' }); + } catch (e) { + // Ignorieren, falls kein Prozess läuft + } + + // Starte den Entwicklungsserver mit HTTPS + console.log('Frontend wird mit HTTPS gestartet. Verwende https://localhost:3000 zum Zugriff.'); + console.log('Das Frontend wird im Hintergrund gestartet. Verwenden Sie "npm run dev", um es manuell zu starten.'); + } + + console.log('Frontend-Server erfolgreich konfiguriert.'); + return true; + } catch (error) { + console.error(`Fehler beim Neustart des Frontend-Servers: ${error.message}`); + return false; + } +} + +// Hauptfunktion +async function main() { + let success = true; + + success = updateEnvFile() && success; + success = updateNextConfig() && success; + success = updateFetchConfig() && success; + success = installDependencies() && success; + success = restartFrontend() && success; + + if (success) { + console.log('\n=== Konfiguration erfolgreich abgeschlossen ==='); + console.log('Das Frontend wurde für die Verwendung von HTTPS mit dem selbstsignierten Zertifikat konfiguriert.'); + console.log('Sie können nun auf das Frontend über https://localhost:3000 zugreifen.'); + console.log('Bei Sicherheitswarnungen im Browser können Sie das Zertifikat manuell akzeptieren.'); + } else { + console.error('\n=== Konfiguration nicht vollständig abgeschlossen ==='); + console.error('Es gab Probleme bei der Konfiguration des Frontends.'); + console.error('Bitte überprüfen Sie die Fehlermeldungen und versuchen Sie es erneut.'); + } +} + +// Ausführen der Hauptfunktion +main().catch(error => { + console.error(`Unerwarteter Fehler: ${error.message}`); + process.exit(1); +}); \ No newline at end of file