feat: add config sync endpoint and security UI updates

- Add GET /api/v1/agents/:id/config endpoint for server configuration
- Agent fetches config during check-in and applies updates
- Add version tracking to prevent unnecessary config applications
- Clean separation: config sync independent of commands
- Fix agent UI subsystem settings to actually control agent behavior
- Update Security Health UI with frosted glass styling and tooltips
This commit is contained in:
Fimeg
2025-11-03 22:36:26 -05:00
parent eccc38d7c9
commit 38894f64d3
18 changed files with 944 additions and 87 deletions

View File

@@ -31,6 +31,10 @@ const (
AgentVersion = "0.1.22" // v0.1.22: Machine binding and version enforcement security release
)
var (
lastConfigVersion int64 = 0 // Track last applied config version
)
// getConfigPath returns the platform-specific config path
func getConfigPath() string {
if runtime.GOOS == "windows" {
@@ -452,6 +456,79 @@ func renewTokenIfNeeded(apiClient *client.Client, cfg *config.Config, err error)
return apiClient, nil
}
// syncServerConfig checks for and applies server configuration updates
func syncServerConfig(apiClient *client.Client, cfg *config.Config) error {
// Get current config from server
serverConfig, err := apiClient.GetConfig(cfg.AgentID)
if err != nil {
return fmt.Errorf("failed to get server config: %w", err)
}
// Check if config version is newer
if serverConfig.Version <= lastConfigVersion {
return nil // No update needed
}
log.Printf("📡 Server config update detected (version: %d)", serverConfig.Version)
changes := false
// Apply subsystem configuration from server
for subsystemName, subsystemConfig := range serverConfig.Subsystems {
if configMap, ok := subsystemConfig.(map[string]interface{}); ok {
enabled := false
intervalMinutes := 0
autoRun := false
if e, exists := configMap["enabled"]; exists {
if eVal, ok := e.(bool); ok {
enabled = eVal
}
}
if i, exists := configMap["interval_minutes"]; exists {
if iVal, ok := i.(float64); ok {
intervalMinutes = int(iVal)
}
}
if a, exists := configMap["auto_run"]; exists {
if aVal, ok := a.(bool); ok {
autoRun = aVal
}
}
// Only log changes if different from current state
currentEnabled := cfg.Subsystems.APT.Enabled // We'd need to check actual current state here
if enabled != currentEnabled {
log.Printf(" → %s: enabled=%v (changed)", subsystemName, enabled)
changes = true
}
if intervalMinutes > 0 && intervalMinutes != cfg.CheckInInterval {
log.Printf(" → %s: interval=%d minutes (changed)", subsystemName, intervalMinutes)
changes = true
// Note: For now, we use the check-in interval from server config
cfg.CheckInInterval = intervalMinutes
}
if autoRun {
log.Printf(" → %s: auto_run=%v (server-side scheduling)", subsystemName, autoRun)
}
}
}
if changes {
log.Printf("✅ Server configuration applied successfully")
} else {
log.Printf(" Server config received but no changes detected")
}
// Update last config version
lastConfigVersion = serverConfig.Version
return nil
}
func runAgent(cfg *config.Config) error {
log.Printf("🚩 RedFlag Agent v%s starting...\n", AgentVersion)
log.Printf("==================================================================")
@@ -670,6 +747,13 @@ func runAgent(cfg *config.Config) error {
}
}
// Sync configuration from server (non-blocking)
go func() {
if err := syncServerConfig(apiClient, cfg); err != nil {
log.Printf("Warning: Failed to sync server config: %v", err)
}
}()
commands := response.Commands
if len(commands) == 0 {
log.Printf("Check-in successful - no new commands")