Implement central logging function and debug mode switching

This commit is contained in:
Michael Reber
2025-01-31 18:25:31 +01:00
parent b88023dd8d
commit aea7afc1fd
4 changed files with 125 additions and 44 deletions

View File

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

View 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
}
}

View File

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

View File

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