mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-11 13:47:05 +02:00
Implement central logging function and debug mode switching
This commit is contained in:
@@ -1,23 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/swissmakers/fail2ban-ui/internal/config"
|
||||
"github.com/swissmakers/fail2ban-ui/pkg/web"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
settings := config.GetSettings()
|
||||
|
||||
// Load HTML templates from pkg/web/templates
|
||||
r.LoadHTMLGlob("pkg/web/templates/*")
|
||||
// Set Gin mode based on settings
|
||||
if settings.Debug {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
} else {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
// Register our routes (IndexHandler, /api/summary, /api/jails/:jail/unban/:ip)
|
||||
web.RegisterRoutes(r)
|
||||
router := gin.Default()
|
||||
router.LoadHTMLGlob("pkg/web/templates/*") // Load HTML templates from pkg/web/templates
|
||||
web.RegisterRoutes(router) // Register routes (IndexHandler, /api/summary, jail/unban/:ip) etc..
|
||||
|
||||
log.Println("Starting Fail2ban-UI server on :8080.")
|
||||
if err := r.Run(":8080"); err != nil {
|
||||
printWelcomeBanner()
|
||||
log.Println("--- Fail2Ban-UI started in", gin.Mode(), "mode ---")
|
||||
log.Println("Server listening on port :8080.")
|
||||
|
||||
if err := router.Run(":8080"); err != nil {
|
||||
log.Fatalf("Server crashed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// printWelcomeBanner prints a cool Tux banner with startup info
|
||||
func printWelcomeBanner() {
|
||||
greeting := getGreeting()
|
||||
const tuxBanner = `
|
||||
.--.
|
||||
|o_o | %s
|
||||
|:_/ |
|
||||
// \ \
|
||||
(| | )
|
||||
/'\_ _/'\
|
||||
\___)=(___/
|
||||
|
||||
Fail2Ban UI - A Swissmade Management Interface
|
||||
----------------------------------------------
|
||||
Developers: https://swissmakers.ch
|
||||
Mode: %s
|
||||
Listening on: http://0.0.0.0:8080
|
||||
----------------------------------------------
|
||||
|
||||
`
|
||||
fmt.Printf(tuxBanner, greeting, gin.Mode())
|
||||
}
|
||||
|
||||
// getGreeting returns a friendly greeting based on the time of day
|
||||
func getGreeting() string {
|
||||
hour := time.Now().Hour()
|
||||
switch {
|
||||
case hour < 12:
|
||||
return "Good morning!"
|
||||
case hour < 18:
|
||||
return "Good afternoon!"
|
||||
default:
|
||||
return "Good evening!"
|
||||
}
|
||||
}
|
||||
|
||||
37
internal/config/logging.go
Normal file
37
internal/config/logging.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Fail2ban UI - A Swiss made, management interface for Fail2ban.
|
||||
//
|
||||
// Copyright (C) 2025 Swissmakers GmbH (https://swissmakers.ch)
|
||||
//
|
||||
// Licensed under the GNU General Public License, Version 3 (GPL-3.0)
|
||||
// You may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// DebugLog prints debug messages only if debug mode is enabled.
|
||||
func DebugLog(format string, v ...interface{}) {
|
||||
// Avoid deadlocks by not calling GetSettings() inside DebugLog.
|
||||
debugEnabled := false
|
||||
debugEnabled = currentSettings.Debug
|
||||
if !debugEnabled {
|
||||
return
|
||||
}
|
||||
// Ensure correct usage of fmt.Printf-style formatting
|
||||
if len(v) > 0 {
|
||||
log.Printf(format, v...) // Uses format directives
|
||||
} else {
|
||||
log.Println(format) // Just prints the message
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -73,11 +72,12 @@ var (
|
||||
func init() {
|
||||
// Attempt to load existing file; if it doesn't exist, create with defaults.
|
||||
if err := loadSettings(); err != nil {
|
||||
fmt.Println("App settings not found, initializing new from jail.local (if exist):", err)
|
||||
fmt.Println("App settings not found, initializing from jail.local (if exist)")
|
||||
if err := initializeFromJailFile(); err != nil {
|
||||
fmt.Println("Error reading jail.local:", err)
|
||||
}
|
||||
setDefaults()
|
||||
fmt.Println("Initialized successfully.")
|
||||
|
||||
// save defaults to file
|
||||
if err := saveSettings(); err != nil {
|
||||
@@ -251,7 +251,7 @@ func ensureJailDConfig() error {
|
||||
// Check if the file already exists
|
||||
if _, err := os.Stat(jailDFile); err == nil {
|
||||
// File already exists, do nothing
|
||||
fmt.Println("Custom jail.d configuration already exists.")
|
||||
DebugLog("Custom jail.d configuration already exists.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ action_mwlg = %(action_)s
|
||||
return fmt.Errorf("failed to write jail.d config: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Created custom jail.d configuration at:", jailDFile)
|
||||
DebugLog("Created custom jail.d configuration at: %v", jailDFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -317,14 +317,14 @@ logpath = /dev/null
|
||||
return fmt.Errorf("failed to write action file: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Action file successfully written to %s\n", actionFile)
|
||||
DebugLog("Custom-action file successfully written to %s\n", actionFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadSettings reads fail2ban-ui-settings.json into currentSettings.
|
||||
func loadSettings() error {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("loadSettings called (settings.go)") // entry point
|
||||
DebugLog("----------------------------")
|
||||
DebugLog("loadSettings called (settings.go)") // entry point
|
||||
data, err := os.ReadFile(settingsFile)
|
||||
if os.IsNotExist(err) {
|
||||
return err // triggers setDefaults + save
|
||||
@@ -346,21 +346,18 @@ func loadSettings() error {
|
||||
|
||||
// saveSettings writes currentSettings to JSON
|
||||
func saveSettings() error {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("saveSettings called (settings.go)") // entry point
|
||||
DebugLog("----------------------------")
|
||||
DebugLog("saveSettings called (settings.go)") // entry point
|
||||
|
||||
b, err := json.MarshalIndent(currentSettings, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("Error marshalling settings:", err) // Debug
|
||||
DebugLog("Error marshalling settings: %v", err) // Debug
|
||||
return err
|
||||
}
|
||||
fmt.Println("Settings marshaled, writing to file...") // Log marshaling success
|
||||
//return os.WriteFile(settingsFile, b, 0644)
|
||||
DebugLog("Settings marshaled, writing to file...") // Log marshaling success
|
||||
err = os.WriteFile(settingsFile, b, 0644)
|
||||
if err != nil {
|
||||
log.Println("Error writing to file:", err) // Debug
|
||||
} else {
|
||||
log.Println("Settings saved successfully!") // Debug
|
||||
DebugLog("Error writing to file: %v", err) // Debug
|
||||
}
|
||||
// Update the Fail2ban action file
|
||||
return writeFail2banAction()
|
||||
@@ -396,7 +393,7 @@ func UpdateSettings(new AppSettings) (AppSettings, error) {
|
||||
settingsLock.Lock()
|
||||
defer settingsLock.Unlock()
|
||||
|
||||
fmt.Println("Locked settings for update") // Log lock acquisition
|
||||
DebugLog("--- Locked settings for update ---") // Log lock acquisition
|
||||
|
||||
old := currentSettings
|
||||
|
||||
@@ -421,7 +418,7 @@ func UpdateSettings(new AppSettings) (AppSettings, error) {
|
||||
}
|
||||
|
||||
currentSettings = new
|
||||
fmt.Println("New settings applied:", currentSettings) // Log settings applied
|
||||
DebugLog("New settings applied: %v", currentSettings) // Log settings applied
|
||||
|
||||
// persist to file
|
||||
if err := saveSettings(); err != nil {
|
||||
|
||||
@@ -81,8 +81,8 @@ func SummaryHandler(c *gin.Context) {
|
||||
|
||||
// UnbanIPHandler unbans a given IP in a specific jail.
|
||||
func UnbanIPHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("UnbanIPHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("UnbanIPHandler called (handlers.go)") // entry point
|
||||
jail := c.Param("jail")
|
||||
ip := c.Param("ip")
|
||||
|
||||
@@ -93,7 +93,7 @@ func UnbanIPHandler(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
fmt.Println(ip + " from jail " + jail + " unbanned successfully (handlers.go)")
|
||||
fmt.Println(ip + " from jail " + jail + " unbanned successfully.")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "IP unbanned successfully",
|
||||
})
|
||||
@@ -112,7 +112,7 @@ func BanNotificationHandler(c *gin.Context) {
|
||||
|
||||
// **DEBUGGING: Log Raw JSON Body**
|
||||
body, _ := io.ReadAll(c.Request.Body)
|
||||
log.Printf("📩 Incoming Ban Notification: %s\n", string(body))
|
||||
config.DebugLog("📩 Incoming Ban Notification: %s\n", string(body))
|
||||
|
||||
// Rebind body so Gin can parse it again (important!)
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||
@@ -246,8 +246,8 @@ func GetJailFilterConfigHandler(c *gin.Context) {
|
||||
|
||||
// SetJailFilterConfigHandler overwrites the current filter config with new content
|
||||
func SetJailFilterConfigHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("SetJailFilterConfigHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("SetJailFilterConfigHandler called (handlers.go)") // entry point
|
||||
jail := c.Param("jail")
|
||||
|
||||
// Parse JSON body (containing the new filter content)
|
||||
@@ -282,16 +282,16 @@ func SetJailFilterConfigHandler(c *gin.Context) {
|
||||
|
||||
// GetSettingsHandler returns the entire AppSettings struct as JSON
|
||||
func GetSettingsHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("GetSettingsHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("GetSettingsHandler called (handlers.go)") // entry point
|
||||
s := config.GetSettings()
|
||||
c.JSON(http.StatusOK, s)
|
||||
}
|
||||
|
||||
// UpdateSettingsHandler updates the AppSettings from a JSON body
|
||||
func UpdateSettingsHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("UpdateSettingsHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("UpdateSettingsHandler called (handlers.go)") // entry point
|
||||
var req config.AppSettings
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
fmt.Println("JSON binding error:", err) // Debug
|
||||
@@ -301,7 +301,7 @@ func UpdateSettingsHandler(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
fmt.Println("JSON binding successful, updating settings (handlers.go)")
|
||||
config.DebugLog("JSON binding successful, updating settings (handlers.go)")
|
||||
|
||||
newSettings, err := config.UpdateSettings(req)
|
||||
if err != nil {
|
||||
@@ -309,7 +309,7 @@ func UpdateSettingsHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
fmt.Println("Settings updated successfully (handlers.go)")
|
||||
config.DebugLog("Settings updated successfully (handlers.go)")
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Settings updated",
|
||||
@@ -320,8 +320,8 @@ func UpdateSettingsHandler(c *gin.Context) {
|
||||
// ListFiltersHandler returns a JSON array of filter names
|
||||
// found as *.conf in /etc/fail2ban/filter.d
|
||||
func ListFiltersHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("ListFiltersHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("ListFiltersHandler called (handlers.go)") // entry point
|
||||
dir := "/etc/fail2ban/filter.d"
|
||||
|
||||
files, err := os.ReadDir(dir)
|
||||
@@ -344,8 +344,8 @@ func ListFiltersHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func TestFilterHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("TestFilterHandler called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("TestFilterHandler called (handlers.go)") // entry point
|
||||
var req struct {
|
||||
FilterName string `json:"filterName"`
|
||||
LogLines []string `json:"logLines"`
|
||||
@@ -361,8 +361,8 @@ func TestFilterHandler(c *gin.Context) {
|
||||
|
||||
// ApplyFail2banSettings updates /etc/fail2ban/jail.local [DEFAULT] with our JSON
|
||||
func ApplyFail2banSettings(jailLocalPath string) error {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("ApplyFail2banSettings called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("ApplyFail2banSettings called (handlers.go)") // entry point
|
||||
s := config.GetSettings()
|
||||
|
||||
// open /etc/fail2ban/jail.local, parse or do a simplistic approach:
|
||||
@@ -387,8 +387,8 @@ func ApplyFail2banSettings(jailLocalPath string) error {
|
||||
|
||||
// ReloadFail2banHandler reloads the Fail2ban service
|
||||
func ReloadFail2banHandler(c *gin.Context) {
|
||||
fmt.Println("----------------------------")
|
||||
fmt.Println("ApplyFail2banSettings called (handlers.go)") // entry point
|
||||
config.DebugLog("----------------------------")
|
||||
config.DebugLog("ApplyFail2banSettings called (handlers.go)") // entry point
|
||||
|
||||
// First we write our new settings to /etc/fail2ban/jail.local
|
||||
// if err := fail2ban.ApplyFail2banSettings("/etc/fail2ban/jail.local"); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user