diff --git a/src/export/build.go b/src/export/build.go new file mode 100644 index 0000000..a874cc2 --- /dev/null +++ b/src/export/build.go @@ -0,0 +1,25 @@ +package export + +import ( + "fail2ban-prometheus-exporter/cfg" + fail2banDb "fail2ban-prometheus-exporter/db" + "log" +) + +func NewExporter(appSettings *cfg.AppSettings, exporterVersion string) *Exporter { + exporter := &Exporter{ + exporterVersion: exporterVersion, + lastError: nil, + dbErrorCount: 0, + socketConnectionErrorCount: 0, + socketRequestErrorCount: 0, + } + if appSettings.Fail2BanDbPath != "" { + log.Print("database-based metrics have been deprecated and will be removed in a future release") + exporter.db = fail2banDb.MustConnectToDb(appSettings.Fail2BanDbPath) + } + if appSettings.Fail2BanSocketPath != "" { + exporter.socketPath = appSettings.Fail2BanSocketPath + } + return exporter +} diff --git a/src/export/database.go b/src/export/database.go new file mode 100644 index 0000000..f030360 --- /dev/null +++ b/src/export/database.go @@ -0,0 +1,102 @@ +package export + +import ( + "github.com/prometheus/client_golang/prometheus" + "log" +) + +const ( + deprecatedNamespace = "fail2ban" +) + +var ( + deprecatedMetricUp = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "up"), + "(Deprecated) Was the last fail2ban query successful.", + nil, nil, + ) + deprecatedMetricBannedIpsPerJail = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "banned_ips"), + "(Deprecated) Number of banned IPs stored in the database (per jail).", + []string{"jail"}, nil, + ) + deprecatedMetricBadIpsPerJail = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "bad_ips"), + "(Deprecated) Number of bad IPs stored in the database (per jail).", + []string{"jail"}, nil, + ) + deprecatedMetricEnabledJails = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "enabled_jails"), + "(Deprecated) Enabled jails.", + []string{"jail"}, nil, + ) + deprecatedMetricErrorCount = prometheus.NewDesc( + prometheus.BuildFQName(deprecatedNamespace, "", "errors"), + "(Deprecated) Number of errors found since startup.", + []string{"type"}, nil, + ) +) + +func (e *Exporter) collectDeprecatedUpMetric(ch chan<- prometheus.Metric) { + var upMetricValue float64 = 1 + if e.lastError != nil { + upMetricValue = 0 + } + ch <- prometheus.MustNewConstMetric( + deprecatedMetricUp, prometheus.GaugeValue, upMetricValue, + ) +} + +func (e *Exporter) collectDeprecatedErrorCountMetric(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", + ) +} + +func (e *Exporter) collectDeprecatedBadIpsPerJailMetrics(ch chan<- prometheus.Metric) { + jailNameToCountMap, err := e.db.CountBadIpsPerJail() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToCountMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricBadIpsPerJail, prometheus.GaugeValue, float64(count), jailName, + ) + } +} + +func (e *Exporter) collectDeprecatedBannedIpsPerJailMetrics(ch chan<- prometheus.Metric) { + jailNameToCountMap, err := e.db.CountBannedIpsPerJail() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToCountMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricBannedIpsPerJail, prometheus.GaugeValue, float64(count), jailName, + ) + } +} + +func (e *Exporter) collectDeprecatedEnabledJailMetrics(ch chan<- prometheus.Metric) { + jailNameToEnabledMap, err := e.db.JailNameToEnabledValue() + e.lastError = err + + if err != nil { + e.dbErrorCount++ + log.Print(err) + } + + for jailName, count := range jailNameToEnabledMap { + ch <- prometheus.MustNewConstMetric( + deprecatedMetricEnabledJails, prometheus.GaugeValue, float64(count), jailName, + ) + } +} diff --git a/src/export/exporter.go b/src/export/exporter.go new file mode 100644 index 0000000..76ebf18 --- /dev/null +++ b/src/export/exporter.go @@ -0,0 +1,64 @@ +package export + +import ( + fail2banDb "fail2ban-prometheus-exporter/db" + "fail2ban-prometheus-exporter/socket" + "github.com/prometheus/client_golang/prometheus" + "log" +) + +type Exporter struct { + db *fail2banDb.Fail2BanDB + socketPath string + exporterVersion string + lastError error + dbErrorCount int + socketConnectionErrorCount int + socketRequestErrorCount int +} + +func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { + if e.db != nil { + ch <- deprecatedMetricUp + ch <- deprecatedMetricBadIpsPerJail + ch <- deprecatedMetricBannedIpsPerJail + ch <- deprecatedMetricEnabledJails + ch <- deprecatedMetricErrorCount + } + if e.socketPath != "" { + ch <- metricServerUp + ch <- metricJailCount + ch <- metricJailFailedCurrent + ch <- metricJailFailedTotal + ch <- metricJailBannedCurrent + ch <- metricJailBannedTotal + } + ch <- metricErrorCount +} + +func (e *Exporter) Collect(ch chan<- prometheus.Metric) { + if e.db != nil { + e.collectDeprecatedBadIpsPerJailMetrics(ch) + e.collectDeprecatedBannedIpsPerJailMetrics(ch) + e.collectDeprecatedEnabledJailMetrics(ch) + e.collectDeprecatedUpMetric(ch) + e.collectDeprecatedErrorCountMetric(ch) + } + if e.socketPath != "" { + s, err := socket.ConnectToSocket(e.socketPath) + if err != nil { + log.Printf("error opening socket: %v", err) + e.socketConnectionErrorCount++ + } else { + defer s.Close() + } + e.collectServerUpMetric(ch, s) + if err == nil && s != nil { + e.collectJailMetrics(ch, s) + } + e.collectVersionMetric(ch, s) + } else { + e.collectVersionMetric(ch, nil) + } + e.collectErrorCountMetric(ch) +} diff --git a/src/export/socket.go b/src/export/socket.go new file mode 100644 index 0000000..ce2a258 --- /dev/null +++ b/src/export/socket.go @@ -0,0 +1,140 @@ +package export + +import ( + "fail2ban-prometheus-exporter/socket" + "github.com/prometheus/client_golang/prometheus" + "log" +) + +const ( + namespace = "f2b" +) + +var ( + metricErrorCount = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "errors"), + "Number of errors found since startup", + []string{"type"}, nil, + ) + metricServerUp = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "up"), + "Check if the fail2ban server is up", + nil, nil, + ) + metricJailCount = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_count"), + "Number of defined jails", + nil, nil, + ) + metricJailFailedCurrent = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_failed_current"), + "Number of current failures on this jail's filter", + []string{"jail"}, nil, + ) + metricJailFailedTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_failed_total"), + "Number of total failures on this jail's filter", + []string{"jail"}, nil, + ) + metricJailBannedCurrent = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_banned_current"), + "Number of IPs currently banned in this jail", + []string{"jail"}, nil, + ) + metricJailBannedTotal = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "jail_banned_total"), + "Total number of IPs banned by this jail (includes expired bans)", + []string{"jail"}, nil, + ) + metricVersionInfo = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "version"), + "Version of the exporter and fail2ban server", + []string{"exporter", "fail2ban"}, nil, + ) +) + +func (e *Exporter) collectErrorCountMetric(ch chan<- prometheus.Metric) { + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", + ) + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.socketConnectionErrorCount), "socket_conn", + ) + ch <- prometheus.MustNewConstMetric( + metricErrorCount, prometheus.CounterValue, float64(e.socketRequestErrorCount), "socket_req", + ) +} + +func (e *Exporter) collectServerUpMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + var serverUp float64 = 0 + if s != nil { + pingSuccess, err := s.Ping() + if err != nil { + e.socketRequestErrorCount++ + log.Print(err) + } + if err == nil && pingSuccess { + serverUp = 1 + } + } + ch <- prometheus.MustNewConstMetric( + metricServerUp, prometheus.GaugeValue, serverUp, + ) +} + +func (e *Exporter) collectJailMetrics(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + jails, err := s.GetJails() + var count float64 = 0 + if err != nil { + e.socketRequestErrorCount++ + log.Print(err) + } + if err == nil { + count = float64(len(jails)) + } + ch <- prometheus.MustNewConstMetric( + metricJailCount, prometheus.GaugeValue, count, + ) + + for i := range jails { + e.collectJailStatsMetric(ch, s, jails[i]) + } +} + +func (e *Exporter) collectJailStatsMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket, jail string) { + stats, err := s.GetJailStats(jail) + if err != nil { + e.socketRequestErrorCount++ + log.Printf("failed to get stats for jail %s: %v", jail, err) + return + } + + ch <- prometheus.MustNewConstMetric( + metricJailFailedCurrent, prometheus.GaugeValue, float64(stats.FailedCurrent), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailFailedTotal, prometheus.GaugeValue, float64(stats.FailedTotal), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailBannedCurrent, prometheus.GaugeValue, float64(stats.BannedCurrent), jail, + ) + ch <- prometheus.MustNewConstMetric( + metricJailBannedTotal, prometheus.GaugeValue, float64(stats.BannedTotal), jail, + ) +} + +func (e *Exporter) collectVersionMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { + var err error + var fail2banVersion = "" + if s != nil { + fail2banVersion, err = s.GetServerVersion() + if err != nil { + e.socketRequestErrorCount++ + log.Printf("failed to get fail2ban server version: %v", err) + } + } + + ch <- prometheus.MustNewConstMetric( + metricVersionInfo, prometheus.GaugeValue, float64(1), e.exporterVersion, fail2banVersion, + ) +} diff --git a/src/exporter.go b/src/exporter.go index 1cf422f..c30dcce 100644 --- a/src/exporter.go +++ b/src/exporter.go @@ -2,8 +2,7 @@ package main import ( "fail2ban-prometheus-exporter/cfg" - fail2banDb "fail2ban-prometheus-exporter/db" - "fail2ban-prometheus-exporter/socket" + "fail2ban-prometheus-exporter/export" "fmt" "log" "net/http" @@ -13,290 +12,13 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -const ( - deprecatedNamespace = "fail2ban" - namespace = "f2b" -) - var ( version = "dev" commit = "none" date = "unknown" builtBy = "unknown" - - deprecatedMetricUp = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "up"), - "(Deprecated) Was the last fail2ban query successful.", - nil, nil, - ) - deprecatedMetricBannedIpsPerJail = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "banned_ips"), - "(Deprecated) Number of banned IPs stored in the database (per jail).", - []string{"jail"}, nil, - ) - deprecatedMetricBadIpsPerJail = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "bad_ips"), - "(Deprecated) Number of bad IPs stored in the database (per jail).", - []string{"jail"}, nil, - ) - deprecatedMetricEnabledJails = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "enabled_jails"), - "(Deprecated) Enabled jails.", - []string{"jail"}, nil, - ) - deprecatedMetricErrorCount = prometheus.NewDesc( - prometheus.BuildFQName(deprecatedNamespace, "", "errors"), - "(Deprecated) Number of errors found since startup.", - []string{"type"}, nil, - ) - - metricErrorCount = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "errors"), - "Number of errors found since startup", - []string{"type"}, nil, - ) - metricServerUp = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "up"), - "Check if the fail2ban server is up", - nil, nil, - ) - metricJailCount = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_count"), - "Number of defined jails", - nil, nil, - ) - metricJailFailedCurrent = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_failed_current"), - "Number of current failures on this jail's filter", - []string{"jail"}, nil, - ) - metricJailFailedTotal = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_failed_total"), - "Number of total failures on this jail's filter", - []string{"jail"}, nil, - ) - metricJailBannedCurrent = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_banned_current"), - "Number of IPs currently banned in this jail", - []string{"jail"}, nil, - ) - metricJailBannedTotal = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "jail_banned_total"), - "Total number of IPs banned by this jail (includes expired bans)", - []string{"jail"}, nil, - ) - metricVersionInfo = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "version"), - "Version of the exporter and fail2ban server", - []string{"exporter", "fail2ban"}, nil, - ) ) -type Exporter struct { - db *fail2banDb.Fail2BanDB - socketPath string - lastError error - dbErrorCount int - socketConnectionErrorCount int - socketRequestErrorCount int -} - -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - if e.db != nil { - ch <- deprecatedMetricUp - ch <- deprecatedMetricBadIpsPerJail - ch <- deprecatedMetricBannedIpsPerJail - ch <- deprecatedMetricEnabledJails - ch <- deprecatedMetricErrorCount - } - if e.socketPath != "" { - ch <- metricServerUp - ch <- metricJailCount - ch <- metricJailFailedCurrent - ch <- metricJailFailedTotal - ch <- metricJailBannedCurrent - ch <- metricJailBannedTotal - } - ch <- metricErrorCount -} - -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - if e.db != nil { - e.collectDeprecatedBadIpsPerJailMetrics(ch) - e.collectDeprecatedBannedIpsPerJailMetrics(ch) - e.collectDeprecatedEnabledJailMetrics(ch) - e.collectDeprecatedUpMetric(ch) - e.collectDeprecatedErrorCountMetric(ch) - } - if e.socketPath != "" { - s, err := socket.ConnectToSocket(e.socketPath) - if err != nil { - log.Printf("error opening socket: %v", err) - e.socketConnectionErrorCount++ - } else { - defer s.Close() - } - e.collectServerUpMetric(ch, s) - if err == nil && s != nil { - e.collectJailMetrics(ch, s) - } - e.collectVersionMetric(ch, s) - } else { - e.collectVersionMetric(ch, nil) - } - e.collectErrorCountMetric(ch) -} - -func (e *Exporter) collectDeprecatedUpMetric(ch chan<- prometheus.Metric) { - var upMetricValue float64 = 1 - if e.lastError != nil { - upMetricValue = 0 - } - ch <- prometheus.MustNewConstMetric( - deprecatedMetricUp, prometheus.GaugeValue, upMetricValue, - ) -} - -func (e *Exporter) collectDeprecatedErrorCountMetric(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", - ) -} - -func (e *Exporter) collectDeprecatedBadIpsPerJailMetrics(ch chan<- prometheus.Metric) { - jailNameToCountMap, err := e.db.CountBadIpsPerJail() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToCountMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricBadIpsPerJail, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectDeprecatedBannedIpsPerJailMetrics(ch chan<- prometheus.Metric) { - jailNameToCountMap, err := e.db.CountBannedIpsPerJail() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToCountMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricBannedIpsPerJail, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectDeprecatedEnabledJailMetrics(ch chan<- prometheus.Metric) { - jailNameToEnabledMap, err := e.db.JailNameToEnabledValue() - e.lastError = err - - if err != nil { - e.dbErrorCount++ - log.Print(err) - } - - for jailName, count := range jailNameToEnabledMap { - ch <- prometheus.MustNewConstMetric( - deprecatedMetricEnabledJails, prometheus.GaugeValue, float64(count), jailName, - ) - } -} - -func (e *Exporter) collectErrorCountMetric(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.dbErrorCount), "db", - ) - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.socketConnectionErrorCount), "socket_conn", - ) - ch <- prometheus.MustNewConstMetric( - metricErrorCount, prometheus.CounterValue, float64(e.socketRequestErrorCount), "socket_req", - ) -} - -func (e *Exporter) collectServerUpMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - var serverUp float64 = 0 - if s != nil { - pingSuccess, err := s.Ping() - if err != nil { - e.socketRequestErrorCount++ - log.Print(err) - } - if err == nil && pingSuccess { - serverUp = 1 - } - } - ch <- prometheus.MustNewConstMetric( - metricServerUp, prometheus.GaugeValue, serverUp, - ) -} - -func (e *Exporter) collectJailMetrics(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - jails, err := s.GetJails() - var count float64 = 0 - if err != nil { - e.socketRequestErrorCount++ - log.Print(err) - } - if err == nil { - count = float64(len(jails)) - } - ch <- prometheus.MustNewConstMetric( - metricJailCount, prometheus.GaugeValue, count, - ) - - for i := range jails { - e.collectJailStatsMetric(ch, s, jails[i]) - } -} - -func (e *Exporter) collectJailStatsMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket, jail string) { - stats, err := s.GetJailStats(jail) - if err != nil { - e.socketRequestErrorCount++ - log.Printf("failed to get stats for jail %s: %v", jail, err) - return - } - - ch <- prometheus.MustNewConstMetric( - metricJailFailedCurrent, prometheus.GaugeValue, float64(stats.FailedCurrent), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailFailedTotal, prometheus.GaugeValue, float64(stats.FailedTotal), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailBannedCurrent, prometheus.GaugeValue, float64(stats.BannedCurrent), jail, - ) - ch <- prometheus.MustNewConstMetric( - metricJailBannedTotal, prometheus.GaugeValue, float64(stats.BannedTotal), jail, - ) -} - -func (e *Exporter) collectVersionMetric(ch chan<- prometheus.Metric, s *socket.Fail2BanSocket) { - var err error - var fail2banVersion = "" - if s != nil { - fail2banVersion, err = s.GetServerVersion() - if err != nil { - e.socketRequestErrorCount++ - log.Printf("failed to get fail2ban server version: %v", err) - } - } - - ch <- prometheus.MustNewConstMetric( - metricVersionInfo, prometheus.GaugeValue, float64(1), version, fail2banVersion, - ) -} - func printAppVersion() { fmt.Println(version) fmt.Printf(" build date: %s\r\n commit hash: %s\r\n built by: %s\r\n", date, commit, builtBy) @@ -309,14 +31,7 @@ func main() { } else { log.Print("starting fail2ban exporter") - exporter := &Exporter{} - if appSettings.Fail2BanDbPath != "" { - log.Print("database-based metrics have been deprecated and will be removed in a future release") - exporter.db = fail2banDb.MustConnectToDb(appSettings.Fail2BanDbPath) - } - if appSettings.Fail2BanSocketPath != "" { - exporter.socketPath = appSettings.Fail2BanSocketPath - } + exporter := export.NewExporter(appSettings, version) prometheus.MustRegister(exporter) http.Handle("/metrics", promhttp.Handler())