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:
parent
8d84b9157e
commit
8dec03fac7
@ -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
|
||||
```
|
121
backend/RASPBERRY_PI_SETUP.md
Normal file
121
backend/RASPBERRY_PI_SETUP.md
Normal 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.
@ -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!")
|
Loading…
x
Reference in New Issue
Block a user