package orchestrator import ( "crypto/ed25519" "fmt" "log" "github.com/Fimeg/RedFlag/aggregator-agent/internal/client" "github.com/Fimeg/RedFlag/aggregator-agent/internal/config" "github.com/Fimeg/RedFlag/aggregator-agent/internal/crypto" "github.com/Fimeg/RedFlag/aggregator-agent/internal/logging" "github.com/google/uuid" ) // CommandHandler handles command processing with signature verification type CommandHandler struct { verifier *crypto.CommandVerifier securityLogger *logging.SecurityLogger serverPublicKey ed25519.PublicKey logger *log.Logger } // CommandSigningConfig holds configuration for command signing type CommandSigningConfig struct { Enabled bool `json:"enabled" env:"REDFLAG_AGENT_COMMAND_SIGNING_ENABLED" default:"true"` EnforcementMode string `json:"enforcement_mode" env:"REDFLAG_AGENT_COMMAND_ENFORCEMENT_MODE" default:"strict"` // strict, warning, disabled } // NewCommandHandler creates a new command handler func NewCommandHandler(cfg *config.Config, securityLogger *logging.SecurityLogger, logger *log.Logger) (*CommandHandler, error) { handler := &CommandHandler{ securityLogger: securityLogger, logger: logger, verifier: crypto.NewCommandVerifier(), } // Load server public key if command signing is enabled if cfg.CommandSigning.Enabled { publicKey, err := crypto.LoadCachedPublicKey() if err != nil { // Try to fetch from server if not cached publicKey, err = crypto.GetPublicKey(cfg.ServerURL) if err != nil { return nil, fmt.Errorf("failed to load server public key: %w", err) } } handler.serverPublicKey = publicKey } return handler, nil } // ProcessCommand processes a command with signature verification func (h *CommandHandler) ProcessCommand(cmd client.CommandItem, cfg *config.Config, agentID uuid.UUID) error { config := cfg.CommandSigning if config.Enabled { if config.EnforcementMode == "strict" { // Strict mode: Verification is required if cmd.Signature == "" { err := fmt.Errorf("strict enforcement enabled but command not signed") h.securityLogger.LogCommandVerificationFailure(cmd.ID, "missing signature") return fmt.Errorf("command verification failed: %w", err) } err := h.verifier.VerifyCommand(cmd, h.serverPublicKey) if err != nil { h.securityLogger.LogCommandVerificationFailure(cmd.ID, err.Error()) return fmt.Errorf("command verification failed: %w", err) } h.securityLogger.LogCommandVerificationSuccess(cmd.ID) } else if config.EnforcementMode == "warning" { // Warning mode: Log failures but allow execution if cmd.Signature != "" { err := h.verifier.VerifyCommand(cmd, h.serverPublicKey) if err != nil { h.logger.Printf("[WARNING] Command verification failed but allowed in warning mode: %v", err) h.securityLogger.LogCommandVerificationFailure(cmd.ID, err.Error()) } else { h.securityLogger.LogCommandVerificationSuccess(cmd.ID) } } else { h.logger.Printf("[WARNING] Command not signed but allowed in warning mode") } } // disabled mode: Skip verification entirely } else if cmd.Signature != "" { // Signing is disabled but command has signature - log info h.logger.Printf("[INFO] Command has signature but signing is disabled") } return nil } // UpdateServerPublicKey updates the cached server public key func (h *CommandHandler) UpdateServerPublicKey(serverURL string) error { publicKey, err := crypto.FetchAndCacheServerPublicKey(serverURL) if err != nil { return fmt.Errorf("failed to update server public key: %w", err) } h.serverPublicKey = publicKey h.logger.Printf("Server public key updated successfully") return nil }