WIP: Save current state - security subsystems, migrations, logging
This commit is contained in:
@@ -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 ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user