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/monitor" "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.Database) if err != nil { log.Fatalf("Datenbank-Fehler: %v", err) } defer database.Close() // WebSocket Hub starten hub := ws.NewHub() hub.SetEventCallback(func(agentID, eventType, message string) { database.InsertAgentEvent(agentID, eventType, message) }) go hub.Run() // Inaktivitaets-Monitor starten mon := monitor.New(database) go mon.Run() // Task-Scheduler starten scheduler := api.NewTaskScheduler(database, hub) go scheduler.Run() // Config API-Keys in DB migrieren (einmalig) database.MigrateConfigAPIKeys(cfg.APIKeys) // API-Key Cache (laedt Keys aus DB, refresht alle 30s) apiKeyCache := api.NewAPIKeyCache(database) // Auth-Handler initialisieren authHandler := api.NewAuthHandler(database, cfg.JWTSecret) authHandler.EnsureDefaultAdmin() // Router aufbauen - Auth-Routes ohne Auth-Middleware authMux := http.NewServeMux() authHandler.SetupAuthRoutes(authMux) // Geschuetzte Routes protectedMux := http.NewServeMux() handler := api.NewHandler(database) handler.SetScheduler(scheduler) handler.SetupRoutes(protectedMux, hub) // GET /api/v1/auth/me braucht JWT protectedMux.HandleFunc("GET /api/v1/auth/me", authHandler.Me) // Combined router: auth routes ungeschuetzt, rest mit DB-basierter Auth mux := http.NewServeMux() mux.Handle("POST /api/v1/auth/login", authMux) mux.Handle("/", api.CombinedAuthWithCache(apiKeyCache, authHandler, protectedMux)) // Middleware-Chain: CORS -> Logging -> Handler var chain http.Handler = mux chain = api.Logging(chain) chain = api.CORS(chain) log.Printf("RMM Backend startet auf %s (TLS)", cfg.ListenAddr) log.Printf("API-Keys (Config): %d, DB-basierte Validierung aktiv", 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 }