package meshagent import ( "context" "crypto/tls" "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "runtime" "strings" "time" ) // IsInstalled prüft ob der MeshAgent-Prozess läuft oder das Binary vorhanden ist. func IsInstalled() bool { if runtime.GOOS == "windows" { // Prüfe ob MeshAgent Service läuft cmd := exec.Command("sc", "query", "Mesh Agent") return cmd.Run() == nil } // Linux: prüfe ob meshagent Prozess läuft cmd := exec.Command("pgrep", "-x", "meshagent") return cmd.Run() == nil } // Install lädt den MeshAgent von MeshCentral herunter und installiert ihn. // meshCentralUrl: z.B. "https://192.168.1.100:4430" func Install(ctx context.Context, meshCentralUrl string) error { if IsInstalled() { log.Println("MeshAgent ist bereits installiert") return nil } log.Printf("MeshAgent wird von %s heruntergeladen...", meshCentralUrl) // Agent-ID je nach OS agentID := "6" // Linux x64 if runtime.GOOS == "windows" { agentID = "3" // Windows x64 } downloadUrl := fmt.Sprintf("%s/meshagents?id=%s", strings.TrimRight(meshCentralUrl, "/"), agentID) // SSL-Fehler ignorieren (selbstsigniertes Zertifikat in Dev) httpClient := &http.Client{ Timeout: 5 * time.Minute, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, } req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadUrl, nil) if err != nil { return fmt.Errorf("HTTP Request konnte nicht erstellt werden: %w", err) } resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("Download fehlgeschlagen: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("MeshCentral antwortete mit Status %d — ist MeshCentral gestartet?", resp.StatusCode) } // Temp-Datei speichern var tmpPath string if runtime.GOOS == "windows" { tmpPath = filepath.Join(os.TempDir(), "meshagent.exe") } else { tmpPath = filepath.Join(os.TempDir(), "meshagent") } f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) if err != nil { return fmt.Errorf("Temp-Datei konnte nicht erstellt werden: %w", err) } if _, err := io.Copy(f, resp.Body); err != nil { f.Close() return fmt.Errorf("Fehler beim Schreiben: %w", err) } f.Close() log.Printf("MeshAgent heruntergeladen nach %s", tmpPath) // Installieren return installBinary(ctx, tmpPath, meshCentralUrl) } func installBinary(ctx context.Context, binaryPath, meshCentralUrl string) error { if runtime.GOOS == "windows" { // Windows: als Service installieren cmd := exec.CommandContext(ctx, binaryPath, "-install", "-url", meshCentralUrl) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("MeshAgent Windows-Installation fehlgeschlagen: %w", err) } log.Println("MeshAgent als Windows Service installiert") } else { // Linux: in /usr/local/bin installieren und als Service starten installPath := "/usr/local/bin/meshagent" if err := os.Rename(binaryPath, installPath); err != nil { // Falls rename scheitert (cross-device), kopieren if err2 := copyFile(binaryPath, installPath); err2 != nil { return fmt.Errorf("MeshAgent konnte nicht nach %s verschoben werden: %w", installPath, err2) } } if err := os.Chmod(installPath, 0755); err != nil { return fmt.Errorf("chmod fehlgeschlagen: %w", err) } // Als Service starten (systemd oder direkt) cmd := exec.CommandContext(ctx, installPath, "-install", "-url", meshCentralUrl) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { // Direkt starten als Fallback log.Printf("Service-Installation fehlgeschlagen, starte direkt: %v", err) go func() { exec.Command(installPath, "-url", meshCentralUrl).Run() }() } log.Println("MeshAgent auf Linux installiert") } return nil } func copyFile(src, dst string) error { srcFile, err := os.Open(src) if err != nil { return err } defer srcFile.Close() dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) if err != nil { return err } defer dstFile.Close() _, err = io.Copy(dstFile, srcFile) return err }