104 lines
3.6 KiB
Go
104 lines
3.6 KiB
Go
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
|
|
} |