WIP: Save current state - security subsystems, migrations, logging

This commit is contained in:
Fimeg
2025-12-16 14:19:59 -05:00
parent f792ab23c7
commit f7c8d23c5d
89 changed files with 8884 additions and 1394 deletions

View File

@@ -7,10 +7,12 @@ import (
"io"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/Fimeg/RedFlag/aggregator/pkg/common"
"github.com/Fimeg/RedFlag/aggregator-agent/internal/common"
"github.com/Fimeg/RedFlag/aggregator-agent/internal/version"
)
// AgentFileInventory represents all files associated with an agent installation
@@ -26,14 +28,14 @@ type AgentFileInventory struct {
// MigrationDetection represents the result of migration detection
type MigrationDetection struct {
CurrentAgentVersion string `json:"current_agent_version"`
CurrentConfigVersion int `json:"current_config_version"`
RequiresMigration bool `json:"requires_migration"`
RequiredMigrations []string `json:"required_migrations"`
MissingSecurityFeatures []string `json:"missing_security_features"`
CurrentAgentVersion string `json:"current_agent_version"`
CurrentConfigVersion int `json:"current_config_version"`
RequiresMigration bool `json:"requires_migration"`
RequiredMigrations []string `json:"required_migrations"`
MissingSecurityFeatures []string `json:"missing_security_features"`
Inventory *AgentFileInventory `json:"inventory"`
DockerDetection *DockerDetection `json:"docker_detection,omitempty"`
DetectionTime time.Time `json:"detection_time"`
DockerDetection *DockerDetection `json:"docker_detection,omitempty"`
DetectionTime time.Time `json:"detection_time"`
}
// SecurityFeature represents a security feature that may be missing
@@ -59,8 +61,8 @@ func NewFileDetectionConfig() *FileDetectionConfig {
OldConfigPath: "/etc/aggregator",
OldStatePath: "/var/lib/aggregator",
NewConfigPath: "/etc/redflag",
NewStatePath: "/var/lib/redflag",
BackupDirPattern: "/etc/redflag.backup.%s",
NewStatePath: "/var/lib/redflag-agent",
BackupDirPattern: "/var/lib/redflag-agent/migration_backups_%s",
}
}
@@ -155,15 +157,15 @@ func scanAgentFiles(config *FileDetectionConfig) (*AgentFileInventory, error) {
// Categorize files
for _, file := range files {
switch {
case containsAny(file.Path, filePatterns["config"]):
case ContainsAny(file.Path, filePatterns["config"]):
inventory.ConfigFiles = append(inventory.ConfigFiles, file)
case containsAny(file.Path, filePatterns["state"]):
case ContainsAny(file.Path, filePatterns["state"]):
inventory.StateFiles = append(inventory.StateFiles, file)
case containsAny(file.Path, filePatterns["binary"]):
case ContainsAny(file.Path, filePatterns["binary"]):
inventory.BinaryFiles = append(inventory.BinaryFiles, file)
case containsAny(file.Path, filePatterns["log"]):
case ContainsAny(file.Path, filePatterns["log"]):
inventory.LogFiles = append(inventory.LogFiles, file)
case containsAny(file.Path, filePatterns["certificate"]):
case ContainsAny(file.Path, filePatterns["certificate"]):
inventory.CertificateFiles = append(inventory.CertificateFiles, file)
}
}
@@ -280,32 +282,98 @@ func readConfigVersion(configPath string) (string, int, error) {
func determineRequiredMigrations(detection *MigrationDetection, config *FileDetectionConfig) []string {
var migrations []string
// Check migration state to skip already completed migrations
configPath := filepath.Join(config.NewConfigPath, "config.json")
stateManager := NewStateManager(configPath)
// Check if old directories exist
for _, oldDir := range detection.Inventory.OldDirectoryPaths {
if _, err := os.Stat(oldDir); err == nil {
migrations = append(migrations, "directory_migration")
// Check if directory migration was already completed
completed, err := stateManager.IsMigrationCompleted("directory_migration")
if err == nil && !completed {
migrations = append(migrations, "directory_migration")
}
break
}
}
// Check config version compatibility
if detection.CurrentConfigVersion < 4 {
migrations = append(migrations, "config_migration")
// Check for legacy installation (old path migration)
hasLegacyDirs := false
for _, oldDir := range detection.Inventory.OldDirectoryPaths {
if _, err := os.Stat(oldDir); err == nil {
hasLegacyDirs = true
break
}
}
// Check if Docker secrets migration is needed (v5)
if detection.CurrentConfigVersion < 5 {
migrations = append(migrations, "config_v5_migration")
// Legacy migration: always migrate if old directories exist
if hasLegacyDirs {
if detection.CurrentConfigVersion < 4 {
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("config_migration")
if err == nil && !completed {
migrations = append(migrations, "config_migration")
}
}
// Check if Docker secrets migration is needed (v5)
if detection.CurrentConfigVersion < 5 {
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("config_v5_migration")
if err == nil && !completed {
migrations = append(migrations, "config_v5_migration")
}
}
} else {
// Version-based migration: compare current config version with expected
// This handles upgrades for agents already in correct location
// Use version package for single source of truth
agentVersion := version.Version
expectedConfigVersionStr := version.ExtractConfigVersionFromAgent(agentVersion)
// Convert to int for comparison (e.g., "6" -> 6)
expectedConfigVersion := 6 // Default fallback
if expectedConfigInt, err := strconv.Atoi(expectedConfigVersionStr); err == nil {
expectedConfigVersion = expectedConfigInt
}
// If config file exists but version is old, migrate
if detection.CurrentConfigVersion < expectedConfigVersion {
if detection.CurrentConfigVersion < 4 {
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("config_migration")
if err == nil && !completed {
migrations = append(migrations, "config_migration")
}
}
// Check if Docker secrets migration is needed (v5)
if detection.CurrentConfigVersion < 5 {
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("config_v5_migration")
if err == nil && !completed {
migrations = append(migrations, "config_v5_migration")
}
}
}
}
// Check if Docker secrets migration is needed
if detection.DockerDetection != nil && detection.DockerDetection.MigrateToSecrets {
migrations = append(migrations, "docker_secrets_migration")
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("docker_secrets_migration")
if err == nil && !completed {
migrations = append(migrations, "docker_secrets_migration")
}
}
// Check if security features need to be applied
if len(detection.MissingSecurityFeatures) > 0 {
migrations = append(migrations, "security_hardening")
// Check if already completed
completed, err := stateManager.IsMigrationCompleted("security_hardening")
if err == nil && !completed {
migrations = append(migrations, "security_hardening")
}
}
return migrations
@@ -389,7 +457,7 @@ func calculateFileChecksum(filePath string) (string, error) {
return fmt.Sprintf("%x", hash.Sum(nil)), nil
}
func containsAny(path string, patterns []string) bool {
func ContainsAny(path string, patterns []string) bool {
for _, pattern := range patterns {
if matched, _ := filepath.Match(pattern, filepath.Base(path)); matched {
return true
@@ -404,7 +472,7 @@ func isRequiredFile(path string, patterns map[string][]string) bool {
}
func shouldMigrateFile(path string, patterns map[string][]string) bool {
return !containsAny(path, []string{"*.log", "*.tmp"})
return !ContainsAny(path, []string{"*.log", "*.tmp"})
}
func getFileDescription(path string) string {
@@ -444,4 +512,4 @@ func detectBinaryVersion(binaryPath string) string {
// This would involve reading binary headers or executing with --version flag
// For now, return empty
return ""
}
}