Implement backend logic and routes for multi-line pharsing also enhanced key-pharsing

This commit is contained in:
2026-01-21 18:45:23 +01:00
parent 9d98e43446
commit ca31e02e29
2 changed files with 160 additions and 20 deletions

View File

@@ -1088,19 +1088,80 @@ func TestLogpathWithResolution(logpath string) (originalPath, resolvedPath strin
return originalPath, resolvedPath, files, nil return originalPath, resolvedPath, files, nil
} }
// ExtractLogpathFromJailConfig extracts the logpath value from jail configuration content. // ExtractLogpathFromJailConfig extracts the logpath value(s) from jail configuration content.
// Supports multiple logpaths in a single line (space-separated) or multiple lines.
// Fail2ban supports both formats:
//
// logpath = /var/log/file1.log /var/log/file2.log
// logpath = /var/log/file1.log
// /var/log/file2.log
//
// Returns all logpaths joined by newlines.
func ExtractLogpathFromJailConfig(jailContent string) string { func ExtractLogpathFromJailConfig(jailContent string) string {
var logpaths []string
scanner := bufio.NewScanner(strings.NewReader(jailContent)) scanner := bufio.NewScanner(strings.NewReader(jailContent))
inLogpathLine := false
currentLogpath := ""
for scanner.Scan() { for scanner.Scan() {
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(scanner.Text())
// Skip comments
if strings.HasPrefix(line, "#") {
if inLogpathLine && currentLogpath != "" {
// End of logpath block at comment, process current logpath
paths := strings.Fields(currentLogpath)
logpaths = append(logpaths, paths...)
currentLogpath = ""
inLogpathLine = false
}
continue
}
// Check if this line starts with logpath =
if strings.HasPrefix(strings.ToLower(line), "logpath") { if strings.HasPrefix(strings.ToLower(line), "logpath") {
parts := strings.SplitN(line, "=", 2) parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 { if len(parts) == 2 {
return strings.TrimSpace(parts[1]) logpathValue := strings.TrimSpace(parts[1])
if logpathValue != "" {
currentLogpath = logpathValue
inLogpathLine = true
} }
} }
} else if inLogpathLine {
// Continuation line (indented or starting with space)
// Fail2ban allows continuation lines for logpath
if line != "" && !strings.Contains(line, "=") {
// This is a continuation line, append to current logpath
currentLogpath += " " + line
} else {
// End of logpath block, process current logpath
if currentLogpath != "" {
// Split by spaces to handle multiple logpaths in one line
paths := strings.Fields(currentLogpath)
logpaths = append(logpaths, paths...)
currentLogpath = ""
} }
return "" inLogpathLine = false
}
} else if inLogpathLine && line == "" {
// Empty line might end the logpath block
if currentLogpath != "" {
paths := strings.Fields(currentLogpath)
logpaths = append(logpaths, paths...)
currentLogpath = ""
}
inLogpathLine = false
}
}
// Process any remaining logpath
if currentLogpath != "" {
paths := strings.Fields(currentLogpath)
logpaths = append(logpaths, paths...)
}
// Join multiple logpaths with newlines
return strings.Join(logpaths, "\n")
} }
// ExtractFilterFromJailConfig extracts the filter name from jail configuration content. // ExtractFilterFromJailConfig extracts the filter name from jail configuration content.
@@ -1183,7 +1244,6 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
"bantime": fmt.Sprintf("bantime = %s", settings.Bantime), "bantime": fmt.Sprintf("bantime = %s", settings.Bantime),
"findtime": fmt.Sprintf("findtime = %s", settings.Findtime), "findtime": fmt.Sprintf("findtime = %s", settings.Findtime),
"maxretry": fmt.Sprintf("maxretry = %d", settings.Maxretry), "maxretry": fmt.Sprintf("maxretry = %d", settings.Maxretry),
"destemail": fmt.Sprintf("destemail = %s", settings.Destemail),
"banaction": fmt.Sprintf("banaction = %s", banaction), "banaction": fmt.Sprintf("banaction = %s", banaction),
"banaction_allports": fmt.Sprintf("banaction_allports = %s", banactionAllports), "banaction_allports": fmt.Sprintf("banaction_allports = %s", banactionAllports),
} }
@@ -1197,7 +1257,7 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
var newLines []string var newLines []string
newLines = append(newLines, strings.Split(strings.TrimRight(config.JailLocalBanner(), "\n"), "\n")...) newLines = append(newLines, strings.Split(strings.TrimRight(config.JailLocalBanner(), "\n"), "\n")...)
newLines = append(newLines, "[DEFAULT]") newLines = append(newLines, "[DEFAULT]")
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "destemail", "banaction", "banaction_allports"} { for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
newLines = append(newLines, keysToUpdate[key]) newLines = append(newLines, keysToUpdate[key])
} }
newLines = append(newLines, "") newLines = append(newLines, "")
@@ -1270,14 +1330,14 @@ func UpdateDefaultSettingsLocal(settings config.AppSettings) error {
// If DEFAULT section wasn't found, create it at the beginning // If DEFAULT section wasn't found, create it at the beginning
if !defaultSectionFound { if !defaultSectionFound {
defaultLines := []string{"[DEFAULT]"} defaultLines := []string{"[DEFAULT]"}
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "destemail"} { for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
defaultLines = append(defaultLines, keysToUpdate[key]) defaultLines = append(defaultLines, keysToUpdate[key])
} }
defaultLines = append(defaultLines, "") defaultLines = append(defaultLines, "")
outputLines = append(defaultLines, outputLines...) outputLines = append(defaultLines, outputLines...)
} else { } else {
// Add any missing keys to the DEFAULT section // Add any missing keys to the DEFAULT section
for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "destemail", "banaction", "banaction_allports"} { for _, key := range []string{"enabled", "bantime.increment", "ignoreip", "bantime", "findtime", "maxretry", "banaction", "banaction_allports"} {
if !keysUpdated[key] { if !keysUpdated[key] {
// Find the DEFAULT section and insert after it // Find the DEFAULT section and insert after it
for i, line := range outputLines { for i, line := range outputLines {

View File

@@ -678,8 +678,13 @@ func ListSSHKeysHandler(c *gin.Context) {
continue continue
} }
name := entry.Name() name := entry.Name()
if strings.HasPrefix(name, "id_") || strings.HasSuffix(name, ".pem") || strings.HasSuffix(name, ".key") { // Only include private keys, not public keys (.pub files)
keys = append(keys, filepath.Join(dir, name)) // SSH requires the private key file, not the public key
if (strings.HasPrefix(name, "id_") && !strings.HasSuffix(name, ".pub")) ||
strings.HasSuffix(name, ".pem") ||
(strings.HasSuffix(name, ".key") && !strings.HasSuffix(name, ".pub")) {
keyPath := filepath.Join(dir, name)
keys = append(keys, keyPath)
} }
} }
if len(keys) == 0 { if len(keys) == 0 {
@@ -1394,17 +1399,93 @@ func TestLogpathHandler(c *gin.Context) {
return return
} }
// Test the logpath with variable resolution // Get server type to determine test strategy
originalPath, resolvedPath, files, err := conn.TestLogpathWithResolution(c.Request.Context(), originalLogpath) server := conn.Server()
isLocalServer := server.Type == "local"
// Split logpath by newlines and spaces (Fail2ban supports multiple logpaths separated by spaces or newlines)
// First split by newlines, then split each line by spaces
var logpaths []string
for _, line := range strings.Split(originalLogpath, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
// Split by spaces to handle multiple logpaths in one line
paths := strings.Fields(line)
logpaths = append(logpaths, paths...)
}
var allResults []map[string]interface{}
for _, logpathLine := range logpaths {
logpathLine = strings.TrimSpace(logpathLine)
if logpathLine == "" {
continue
}
if isLocalServer {
// For local servers: only test in fail2ban-ui container (container can only see mounted paths)
// Resolve variables first
resolvedPath, err := fail2ban.ResolveLogpathVariables(logpathLine)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to test logpath: " + err.Error()}) allResults = append(allResults, map[string]interface{}{
return "logpath": logpathLine,
"resolved_path": "",
"found": false,
"files": []string{},
"error": err.Error(),
})
continue
}
if resolvedPath == "" {
resolvedPath = logpathLine
}
// Test in fail2ban-ui container
files, localErr := fail2ban.TestLogpath(resolvedPath)
allResults = append(allResults, map[string]interface{}{
"logpath": logpathLine,
"resolved_path": resolvedPath,
"found": len(files) > 0,
"files": files,
"error": func() string {
if localErr != nil {
return localErr.Error()
}
return ""
}(),
})
} else {
// For SSH/Agent servers: test on remote server (via connector)
_, resolvedPath, filesOnRemote, err := conn.TestLogpathWithResolution(c.Request.Context(), logpathLine)
if err != nil {
allResults = append(allResults, map[string]interface{}{
"logpath": logpathLine,
"resolved_path": resolvedPath,
"found": false,
"files": []string{},
"error": err.Error(),
})
continue
}
allResults = append(allResults, map[string]interface{}{
"logpath": logpathLine,
"resolved_path": resolvedPath,
"found": len(filesOnRemote) > 0,
"files": filesOnRemote,
"error": "",
})
}
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"original_logpath": originalPath, "original_logpath": originalLogpath,
"resolved_logpath": resolvedPath, "is_local_server": isLocalServer,
"files": files, "results": allResults,
}) })
} }
@@ -1916,7 +1997,6 @@ func UpdateSettingsHandler(c *gin.Context) {
oldSettings.Bantime != newSettings.Bantime || oldSettings.Bantime != newSettings.Bantime ||
oldSettings.Findtime != newSettings.Findtime || oldSettings.Findtime != newSettings.Findtime ||
oldSettings.Maxretry != newSettings.Maxretry || oldSettings.Maxretry != newSettings.Maxretry ||
oldSettings.Destemail != newSettings.Destemail ||
oldSettings.Banaction != newSettings.Banaction || oldSettings.Banaction != newSettings.Banaction ||
oldSettings.BanactionAllports != newSettings.BanactionAllports oldSettings.BanactionAllports != newSettings.BanactionAllports
@@ -2142,8 +2222,8 @@ func ApplyFail2banSettings(jailLocalPath string) error {
fmt.Sprintf("bantime = %s", s.Bantime), fmt.Sprintf("bantime = %s", s.Bantime),
fmt.Sprintf("findtime = %s", s.Findtime), fmt.Sprintf("findtime = %s", s.Findtime),
fmt.Sprintf("maxretry = %d", s.Maxretry), fmt.Sprintf("maxretry = %d", s.Maxretry),
fmt.Sprintf("destemail = %s", s.Destemail), fmt.Sprintf("banaction = %s", s.Banaction),
//fmt.Sprintf("sender = %s", s.Sender), fmt.Sprintf("banaction_allports = %s", s.BanactionAllports),
"", "",
} }
content := strings.Join(newLines, "\n") content := strings.Join(newLines, "\n")