package services import ( "fmt" "log" "os" "path/filepath" "strings" "github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" ) // BuildOrchestratorService handles building and signing agent binaries type BuildOrchestratorService struct { signingService *SigningService packageQueries *queries.PackageQueries agentDir string // Directory containing pre-built binaries } // NewBuildOrchestratorService creates a new build orchestrator service func NewBuildOrchestratorService(signingService *SigningService, packageQueries *queries.PackageQueries, agentDir string) *BuildOrchestratorService { return &BuildOrchestratorService{ signingService: signingService, packageQueries: packageQueries, agentDir: agentDir, } } // BuildAndSignAgent builds (or retrieves) and signs an agent binary func (s *BuildOrchestratorService) BuildAndSignAgent(version, platform, architecture string) (*models.AgentUpdatePackage, error) { // Determine binary name binaryName := "redflag-agent" if strings.HasPrefix(platform, "windows") { binaryName += ".exe" } // Path to pre-built binary binaryPath := filepath.Join(s.agentDir, "binaries", platform, binaryName) // Check if binary exists if _, err := os.Stat(binaryPath); os.IsNotExist(err) { return nil, fmt.Errorf("binary not found for platform %s: %w", platform, err) } // Sign the binary if signing is enabled if s.signingService.IsEnabled() { signedPackage, err := s.signingService.SignFile(binaryPath) if err != nil { return nil, fmt.Errorf("failed to sign agent binary: %w", err) } // Set additional fields signedPackage.Version = version signedPackage.Platform = platform signedPackage.Architecture = architecture // Store signed package in database err = s.packageQueries.StoreSignedPackage(signedPackage) if err != nil { return nil, fmt.Errorf("failed to store signed package: %w", err) } log.Printf("Successfully signed and stored agent binary: %s (%s/%s)", signedPackage.ID, platform, architecture) return signedPackage, nil } else { log.Printf("Signing disabled, creating unsigned package entry") // Create unsigned package entry for backward compatibility unsignedPackage := &models.AgentUpdatePackage{ ID: uuid.New(), Version: version, Platform: platform, Architecture: architecture, BinaryPath: binaryPath, Signature: "", Checksum: "", // Would need to calculate if needed FileSize: 0, // Would need to stat if needed CreatedBy: "build-orchestrator", IsActive: true, } // Get file info if info, err := os.Stat(binaryPath); err == nil { unsignedPackage.FileSize = info.Size() } // Store unsigned package err := s.packageQueries.StoreSignedPackage(unsignedPackage) if err != nil { return nil, fmt.Errorf("failed to store unsigned package: %w", err) } return unsignedPackage, nil } } // SignExistingBinary signs an existing binary file func (s *BuildOrchestratorService) SignExistingBinary(binaryPath, version, platform, architecture string) (*models.AgentUpdatePackage, error) { // Check if file exists if _, err := os.Stat(binaryPath); os.IsNotExist(err) { return nil, fmt.Errorf("binary not found: %s", binaryPath) } // Sign the binary if signing is enabled if !s.signingService.IsEnabled() { return nil, fmt.Errorf("signing service is disabled") } signedPackage, err := s.signingService.SignFile(binaryPath) if err != nil { return nil, fmt.Errorf("failed to sign agent binary: %w", err) } // Set additional fields signedPackage.Version = version signedPackage.Platform = platform signedPackage.Architecture = architecture // Store signed package in database err = s.packageQueries.StoreSignedPackage(signedPackage) if err != nil { return nil, fmt.Errorf("failed to store signed package: %w", err) } log.Printf("Successfully signed and stored agent binary: %s (%s/%s)", signedPackage.ID, platform, architecture) return signedPackage, nil } // GetSignedPackage retrieves a signed package by version and platform func (s *BuildOrchestratorService) GetSignedPackage(version, platform, architecture string) (*models.AgentUpdatePackage, error) { return s.packageQueries.GetSignedPackage(version, platform, architecture) } // ListSignedPackages lists all signed packages (with optional filters) func (s *BuildOrchestratorService) ListSignedPackages(version, platform string, limit, offset int) ([]models.AgentUpdatePackage, error) { return s.packageQueries.ListUpdatePackages(version, platform, limit, offset) }