implemented LE and ACME and fixed some bugs

This commit is contained in:
2025-11-27 04:20:09 +01:00
parent ec1e0da9d5
commit 145dfd3d7c
36 changed files with 10583 additions and 1107 deletions

160
backend/cert_parser.go Normal file
View File

@@ -0,0 +1,160 @@
package main
import (
"crypto/x509"
"database/sql"
"encoding/pem"
"fmt"
"strings"
"time"
)
// ParseCertificateExtrakt Ablaufdatum und CA-Status aus einem PEM-Zertifikat
// Gibt zurück: expiresAt, isIntermediate, error
func ParseCertificate(certPEM string) (time.Time, bool, error) {
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
return time.Time{}, false, fmt.Errorf("fehler beim Dekodieren des PEM-Blocks")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return time.Time{}, false, fmt.Errorf("fehler beim Parsen des Zertifikats: %v", err)
}
expiresAt := cert.NotAfter
// Ein Zertifikat ist Intermediate wenn IsCA=true ist
isIntermediate := cert.IsCA
return expiresAt, isIntermediate, nil
}
// SplitCertificateChain trennt eine PEM-Zertifikatskette in einzelne Zertifikate
// Gibt zurück: leafCert (PEM), intermediateCert (PEM), error
func SplitCertificateChain(certChainPEM string) (string, string, error) {
var leafCert string
var intermediateCert string
// Dekodiere alle PEM-Blöcke aus der Kette
var blocks []*pem.Block
rest := []byte(certChainPEM)
for {
block, remaining := pem.Decode(rest)
if block == nil {
break
}
if block.Type == "CERTIFICATE" {
blocks = append(blocks, block)
}
rest = remaining
}
if len(blocks) == 0 {
return "", "", fmt.Errorf("keine Zertifikate in der Kette gefunden")
}
// Parse jedes Zertifikat und trenne nach IsCA
for _, block := range blocks {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
continue // Überspringe ungültige Zertifikate
}
// Encode zurück zu PEM
certPEM := string(pem.EncodeToMemory(block))
if cert.IsCA {
// Intermediate CA
if intermediateCert != "" {
// Wenn bereits ein Intermediate vorhanden ist, hänge es an (kann mehrere geben)
intermediateCert += "\n" + certPEM
} else {
intermediateCert = certPEM
}
} else {
// Leaf Certificate
if leafCert != "" {
// Wenn bereits ein Leaf vorhanden ist, verwende das erste (sollte nur eines geben)
continue
}
leafCert = certPEM
}
}
return leafCert, intermediateCert, nil
}
// GetCertificateIssuer extrahiert den Issuer-Namen aus einem PEM-Zertifikat
// Gibt zurück: issuerName (string), error
func GetCertificateIssuer(certPEM string) (string, error) {
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
return "", fmt.Errorf("fehler beim Dekodieren des PEM-Blocks")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("fehler beim Parsen des Zertifikats: %v", err)
}
return cert.Issuer.String(), nil
}
// GetProviderNameFromIssuer bestimmt den Provider-Namen basierend auf dem Issuer
func GetProviderNameFromIssuer(issuer string) string {
issuerLower := fmt.Sprintf("%v", issuer)
if strings.Contains(issuerLower, "Let's Encrypt") || strings.Contains(issuerLower, "letsencrypt") {
return "Let's Encrypt"
}
if strings.Contains(issuerLower, "DigiCert") {
return "DigiCert"
}
if strings.Contains(issuerLower, "GlobalSign") {
return "GlobalSign"
}
if strings.Contains(issuerLower, "Sectigo") {
return "Sectigo"
}
if strings.Contains(issuerLower, "GoDaddy") {
return "GoDaddy"
}
// Fallback: Gib den Issuer-Namen zurück
return issuer
}
// CheckExistingValidCertificate prüft ob bereits ein gültiges Zertifikat für einen FQDN existiert
// Gibt zurück: exists (bool), expiresAt (time.Time), error
func CheckExistingValidCertificate(fqdnID, spaceID string) (bool, time.Time, error) {
var certPEM string
var expiresAtStr sql.NullString
err := db.QueryRow(`
SELECT certificate_pem, expires_at
FROM certificates
WHERE fqdn_id = ? AND space_id = ? AND status = 'issued'
ORDER BY created_at DESC
LIMIT 1
`, fqdnID, spaceID).Scan(&certPEM, &expiresAtStr)
if err != nil {
if err == sql.ErrNoRows {
return false, time.Time{}, nil
}
return false, time.Time{}, err
}
// Wenn expires_at bereits in DB vorhanden ist, verwende es
if expiresAtStr.Valid && expiresAtStr.String != "" {
expiresAt, err := time.Parse("2006-01-02 15:04:05", expiresAtStr.String)
if err == nil {
return true, expiresAt, nil
}
}
// Sonst parse das Zertifikat
expiresAt, _, err := ParseCertificate(certPEM)
if err != nil {
return true, time.Time{}, err
}
return true, expiresAt, nil
}