diff --git a/internal/fail2ban/jail_management.go b/internal/fail2ban/jail_management.go index e137caa..7d3acaa 100644 --- a/internal/fail2ban/jail_management.go +++ b/internal/fail2ban/jail_management.go @@ -191,41 +191,6 @@ func UpdateJailEnabledStates(updates map[string]bool) error { return nil } -// updateJailConfigFile updates a single jail configuration file with the new enabled states. -func updateJailConfigFile(path string, updates map[string]bool) error { - input, err := os.ReadFile(path) - if err != nil { - return err - } - lines := strings.Split(string(input), "\n") - var outputLines []string - var currentJail string - for _, line := range lines { - trimmed := strings.TrimSpace(line) - if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") { - currentJail = strings.Trim(trimmed, "[]") - outputLines = append(outputLines, line) - } else if strings.HasPrefix(trimmed, "enabled") { - if val, ok := updates[currentJail]; ok { - outputLines = append(outputLines, fmt.Sprintf("enabled = %t", val)) - // Remove the update from map to mark it as processed. - delete(updates, currentJail) - } else { - outputLines = append(outputLines, line) - } - } else { - outputLines = append(outputLines, line) - } - } - // For any jails in updates that did not have an "enabled" line, append it. - for jail, val := range updates { - outputLines = append(outputLines, fmt.Sprintf("[%s]", jail)) - outputLines = append(outputLines, fmt.Sprintf("enabled = %t", val)) - } - newContent := strings.Join(outputLines, "\n") - return os.WriteFile(path, []byte(newContent), 0644) -} - // MigrateJailsToJailD migrates all non-DEFAULT jails from jail.local to individual files in jail.d/. // Creates a backup of jail.local before migration. If a jail already exists in jail.d, jail.local takes precedence. func MigrateJailsToJailD() error { @@ -557,37 +522,6 @@ func TestLogpath(logpath string) ([]string, error) { return matches, nil } -// parseJailSection extracts the logpath from a jail configuration content. -func parseJailSection(content string, jailName string) (string, error) { - // This function can be used to extract specific settings from jail config - // For now, we'll use it to find logpath - scanner := bufio.NewScanner(strings.NewReader(content)) - var inTargetJail bool - var jailContent strings.Builder - - for scanner.Scan() { - line := scanner.Text() - trimmed := strings.TrimSpace(line) - - if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") { - currentJail := strings.Trim(trimmed, "[]") - if currentJail == jailName { - inTargetJail = true - jailContent.Reset() - jailContent.WriteString(line) - jailContent.WriteString("\n") - } else { - inTargetJail = false - } - } else if inTargetJail { - jailContent.WriteString(line) - jailContent.WriteString("\n") - } - } - - return jailContent.String(), scanner.Err() -} - // ExtractLogpathFromJailConfig extracts the logpath value from jail configuration content. func ExtractLogpathFromJailConfig(jailContent string) string { scanner := bufio.NewScanner(strings.NewReader(jailContent)) @@ -602,3 +536,29 @@ func ExtractLogpathFromJailConfig(jailContent string) string { } return "" } + +// ExtractFilterFromJailConfig extracts the filter name from jail configuration content. +// Handles formats like: filter = sshd, filter = sshd[mode=aggressive], etc. +// Returns the base filter name (without parameters in brackets). +func ExtractFilterFromJailConfig(jailContent string) string { + scanner := bufio.NewScanner(strings.NewReader(jailContent)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + // Skip comments + if strings.HasPrefix(line, "#") { + continue + } + if strings.HasPrefix(strings.ToLower(line), "filter") { + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + filterValue := strings.TrimSpace(parts[1]) + // Extract base filter name (before [ if present) + if idx := strings.Index(filterValue, "["); idx >= 0 { + filterValue = filterValue[:idx] + } + return strings.TrimSpace(filterValue) + } + } + } + return "" +} diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go index 6d13173..0fcf4ba 100644 --- a/pkg/web/handlers.go +++ b/pkg/web/handlers.go @@ -670,7 +670,7 @@ func GetJailFilterConfigHandler(c *gin.Context) { config.DebugLog("GetJailFilterConfigHandler called (handlers.go)") // entry point jail := c.Param("jail") config.DebugLog("Jail name: %s", jail) - + conn, err := resolveConnector(c) if err != nil { config.DebugLog("Failed to resolve connector: %v", err) @@ -678,28 +678,67 @@ func GetJailFilterConfigHandler(c *gin.Context) { return } config.DebugLog("Connector resolved: %s", conn.Server().Name) - + + var filterCfg string + var jailCfg string + var jailCfgLoaded bool + var filterErr error + + // First, try to load filter config using jail name config.DebugLog("Loading filter config for jail: %s", jail) - filterCfg, err := conn.GetFilterConfig(c.Request.Context(), jail) - if err != nil { - config.DebugLog("Failed to load filter config: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load filter config: " + err.Error()}) - return + filterCfg, filterErr = conn.GetFilterConfig(c.Request.Context(), jail) + if filterErr != nil { + config.DebugLog("Failed to load filter config with jail name, trying to find filter from jail config: %v", filterErr) + + // Load jail config first to check for custom filter directive + var jailErr error + jailCfg, jailErr = conn.GetJailConfig(c.Request.Context(), jail) + if jailErr != nil { + config.DebugLog("Failed to load jail config: %v", jailErr) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load filter config: " + filterErr.Error() + ". Also failed to load jail config: " + jailErr.Error()}) + return + } + jailCfgLoaded = true + config.DebugLog("Jail config loaded, length: %d", len(jailCfg)) + + // Extract filter name from jail config + filterName := fail2ban.ExtractFilterFromJailConfig(jailCfg) + if filterName == "" { + config.DebugLog("No filter directive found in jail config") + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load filter config: " + filterErr.Error() + ". No filter directive found in jail config."}) + return + } + + config.DebugLog("Found filter directive in jail config: %s, trying to load that filter", filterName) + // Try loading the filter specified in jail config + filterCfg, filterErr = conn.GetFilterConfig(c.Request.Context(), filterName) + if filterErr != nil { + config.DebugLog("Failed to load filter config for %s: %v", filterName, filterErr) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": fmt.Sprintf("Failed to load filter config. Tried '%s' (jail name) and '%s' (from jail config), both failed. Last error: %v", jail, filterName, filterErr), + }) + return + } + config.DebugLog("Successfully loaded filter config for %s (from jail config directive)", filterName) } config.DebugLog("Filter config loaded, length: %d", len(filterCfg)) - - config.DebugLog("Loading jail config for jail: %s", jail) - jailCfg, err := conn.GetJailConfig(c.Request.Context(), jail) - if err != nil { - config.DebugLog("Failed to load jail config: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load jail config: " + err.Error()}) - return + + // Load jail config if not already loaded + if !jailCfgLoaded { + config.DebugLog("Loading jail config for jail: %s", jail) + var jailErr error + jailCfg, jailErr = conn.GetJailConfig(c.Request.Context(), jail) + if jailErr != nil { + config.DebugLog("Failed to load jail config: %v", jailErr) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load jail config: " + jailErr.Error()}) + return + } + config.DebugLog("Jail config loaded, length: %d", len(jailCfg)) } - config.DebugLog("Jail config loaded, length: %d", len(jailCfg)) - + c.JSON(http.StatusOK, gin.H{ - "jail": jail, - "filter": filterCfg, + "jail": jail, + "filter": filterCfg, "jailConfig": jailCfg, }) } @@ -717,7 +756,7 @@ func SetJailFilterConfigHandler(c *gin.Context) { config.DebugLog("SetJailFilterConfigHandler called (handlers.go)") // entry point jail := c.Param("jail") config.DebugLog("Jail name: %s", jail) - + conn, err := resolveConnector(c) if err != nil { config.DebugLog("Failed to resolve connector: %v", err)