Fix case if a jail have a custom filter set, that isn't the same name as the jail is

This commit is contained in:
2025-12-03 21:02:48 +01:00
parent cd7a814cda
commit 366d0965e3
2 changed files with 84 additions and 85 deletions

View File

@@ -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 ""
}

View File

@@ -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)