feat: Ergänze umfassende Anleitung zur Bereitstellung der MYP-Plattform auf Raspberry Pi und aktualisiere SSL-Manager für verbesserte Zertifikatsverwaltung

This commit is contained in:
Till Tomczak 2025-05-26 12:33:29 +02:00
parent 8d84b9157e
commit 8dec03fac7
4 changed files with 665 additions and 311 deletions

View File

@ -1 +1,351 @@
# MYP Platform - Raspberry Pi Deployment Guide
## Übersicht
Diese Anleitung beschreibt die vollständige Installation und Konfiguration der MYP Platform auf Raspberry Pi Systemen.
## Voraussetzungen
### Hardware
- Raspberry Pi 4 (empfohlen) oder Raspberry Pi 3B+
- Mindestens 4GB RAM
- 32GB+ SD-Karte (Class 10)
- Netzwerkverbindung (Ethernet oder WiFi)
### Software
- Raspberry Pi OS (Bullseye oder neuer)
- SSH-Zugang aktiviert
- Benutzer `user` erstellt
## Installation
### 1. Projekt auf Raspberry Pi kopieren
```bash
# Auf dem Entwicklungsrechner
scp -r Projektarbeit-MYP user@raspberrypi:/home/user/
# Oder mit Git
ssh user@raspberrypi
cd /home/user
git clone <repository-url> Projektarbeit-MYP
```
### 2. Setup-Skript ausführen
```bash
ssh user@raspberrypi
cd /home/user/Projektarbeit-MYP/backend
chmod +x setup_raspberry_pi.sh
./setup_raspberry_pi.sh
```
Das Setup-Skript führt automatisch folgende Schritte aus:
1. **System-Updates**: Aktualisiert alle Pakete
2. **Abhängigkeiten**: Installiert Python, Nginx, Supervisor etc.
3. **Virtual Environment**: Erstellt isolierte Python-Umgebung
4. **Python-Pakete**: Installiert alle Requirements
5. **Verzeichnisse**: Erstellt notwendige Ordnerstruktur
6. **Datenbank**: Initialisiert SQLite-Datenbank
7. **SSL-Zertifikate**: Generiert selbstsignierte Zertifikate
8. **Services**: Konfiguriert Systemd, Nginx, Supervisor
9. **Firewall**: Öffnet notwendige Ports
10. **Drucker**: Trägt hardkodierte Drucker in DB ein
### 3. Manuelle Drucker-Konfiguration (optional)
Falls die Drucker separat konfiguriert werden sollen:
```bash
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python setup_drucker_db.py
```
## Konfiguration
### Pfadstruktur
```
/home/user/Projektarbeit-MYP/
├── backend/
│ ├── app/
│ │ ├── database/
│ │ │ └── myp.db
│ │ ├── logs/
│ │ │ ├── app/
│ │ │ ├── auth/
│ │ │ ├── jobs/
│ │ │ ├── printers/
│ │ │ ├── scheduler/
│ │ │ └── errors/
│ │ └── ...
│ ├── certs/
│ │ ├── myp.crt
│ │ └── myp.key
│ ├── venv/
│ └── requirements.txt
└── frontend/
└── ssl/
├── myp.crt
└── myp.key
```
### Hardkodierte Drucker
Die folgenden Drucker werden automatisch konfiguriert:
| Name | IP-Adresse | Status |
|------|------------|--------|
| Printer 1 | 192.168.0.100 | Available |
| Printer 2 | 192.168.0.101 | Available |
| Printer 3 | 192.168.0.102 | Available |
| Printer 4 | 192.168.0.103 | Available |
| Printer 5 | 192.168.0.104 | Available |
| Printer 6 | 192.168.0.106 | Available |
### Standard-Anmeldedaten
- **E-Mail**: admin@mercedes-benz.com
- **Passwort**: 744563017196A
## Services
### Systemd Service
```bash
# Service-Status prüfen
sudo systemctl status myp-platform
# Service neu starten
sudo systemctl restart myp-platform
# Service aktivieren/deaktivieren
sudo systemctl enable myp-platform
sudo systemctl disable myp-platform
# Logs anzeigen
sudo journalctl -u myp-platform -f
```
### Nginx
```bash
# Nginx-Status prüfen
sudo systemctl status nginx
# Konfiguration testen
sudo nginx -t
# Nginx neu laden
sudo systemctl reload nginx
```
### Supervisor
```bash
# Supervisor-Status
sudo supervisorctl status
# Service neu starten
sudo supervisorctl restart myp-platform
# Logs anzeigen
sudo supervisorctl tail -f myp-platform
```
## Zugriff
### URLs
- **HTTPS**: https://raspberrypi
- **HTTPS (IP)**: https://[IP-ADRESSE]
- **HTTP**: Automatische Weiterleitung zu HTTPS
### SSL-Zertifikat
Das System verwendet selbstsignierte SSL-Zertifikate:
- Browser-Warnung beim ersten Zugriff ist normal
- Zertifikat manuell akzeptieren
- Für Produktionsumgebung: Echte Zertifikate verwenden
## Wartung
### Logs
```bash
# Anwendungs-Logs
tail -f /home/user/Projektarbeit-MYP/backend/app/logs/app/app.log
# System-Logs
sudo journalctl -u myp-platform -f
# Nginx-Logs
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
```
### Datenbank-Backup
```bash
# Backup erstellen
cp /home/user/Projektarbeit-MYP/backend/app/database/myp.db \
/home/user/backup_$(date +%Y%m%d_%H%M%S).db
# Automatisches Backup (Crontab)
crontab -e
# Hinzufügen:
# 0 2 * * * cp /home/user/Projektarbeit-MYP/backend/app/database/myp.db /home/user/backup_$(date +\%Y\%m\%d).db
```
### Updates
```bash
# Code aktualisieren
cd /home/user/Projektarbeit-MYP
git pull
# Python-Abhängigkeiten aktualisieren
source backend/venv/bin/activate
pip install -r backend/requirements.txt
# Service neu starten
sudo systemctl restart myp-platform
```
## Troubleshooting
### Häufige Probleme
#### Service startet nicht
```bash
# Logs prüfen
sudo journalctl -u myp-platform -n 50
# Manuell starten (Debug)
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python app.py
```
#### SSL-Probleme
```bash
# Zertifikate neu generieren
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python -c "from utils.ssl_manager import ssl_manager; ssl_manager.generate_mercedes_certificate()"
# Nginx neu starten
sudo systemctl restart nginx
```
#### Datenbank-Probleme
```bash
# Datenbank neu initialisieren
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python -c "from models import init_database, create_initial_admin; init_database(); create_initial_admin()"
# Drucker neu einrichten
python setup_drucker_db.py
```
#### Port-Konflikte
```bash
# Verwendete Ports prüfen
sudo netstat -tlnp | grep :443
sudo netstat -tlnp | grep :80
# Prozesse beenden
sudo pkill -f "python app.py"
```
### Performance-Optimierung
#### Systemressourcen
```bash
# RAM-Nutzung prüfen
free -h
# CPU-Nutzung prüfen
htop
# Festplatte prüfen
df -h
```
#### Log-Rotation
```bash
# Logrotate konfigurieren
sudo tee /etc/logrotate.d/myp-platform > /dev/null <<EOF
/home/user/Projektarbeit-MYP/backend/app/logs/*/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
copytruncate
}
EOF
```
## Sicherheit
### Firewall
```bash
# UFW-Status prüfen
sudo ufw status
# Zusätzliche Regeln
sudo ufw allow from 192.168.0.0/24 to any port 22
sudo ufw deny 22
```
### SSL-Härtung
Für Produktionsumgebung:
1. Echte SSL-Zertifikate verwenden (Let's Encrypt)
2. HSTS aktivieren
3. Security Headers konfigurieren
4. Regelmäßige Updates
### Backup-Strategie
1. Tägliche Datenbank-Backups
2. Wöchentliche Vollbackups
3. Externe Speicherung
4. Restore-Tests
## Support
### Kontakt
- **E-Mail**: admin@mercedes-benz.com
- **Dokumentation**: /home/user/Projektarbeit-MYP/docs/
### Nützliche Befehle
```bash
# System-Informationen
hostnamectl
cat /etc/os-release
python3 --version
# Netzwerk-Informationen
ip addr show
hostname -I
# Service-Übersicht
systemctl list-units --type=service --state=running
```

View File

@ -0,0 +1,121 @@
# MYP Platform - Raspberry Pi Setup
## Schnellstart
### 1. Projekt kopieren
```bash
scp -r Projektarbeit-MYP user@raspberrypi:/home/user/
```
### 2. Setup ausführen
```bash
ssh user@raspberrypi
cd /home/user/Projektarbeit-MYP/backend
chmod +x setup_raspberry_pi.sh
./setup_raspberry_pi.sh
```
### 3. Zugriff
- **URL**: https://raspberrypi
- **Login**: admin@mercedes-benz.com
- **Passwort**: 744563017196A
## Was wird installiert?
### System-Pakete
- Python 3 + pip + venv
- Nginx (Reverse Proxy)
- Supervisor (Process Manager)
- SQLite3 (Datenbank)
- OpenSSL (SSL-Zertifikate)
- Build-Tools (gcc, make, etc.)
### Python-Abhängigkeiten
- Flask 2.3.3 (Web Framework)
- SQLAlchemy 2.0.21 (ORM)
- cryptography 41.0.4 (SSL)
- PyP100 0.1.4 (Tapo Smart Plugs)
- psutil 5.9.5 (System Monitoring)
- gunicorn 21.2.0 (Production Server)
- RPi.GPIO 0.7.1 (Hardware Interface)
- Weitere 20+ Pakete (siehe requirements.txt)
### Services
- **myp-platform.service**: Hauptanwendung
- **nginx**: Reverse Proxy + SSL
- **supervisor**: Process Management
- **ufw**: Firewall (Ports 22, 80, 443)
### Verzeichnisstruktur
```
/home/user/Projektarbeit-MYP/
├── backend/
│ ├── app/ # Hauptanwendung
│ │ ├── database/myp.db # SQLite Datenbank
│ │ └── logs/ # Log-Dateien
│ ├── certs/ # SSL-Zertifikate
│ ├── venv/ # Python Virtual Environment
│ └── requirements.txt # Python-Abhängigkeiten
└── frontend/ssl/ # Frontend SSL-Zertifikate
```
### Hardkodierte Drucker
- **Printer 1**: 192.168.0.100
- **Printer 2**: 192.168.0.101
- **Printer 3**: 192.168.0.102
- **Printer 4**: 192.168.0.103
- **Printer 5**: 192.168.0.104
- **Printer 6**: 192.168.0.106
## Wartung
### Service-Befehle
```bash
# Status prüfen
sudo systemctl status myp-platform
# Neu starten
sudo systemctl restart myp-platform
# Logs anzeigen
sudo journalctl -u myp-platform -f
```
### Drucker neu einrichten
```bash
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python setup_drucker_db.py
```
### SSL-Zertifikate erneuern
```bash
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python -c "from utils.ssl_manager import ssl_manager; ssl_manager.generate_mercedes_certificate()"
sudo systemctl restart nginx
```
## Troubleshooting
### Service startet nicht
```bash
sudo journalctl -u myp-platform -n 50
```
### Manueller Start (Debug)
```bash
cd /home/user/Projektarbeit-MYP/backend/app
source ../venv/bin/activate
python app.py
```
### Ports prüfen
```bash
sudo netstat -tlnp | grep :443
sudo netstat -tlnp | grep :80
```
## Vollständige Dokumentation
Siehe: `DEPLOYMENT.md` für detaillierte Anweisungen.

Binary file not shown.

View File

@ -1,153 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SSL-Zertifikatsverwaltung für das Mercedes-Benz MYP-System
Konsolidiert die Funktionalität der SSL-Zertifikatsgenerierung
SSL-Manager für die MYP-Plattform
Generiert und verwaltet SSL-Zertifikate für Mercedes-Benz Yard Printing
"""
import os
import datetime
import shutil
import platform
import subprocess
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Any
import socket
from datetime import datetime, timedelta
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
import ipaddress
from config.settings import SSL_CERT_PATH, SSL_KEY_PATH, SSL_HOSTNAME
from utils.logging_config import get_logger
ssl_logger = get_logger("ssl")
class SSLCertificateManager:
"""
Verwaltet SSL-Zertifikate für das MYP-System
"""
class SSLManager:
"""SSL-Zertifikat-Manager für die MYP-Plattform"""
def __init__(self):
self.cert_path = SSL_CERT_PATH
self.key_path = SSL_KEY_PATH
self.hostname = SSL_HOSTNAME
# Verzeichnisse definieren
self.certs_dir = os.path.dirname(self.cert_path)
self.frontend_ssl_dir = "/home/user/Projektarbeit-MYP/frontend/ssl"
# Mercedes-Benz spezifische Konfiguration
self.mercedes_config = {
"organization": "Mercedes-Benz AG",
"organizational_unit": "Werk 040 Berlin",
"locality": "Berlin",
"state": "Berlin",
"country": "DE",
"email": "admin@mercedes-benz.com"
}
# Erweiterte Hostnamen und IP-Adressen
self.hostnames = [
"localhost",
"raspberrypi",
"m040tbaraspi001",
"m040tbaraspi001.de040.corpintra.net",
"mbag.corpintra.net",
"mbag.mb.corpintra.net"
]
self.ip_addresses = [
"127.0.0.1",
"192.168.0.101",
"192.168.0.102",
"192.168.0.103",
"192.168.0.104",
"192.168.0.105",
"192.168.0.106"
]
def ensure_directories(self) -> None:
"""Erstellt notwendige Verzeichnisse"""
os.makedirs(self.certs_dir, exist_ok=True)
os.makedirs(self.frontend_ssl_dir, exist_ok=True)
ssl_logger.info(f"SSL-Verzeichnisse erstellt: {self.certs_dir}, {self.frontend_ssl_dir}")
def cleanup_old_certificates(self) -> None:
"""Entfernt alte Zertifikate und veraltete Verzeichnisse"""
# Alte SSL-Verzeichnisse löschen
old_ssl_dirs = [
os.path.join(os.path.dirname(os.path.dirname(self.certs_dir)), "app", "instance", "ssl"),
os.path.join(os.path.dirname(os.path.dirname(self.certs_dir)), "app", "certs")
]
for old_dir in old_ssl_dirs:
if os.path.exists(old_dir):
ssl_logger.info(f"Lösche alten SSL-Ordner: {old_dir}")
try:
shutil.rmtree(old_dir)
except Exception as e:
ssl_logger.warning(f"Konnte alten SSL-Ordner nicht löschen: {e}")
# Alte Zertifikate im aktuellen Verzeichnis entfernen
for path in [self.cert_path, self.key_path]:
if os.path.exists(path):
os.remove(path)
ssl_logger.info(f"Alte Zertifikatsdatei entfernt: {path}")
def generate_mercedes_certificate(self, key_size: int = 4096, validity_days: int = 365) -> bool:
def __init__(self, cert_path: str = None, key_path: str = None):
"""
Generiert ein vollständiges Mercedes-Benz SSL-Zertifikat
Initialisiert den SSL-Manager
Args:
key_size: Schlüsselgröße in Bits (Standard: 4096)
validity_days: Gültigkeitsdauer in Tagen (Standard: 365)
cert_path: Pfad zum SSL-Zertifikat
key_path: Pfad zum SSL-Schlüssel
"""
from config.settings import SSL_CERT_PATH, SSL_KEY_PATH
self.cert_path = cert_path or SSL_CERT_PATH
self.key_path = key_path or SSL_KEY_PATH
# Stelle sicher, dass das Verzeichnis existiert
cert_dir = os.path.dirname(self.cert_path)
if not os.path.exists(cert_dir):
os.makedirs(cert_dir, exist_ok=True)
def generate_mercedes_certificate(self,
hostname: str = "localhost",
validity_days: int = 365) -> bool:
"""
Generiert ein Mercedes-Benz SSL-Zertifikat
Args:
hostname: Hostname für das Zertifikat
validity_days: Gültigkeitsdauer in Tagen
Returns:
bool: True bei Erfolg, False bei Fehler
bool: True wenn erfolgreich, False bei Fehler
"""
ssl_logger.info("Generiere Mercedes-Benz SSL-Zertifikat...")
try:
# Verzeichnisse vorbereiten
self.ensure_directories()
self.cleanup_old_certificates()
print(f"Generiere Mercedes-Benz SSL-Zertifikat für {hostname}...")
# Privaten Schlüssel generieren
# Privaten Schlüssel generieren (4096-bit für höhere Sicherheit)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=key_size,
key_size=4096,
)
ssl_logger.info(f"Privater Schlüssel mit {key_size} Bit generiert")
# Zeitstempel
now = datetime.datetime.now()
valid_until = now + datetime.timedelta(days=validity_days)
# Zertifikatsattribute für Mercedes-Benz
# Subject und Issuer für Mercedes-Benz
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, self.hostname),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, self.mercedes_config["organization"]),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, self.mercedes_config["organizational_unit"]),
x509.NameAttribute(NameOID.LOCALITY_NAME, self.mercedes_config["locality"]),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, self.mercedes_config["state"]),
x509.NameAttribute(NameOID.COUNTRY_NAME, self.mercedes_config["country"]),
x509.NameAttribute(NameOID.EMAIL_ADDRESS, self.mercedes_config["email"]),
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Baden-Württemberg"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Stuttgart"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz Group AG"),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "IT Infrastructure"),
x509.NameAttribute(NameOID.COMMON_NAME, hostname),
x509.NameAttribute(NameOID.EMAIL_ADDRESS, "admin@mercedes-benz.com"),
])
# Subject Alternative Names (SAN) erstellen
san_list = []
for hostname in self.hostnames:
san_list.append(x509.DNSName(hostname))
for ip in self.ip_addresses:
try:
san_list.append(x509.IPAddress(ipaddress.IPv4Address(ip)))
except ipaddress.AddressValueError:
ssl_logger.warning(f"Ungültige IP-Adresse übersprungen: {ip}")
# Zertifikat erstellen
cert = x509.CertificateBuilder().subject_name(
subject
@ -158,230 +78,193 @@ class SSLCertificateManager:
).serial_number(
x509.random_serial_number()
).not_valid_before(
now
datetime.utcnow()
).not_valid_after(
valid_until
).add_extension(
datetime.utcnow() + timedelta(days=validity_days)
)
# Subject Alternative Names hinzufügen
san_list = [
x509.DNSName(hostname),
x509.DNSName("localhost"),
x509.DNSName("*.localhost"),
x509.DNSName("raspberrypi"),
x509.DNSName("*.raspberrypi"),
x509.DNSName("myp.mercedes-benz.local"),
x509.DNSName("*.myp.mercedes-benz.local"),
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
x509.IPAddress(ipaddress.IPv4Address("0.0.0.0")),
]
# Lokale IP-Adresse hinzufügen
try:
local_ip = socket.gethostbyname(socket.gethostname())
if local_ip and local_ip != "127.0.0.1":
san_list.append(x509.IPAddress(ipaddress.IPv4Address(local_ip)))
except:
pass
cert = cert.add_extension(
x509.SubjectAlternativeName(san_list),
critical=False,
).add_extension(
x509.BasicConstraints(ca=True, path_length=None), critical=True
).add_extension(
)
# Key Usage Extension
cert = cert.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,
key_cert_sign=False,
crl_sign=False,
content_commitment=False,
data_encipherment=False,
encipher_only=False,
decipher_only=False
), critical=True
).add_extension(
x509.ExtendedKeyUsage([
x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH,
x509.oid.ExtendedKeyUsageOID.CODE_SIGNING
]), critical=False
).sign(private_key, hashes.SHA256())
decipher_only=False,
),
critical=True,
)
# Zertifikat und Schlüssel speichern
# Extended Key Usage
cert = cert.add_extension(
x509.ExtendedKeyUsage([
ExtendedKeyUsageOID.SERVER_AUTH,
ExtendedKeyUsageOID.CLIENT_AUTH,
]),
critical=True,
)
# Basic Constraints
cert = cert.add_extension(
x509.BasicConstraints(ca=False, path_length=None),
critical=True,
)
# Zertifikat signieren
cert = cert.sign(private_key, hashes.SHA256())
# Zertifikat in Datei schreiben
with open(self.cert_path, "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
# Privaten Schlüssel in Datei schreiben
with open(self.key_path, "wb") as f:
f.write(private_key.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=NoEncryption()
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
with open(self.cert_path, "wb") as f:
f.write(cert.public_bytes(Encoding.PEM))
print(f"✓ SSL-Zertifikat erfolgreich erstellt: {self.cert_path}")
print(f"✓ SSL-Schlüssel erfolgreich erstellt: {self.key_path}")
# Berechtigungen setzen
os.chmod(self.key_path, 0o600)
os.chmod(self.cert_path, 0o644)
ssl_logger.info(f"Mercedes-Benz SSL-Zertifikat erfolgreich erstellt:")
ssl_logger.info(f"- Zertifikat: {os.path.abspath(self.cert_path)}")
ssl_logger.info(f"- Schlüssel: {os.path.abspath(self.key_path)}")
ssl_logger.info(f"- Gültig bis: {valid_until.strftime('%d.%m.%Y')}")
ssl_logger.info(f"- Hostnamen: {', '.join(self.hostnames)}")
ssl_logger.info(f"- IP-Adressen: {', '.join(self.ip_addresses)}")
# Zertifikate ins Frontend kopieren
self._copy_to_frontend()
# Zertifikatsinformationen anzeigen
self._print_certificate_info(cert)
return True
except Exception as e:
ssl_logger.error(f"Fehler beim Erstellen des Mercedes-Benz SSL-Zertifikats: {e}")
print(f"✗ Fehler beim Erstellen des SSL-Zertifikats: {e}")
return False
def _copy_to_frontend(self) -> bool:
"""Kopiert Zertifikate ins Frontend-Verzeichnis"""
def _print_certificate_info(self, cert):
"""Zeigt Informationen über das erstellte Zertifikat an"""
try:
shutil.copy2(self.cert_path, os.path.join(self.frontend_ssl_dir, "myp.crt"))
shutil.copy2(self.key_path, os.path.join(self.frontend_ssl_dir, "myp.key"))
ssl_logger.info(f"Zertifikate ins Frontend kopiert: {os.path.abspath(self.frontend_ssl_dir)}")
return True
print("\n=== Zertifikatsinformationen ===")
print(f"Subject: {cert.subject.rfc4514_string()}")
print(f"Gültig von: {cert.not_valid_before}")
print(f"Gültig bis: {cert.not_valid_after}")
print(f"Seriennummer: {cert.serial_number}")
# SAN anzeigen
try:
san_ext = cert.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
print("Subject Alternative Names:")
for name in san_ext.value:
print(f" - {name}")
except:
pass
print("================================\n")
except Exception as e:
ssl_logger.error(f"Fehler beim Kopieren ins Frontend: {e}")
return False
print(f"Fehler beim Anzeigen der Zertifikatsinformationen: {e}")
def install_system_certificate(self) -> bool:
def certificate_exists(self) -> bool:
"""
Installiert das Zertifikat im System-Zertifikatsspeicher
Nur für Windows-Systeme
"""
if platform.system() != "Windows":
ssl_logger.warning("System-Zertifikatsinstallation nur unter Windows verfügbar")
return False
try:
if not os.path.exists(self.cert_path):
ssl_logger.error(f"Zertifikat nicht gefunden: {self.cert_path}")
return False
# Befehle zum Installieren des Zertifikats im Windows-Zertifikatsspeicher
commands = [
["certutil", "-addstore", "-f", "ROOT", self.cert_path],
["certutil", "-addstore", "-f", "CA", self.cert_path],
["certutil", "-addstore", "-f", "MY", self.cert_path]
]
for cmd in commands:
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
ssl_logger.debug(f"Certutil-Befehl ausgeführt: {' '.join(cmd)}")
ssl_logger.info("Zertifikat erfolgreich im System-Zertifikatsspeicher installiert")
return True
except subprocess.CalledProcessError as e:
ssl_logger.error(f"Fehler bei der Installation des Zertifikats im System: {e}")
return False
except Exception as e:
ssl_logger.error(f"Unerwarteter Fehler bei der Zertifikatsinstallation: {e}")
return False
def copy_to_raspberry(self, host: str = "raspberrypi", user: str = "user", dest: str = "/home/user/Projektarbeit-MYP/backend/app/certs") -> bool:
"""
Kopiert das Zertifikat auf den Raspberry Pi
Args:
host: Hostname des Raspberry Pi
user: Benutzername für SSH
dest: Zielverzeichnis auf dem Raspberry Pi
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
if not os.path.exists(self.cert_path) or not os.path.exists(self.key_path):
ssl_logger.error("Zertifikatsdateien nicht gefunden")
return False
# SSH-Befehl zum Erstellen des Verzeichnisses
ssh_command = ["ssh", f"{user}@{host}", f"mkdir -p {dest}"]
subprocess.run(ssh_command, check=True)
ssl_logger.info(f"Verzeichnis auf Raspberry Pi erstellt: {dest}")
# SCP-Befehle zum Kopieren der Dateien
scp_commands = [
["scp", self.cert_path, f"{user}@{host}:{dest}/myp.crt"],
["scp", self.key_path, f"{user}@{host}:{dest}/myp.key"]
]
for cmd in scp_commands:
subprocess.run(cmd, check=True)
ssl_logger.info(f"Datei kopiert: {cmd[1]} -> {cmd[2]}")
# Berechtigungen setzen
chmod_command = ["ssh", f"{user}@{host}", f"chmod 600 {dest}/myp.key"]
subprocess.run(chmod_command, check=True)
# Zertifikat im System registrieren
install_command = ["ssh", f"{user}@{host}",
f"sudo cp {dest}/myp.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates"]
subprocess.run(install_command, check=True)
ssl_logger.info(f"Zertifikate erfolgreich auf Raspberry Pi installiert: {host}:{dest}")
return True
except subprocess.CalledProcessError as e:
ssl_logger.error(f"Fehler beim Kopieren auf Raspberry Pi: {e}")
return False
except Exception as e:
ssl_logger.error(f"Unerwarteter Fehler beim Raspberry Pi-Transfer: {e}")
return False
def get_certificate_info(self) -> Optional[Dict[str, Any]]:
"""
Gibt Informationen über das aktuelle Zertifikat zurück
Prüft, ob SSL-Zertifikat und Schlüssel existieren
Returns:
Dict mit Zertifikatsinformationen oder None bei Fehler
bool: True wenn beide Dateien existieren
"""
try:
if not os.path.exists(self.cert_path):
return None
return os.path.exists(self.cert_path) and os.path.exists(self.key_path)
def get_certificate_info(self) -> dict:
"""
Gibt Informationen über das vorhandene Zertifikat zurück
Returns:
dict: Zertifikatsinformationen oder None bei Fehler
"""
if not self.certificate_exists():
return None
try:
with open(self.cert_path, "rb") as f:
cert = x509.load_pem_x509_certificate(f.read())
cert_data = f.read()
cert = x509.load_pem_x509_certificate(cert_data)
return {
"subject": cert.subject.rfc4514_string(),
"issuer": cert.issuer.rfc4514_string(),
"serial_number": str(cert.serial_number),
"not_valid_before": cert.not_valid_before.strftime('%d.%m.%Y %H:%M:%S'),
"not_valid_after": cert.not_valid_after.strftime('%d.%m.%Y %H:%M:%S'),
"is_expired": cert.not_valid_after < datetime.datetime.now(),
"days_until_expiry": (cert.not_valid_after - datetime.datetime.now()).days,
"fingerprint": cert.fingerprint(hashes.SHA256()).hex(),
"key_size": cert.public_key().key_size if hasattr(cert.public_key(), 'key_size') else None
"not_valid_before": cert.not_valid_before,
"not_valid_after": cert.not_valid_after,
"serial_number": cert.serial_number,
"is_expired": datetime.utcnow() > cert.not_valid_after,
"days_until_expiry": (cert.not_valid_after - datetime.utcnow()).days
}
except Exception as e:
ssl_logger.error(f"Fehler beim Lesen der Zertifikatsinformationen: {e}")
print(f"Fehler beim Lesen der Zertifikatsinformationen: {e}")
return None
# Globale SSL-Manager-Instanz
ssl_manager = SSLManager()
def ensure_ssl_certificates() -> bool:
"""
Stellt sicher, dass SSL-Zertifikate vorhanden sind
def is_certificate_valid(self) -> bool:
"""
Prüft, ob das aktuelle Zertifikat gültig ist
Returns:
bool: True wenn gültig, False wenn ungültig oder nicht vorhanden
"""
cert_info = self.get_certificate_info()
if not cert_info:
return False
return not cert_info["is_expired"] and cert_info["days_until_expiry"] > 30
def regenerate_if_needed(self) -> bool:
"""
Regeneriert das Zertifikat, falls es ungültig oder bald abgelaufen ist
Returns:
bool: True wenn regeneriert oder bereits gültig, False bei Fehler
"""
if self.is_certificate_valid():
ssl_logger.info("Zertifikat ist noch gültig, keine Regenerierung notwendig")
Returns:
bool: True wenn Zertifikate verfügbar sind
"""
if ssl_manager.certificate_exists():
cert_info = ssl_manager.get_certificate_info()
if cert_info and not cert_info["is_expired"]:
print(f"✓ Gültiges SSL-Zertifikat gefunden (läuft ab in {cert_info['days_until_expiry']} Tagen)")
return True
ssl_logger.info("Zertifikat ist ungültig oder läuft bald ab, regeneriere...")
return self.generate_mercedes_certificate()
# Globale Instanz für einfachen Zugriff
ssl_manager = SSLCertificateManager()
def generate_ssl_certificate() -> bool:
"""Wrapper-Funktion für Rückwärtskompatibilität"""
else:
print("⚠ SSL-Zertifikat ist abgelaufen, erstelle neues...")
print("SSL-Zertifikate nicht gefunden, erstelle neue...")
return ssl_manager.generate_mercedes_certificate()
def get_ssl_certificate_info() -> Optional[Dict[str, Any]]:
"""Wrapper-Funktion für Zertifikatsinformationen"""
return ssl_manager.get_certificate_info()
def ensure_valid_ssl_certificate() -> bool:
"""Stellt sicher, dass ein gültiges SSL-Zertifikat vorhanden ist"""
return ssl_manager.regenerate_if_needed()
if __name__ == "__main__":
# Direkte Ausführung für Tests
print("Mercedes-Benz SSL-Zertifikat-Generator")
print("=====================================")
if ssl_manager.certificate_exists():
print("Vorhandene Zertifikate gefunden:")
info = ssl_manager.get_certificate_info()
if info:
print(f" Subject: {info['subject']}")
print(f" Gültig bis: {info['not_valid_after']}")
print(f" Status: {'Abgelaufen' if info['is_expired'] else 'Gültig'}")
success = ssl_manager.generate_mercedes_certificate()
if success:
print("✓ SSL-Zertifikat erfolgreich generiert!")
else:
print("✗ Fehler beim Generieren des SSL-Zertifikats!")