2 Commits

Author SHA1 Message Date
d3m0k1d
3acd0b899c fix: linter run and gosec fix
Some checks failed
build / build (push) Failing after 1m52s
2026-02-22 19:45:59 +03:00
d3m0k1d
3ac1250bfc feat: add metrics support 2026-02-22 19:45:47 +03:00
5 changed files with 110 additions and 0 deletions

View File

@@ -60,6 +60,11 @@ var DaemonCmd = &cobra.Command{
log.Error("Failed to load config", "error", err) log.Error("Failed to load config", "error", err)
os.Exit(1) os.Exit(1)
} }
_, err = config.LoadMetricsConfig()
if err != nil {
log.Error("Failed to load metrics config", "error", err)
os.Exit(1)
}
var b blocker.BlockerEngine var b blocker.BlockerEngine
fw := cfg.Firewall.Name fw := cfg.Firewall.Name
b = blocker.GetBlocker(fw, cfg.Firewall.Config) b = blocker.GetBlocker(fw, cfg.Firewall.Config)

View File

@@ -10,8 +10,25 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/d3m0k1d/BanForge/internal/logger" "github.com/d3m0k1d/BanForge/internal/logger"
"github.com/d3m0k1d/BanForge/internal/metrics"
) )
func LoadMetricsConfig() (*Metrics, error) {
cfg := &Metrics{}
_, err := toml.DecodeFile("/etc/banforge/config.toml", cfg)
if err != nil {
return nil, fmt.Errorf("failed to decode config: %w", err)
}
if cfg.Enabled && cfg.Port > 0 && cfg.Port < 65535 {
go metrics.StartMetricsServer(cfg.Port)
} else if cfg.Enabled {
fmt.Println("Metrics enabled but port invalid, not starting server")
}
return cfg, nil
}
func LoadRuleConfig() ([]Rule, error) { func LoadRuleConfig() ([]Rule, error) {
log := logger.New(false) log := logger.New(false)
var cfg Rules var cfg Rules

View File

@@ -8,6 +8,10 @@ const Base_config = `
name = "" name = ""
config = "/etc/nftables.conf" config = "/etc/nftables.conf"
[metrics]
enabled = false
port = 2122
[[service]] [[service]]
name = "nginx" name = "nginx"
logging = "file" logging = "file"

View File

@@ -14,6 +14,7 @@ type Service struct {
type Config struct { type Config struct {
Firewall Firewall `toml:"firewall"` Firewall Firewall `toml:"firewall"`
Metrics Metrics `toml:"metrics"`
Service []Service `toml:"service"` Service []Service `toml:"service"`
} }
@@ -31,3 +32,8 @@ type Rule struct {
MaxRetry int `toml:"max_retry"` MaxRetry int `toml:"max_retry"`
BanTime string `toml:"ban_time"` BanTime string `toml:"ban_time"`
} }
type Metrics struct {
Enabled bool `toml:"enabled"`
Port int `toml:"port"`
}

View File

@@ -0,0 +1,78 @@
package metrics
import (
"fmt"
"log"
"net/http"
"sync"
"time"
)
var (
metricsMu sync.RWMutex
metrics = make(map[string]int64)
)
func IncBan(service string) {
metricsMu.Lock()
metrics["ban_count"]++
metrics[service+"_bans"]++
metricsMu.Unlock()
}
func IncUnban(service string) {
metricsMu.Lock()
metrics["unban_count"]++
metrics[service+"_unbans"]++
metricsMu.Unlock()
}
func IncRuleMatched(rule_name string) {
metricsMu.Lock()
metrics[rule_name+"_rule_matched"]++
metricsMu.Unlock()
}
func IncLogParsed() {
metricsMu.Lock()
metrics["log_parsed"]++
metricsMu.Unlock()
}
func MetricsHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
metricsMu.RLock()
snapshot := make(map[string]int64, len(metrics))
for k, v := range metrics {
snapshot[k] = v
}
metricsMu.RUnlock()
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
for name, value := range snapshot {
metricName := name + "_total"
_, _ = fmt.Fprintf(w, "# TYPE %s counter\n", metricName)
_, _ = fmt.Fprintf(w, "%s %d\n", metricName, value)
}
})
}
func StartMetricsServer(port int) {
mux := http.NewServeMux()
mux.Handle("/metrics", MetricsHandler())
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
log.Printf("Starting metrics server on %s", server.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("Metrics server error: %v", err)
}
}