diff --git a/aggregator-server/internal/api/handlers/build_orchestrator.go b/aggregator-server/internal/api/handlers/build_orchestrator.go index 376e992..a135cb2 100644 --- a/aggregator-server/internal/api/handlers/build_orchestrator.go +++ b/aggregator-server/internal/api/handlers/build_orchestrator.go @@ -91,6 +91,7 @@ func NewAgentBuild(c *gin.Context) { } // UpgradeAgentBuild handles agent upgrade requests +// Deprecated: Use ConfigService for config building func UpgradeAgentBuild(c *gin.Context) { agentID := c.Param("agentID") if agentID == "" { diff --git a/aggregator-server/internal/services/agent_builder.go b/aggregator-server/internal/services/agent_builder.go index ef62ef0..c5991ce 100644 --- a/aggregator-server/internal/services/agent_builder.go +++ b/aggregator-server/internal/services/agent_builder.go @@ -11,6 +11,7 @@ import ( ) // AgentBuilder handles generating embedded agent configurations +// Deprecated: Configuration logic should use services.ConfigService type AgentBuilder struct { buildContext string } @@ -21,6 +22,7 @@ func NewAgentBuilder() *AgentBuilder { } // BuildAgentWithConfig generates agent configuration and prepares signed binary +// Deprecated: Delegate config generation to services.ConfigService func (ab *AgentBuilder) BuildAgentWithConfig(config *AgentConfiguration) (*BuildResult, error) { // Create temporary build directory buildDir, err := os.MkdirTemp("", "agent-build-") diff --git a/aggregator-server/internal/services/agent_lifecycle.go b/aggregator-server/internal/services/agent_lifecycle.go index 31f9c94..fb0bd46 100644 --- a/aggregator-server/internal/services/agent_lifecycle.go +++ b/aggregator-server/internal/services/agent_lifecycle.go @@ -269,27 +269,6 @@ func (s *BuildService) BuildArtifacts(ctx context.Context, cfg *AgentConfig) (*B return &BuildArtifacts{}, nil } -// ConfigService placeholder (to be implemented) -type ConfigService struct { - db *sqlx.DB - config *config.Config - logger *log.Logger -} - -func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService { - return &ConfigService{db: db, config: cfg, logger: logger} -} - -func (s *ConfigService) GenerateNewConfig(cfg *AgentConfig) ([]byte, error) { - // Placeholder: Return empty JSON - return []byte("{}"), nil -} - -func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) { - // Placeholder: Return empty JSON - return []byte("{}"), nil -} - // ArtifactService placeholder (to be implemented) type ArtifactService struct { db *sqlx.DB diff --git a/aggregator-server/internal/services/config_builder.go b/aggregator-server/internal/services/config_builder.go index 8388b58..e673d12 100644 --- a/aggregator-server/internal/services/config_builder.go +++ b/aggregator-server/internal/services/config_builder.go @@ -37,6 +37,8 @@ type PublicKeyResponse struct { } // ConfigBuilder handles dynamic agent configuration generation +// ConfigBuilder builds agent configurations +// Deprecated: Use services.ConfigService instead type ConfigBuilder struct { serverURL string templates map[string]AgentTemplate diff --git a/aggregator-server/internal/services/config_service.go b/aggregator-server/internal/services/config_service.go new file mode 100644 index 0000000..25b48e1 --- /dev/null +++ b/aggregator-server/internal/services/config_service.go @@ -0,0 +1,177 @@ +package services + +import ( + "context" + "encoding/json" + "fmt" + "log" + "time" + + "github.com/Fimeg/RedFlag/aggregator-server/internal/config" + "github.com/Fimeg/RedFlag/aggregator-server/internal/models" + "github.com/google/uuid" + "github.com/jmoiron/sqlx" +) + +// ConfigService manages agent configuration generation and validation +type ConfigService struct { + db *sqlx.DB + config *config.Config + logger *log.Logger +} + +// NewConfigService creates a new configuration service +func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService { + return &ConfigService{ + db: db, + config: cfg, + logger: logger, + } +} + +// AgentConfigData represents agent configuration structure +type AgentConfigData struct { + AgentID string `json:"agent_id"` + Version string `json:"version"` + Platform string `json:"platform"` + ServerURL string `json:"server_url"` + LogLevel string `json:"log_level"` + Intervals map[string]int `json:"intervals"` + Subsystems map[string]interface{} `json:"subsystems"` + MaxRetries int `json:"max_retries"` + TimeoutSeconds int `json:"timeout_seconds"` + MachineID string `json:"machine_id"` + AgentType string `json:"agent_type"` + ConfigPath string `json:"config_path"` + StatePath string `json:"state_path"` + LogPath string `json:"log_path"` + ServiceName string `json:"service_name"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// GenerateNewConfig creates configuration for a new agent +func (s *ConfigService) GenerateNewConfig(agentCfg *AgentConfig) ([]byte, error) { + // Base configuration + serverURL := fmt.Sprintf("http://%s:%d", s.config.Server.Host, s.config.Server.Port) + if s.config.Server.PublicURL != "" { + serverURL = s.config.Server.PublicURL + } + + cfg := &AgentConfigData{ + AgentID: agentCfg.AgentID, + Version: agentCfg.Version, + Platform: agentCfg.Platform, + ServerURL: serverURL, + LogLevel: "info", + Intervals: map[string]int{ + "metrics": 300, // 5 minutes + "updates": 3600, // 1 hour + "commands": 30, // 30 seconds + }, + Subsystems: map[string]interface{}{ + "updates": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300}, + "storage": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60}, + "system": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60}, + "docker": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 120}, + }, + MaxRetries: 3, + TimeoutSeconds: 30, + MachineID: agentCfg.MachineID, + AgentType: agentCfg.AgentType, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + // Platform-specific customizations + s.applyPlatformDefaults(cfg) + + // Validate configuration + if err := s.validateConfig(cfg); err != nil { + return nil, fmt.Errorf("validation failed: %w", err) + } + + // Marshal to JSON + configJSON, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + return nil, fmt.Errorf("marshal failed: %w", err) + } + + return configJSON, nil +} + +// LoadExistingConfig retrieves and updates existing agent configuration +func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) { + // Get existing agent from database + var agent models.Agent + query := `SELECT * FROM agents WHERE id = $1` + err := s.db.Get(&agent, query, agentID) + if err != nil { + return nil, fmt.Errorf("agent not found: %w", err) + } + + // Generate new config based on agent data + agentCfg := &AgentConfig{ + AgentID: agentID, + Version: agent.CurrentVersion, + Platform: agent.OSType, + Architecture: agent.OSArchitecture, + MachineID: "", + AgentType: "", // Could be stored in Metadata + Hostname: agent.Hostname, + } + + // Use GenerateNewConfig to create config + return s.GenerateNewConfig(agentCfg) +} + +// applyPlatformDefaults applies platform-specific configuration +func (s *ConfigService) applyPlatformDefaults(cfg *AgentConfigData) { + switch cfg.Platform { + case "windows-amd64", "windows-arm64", "windows-386": + // Windows-specific paths + cfg.ConfigPath = "C:\\ProgramData\\RedFlag\\config.json" + cfg.StatePath = "C:\\ProgramData\\RedFlag\\state\\" + cfg.LogPath = "C:\\ProgramData\\RedFlag\\logs\\" + cfg.ServiceName = "RedFlagAgent" + + // Windows-specific subsystems + cfg.Subsystems["windows"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300} + cfg.Subsystems["winget"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 180} + + default: + // Linux defaults + cfg.ConfigPath = "/etc/redflag/config.json" + cfg.StatePath = "/var/lib/redflag/" + cfg.LogPath = "/var/log/redflag/" + cfg.ServiceName = "redflag-agent" + } +} + +// validateConfig validates configuration +func (s *ConfigService) validateConfig(cfg *AgentConfigData) error { + if cfg.AgentID == "" { + return fmt.Errorf("agent_id is required") + } + if cfg.Version == "" { + return fmt.Errorf("version is required") + } + if cfg.Platform == "" { + return fmt.Errorf("platform is required") + } + if cfg.ServerURL == "" { + return fmt.Errorf("server_url is required") + } + + return nil +} + +// SaveConfig saves agent configuration to database +func (s *ConfigService) SaveConfig(ctx context.Context, agentID uuid.UUID, configJSON []byte) error { + query := ` + UPDATE agents SET config = $1, updated_at = $2 + WHERE id = $3 + ` + _, err := s.db.ExecContext(ctx, query, configJSON, time.Now(), agentID) + return err +}