package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "fmt" "log" "math/big" "net" "net/http" "os" "path/filepath" "time" "github.com/cynfo/rmm-backend/api" "github.com/cynfo/rmm-backend/config" "github.com/cynfo/rmm-backend/db" "github.com/cynfo/rmm-backend/ws" ) func main() { cfgPath := "config.yaml" if len(os.Args) > 1 { cfgPath = os.Args[1] } cfg, err := config.Load(cfgPath) if err != nil { log.Fatalf("Konfiguration laden fehlgeschlagen: %v", err) } // TLS-Zertifikate pruefen/generieren if err := ensureTLS(cfg.TLSCert, cfg.TLSKey); err != nil { log.Fatalf("TLS-Setup fehlgeschlagen: %v", err) } // Datenbank initialisieren database, err := db.New(cfg.DBPath) if err != nil { log.Fatalf("Datenbank-Fehler: %v", err) } defer database.Close() // WebSocket Hub starten hub := ws.NewHub() go hub.Run() // Router aufbauen mux := http.NewServeMux() handler := api.NewHandler(database) handler.SetupRoutes(mux, hub) // Middleware-Chain: Logging -> Auth -> Handler var chain http.Handler = mux chain = api.APIKeyAuth(cfg.APIKeys, chain) chain = api.Logging(chain) log.Printf("RMM Backend startet auf %s (TLS)", cfg.ListenAddr) log.Printf("API-Keys konfiguriert: %d", len(cfg.APIKeys)) if err := http.ListenAndServeTLS(cfg.ListenAddr, cfg.TLSCert, cfg.TLSKey, chain); err != nil { log.Fatalf("Server-Fehler: %v", err) } } // ensureTLS generiert self-signed Zertifikate wenn keine vorhanden func ensureTLS(certPath, keyPath string) error { if _, err := os.Stat(certPath); err == nil { if _, err := os.Stat(keyPath); err == nil { log.Println("TLS-Zertifikate gefunden") return nil } } log.Println("Generiere self-signed TLS-Zertifikat...") // Verzeichnis erstellen if dir := filepath.Dir(certPath); dir != "" && dir != "." { os.MkdirAll(dir, 0755) } if dir := filepath.Dir(keyPath); dir != "" && dir != "." { os.MkdirAll(dir, 0755) } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return fmt.Errorf("Key generieren: %w", err) } serial, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) template := x509.Certificate{ SerialNumber: serial, Subject: pkix.Name{ Organization: []string{"RMM Backend"}, CommonName: "rmm-backend", }, NotBefore: time.Now(), NotAfter: time.Now().Add(365 * 24 * time.Hour * 10), // 10 Jahre KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("192.168.85.13")}, DNSNames: []string{"localhost", "rmm-backend"}, } certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) if err != nil { return fmt.Errorf("Zertifikat erstellen: %w", err) } certFile, err := os.Create(certPath) if err != nil { return err } defer certFile.Close() pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}) keyBytes, err := x509.MarshalECPrivateKey(key) if err != nil { return err } keyFile, err := os.OpenFile(keyPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) if err != nil { return err } defer keyFile.Close() pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}) log.Printf("TLS-Zertifikat generiert: %s, %s", certPath, keyPath) return nil }