mirror of
https://github.com/aykhans/my-self-host-services.git
synced 2026-05-29 15:35:59 +00:00
Compare commits
7 Commits
ed68045605
...
14e6115618
| Author | SHA1 | Date | |
|---|---|---|---|
| 14e6115618 | |||
| 59a62a3cc7 | |||
| cb4d12822d | |||
| 1581be1722 | |||
| 2e132075b3 | |||
| 4ebbe30b4a | |||
| 73829dd5a4 |
@@ -10,3 +10,12 @@ CROWDSEC_BOUNCER_KEY_FW=
|
|||||||
# Leave blank to run without Console (no community blocklist subscriptions).
|
# Leave blank to run without Console (no community blocklist subscriptions).
|
||||||
CROWDSEC_ENROLL_KEY=
|
CROWDSEC_ENROLL_KEY=
|
||||||
CROWDSEC_ENROLL_INSTANCE_NAME=
|
CROWDSEC_ENROLL_INSTANCE_NAME=
|
||||||
|
|
||||||
|
############# MaxMind GeoIP #############
|
||||||
|
# Free account: https://www.maxmind.com/en/geolite2/signup
|
||||||
|
# After signup: Manage License Keys -> Generate new license key.
|
||||||
|
# Used by the geoipupdate sidecar to download/refresh GeoLite2-City.mmdb,
|
||||||
|
# consumed by the exporter for the Country column in the Local bans table.
|
||||||
|
# Leave blank to disable: exporter still runs, country labels will be empty.
|
||||||
|
MAXMIND_ACCOUNT_ID=
|
||||||
|
MAXMIND_LICENSE_KEY=
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
/db/*
|
/db/*
|
||||||
/config/*
|
/config/*
|
||||||
|
/geoip/*
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ services:
|
|||||||
- ./data/db:/var/lib/crowdsec/data
|
- ./data/db:/var/lib/crowdsec/data
|
||||||
- ./data/config:/etc/crowdsec
|
- ./data/config:/etc/crowdsec
|
||||||
- ./acquis.d:/etc/crowdsec/acquis.d:ro
|
- ./acquis.d:/etc/crowdsec/acquis.d:ro
|
||||||
|
- ./profiles/profiles.yaml:/etc/crowdsec/profiles.yaml:ro
|
||||||
- ./parsers/s00-raw/stalwart-logs.yaml:/etc/crowdsec/parsers/s00-raw/stalwart-logs.yaml:ro
|
- ./parsers/s00-raw/stalwart-logs.yaml:/etc/crowdsec/parsers/s00-raw/stalwart-logs.yaml:ro
|
||||||
- ./parsers/s01-parse/stalwart-logs-extended.yaml:/etc/crowdsec/parsers/s01-parse/stalwart-logs-extended.yaml:ro
|
- ./parsers/s01-parse/stalwart-logs-extended.yaml:/etc/crowdsec/parsers/s01-parse/stalwart-logs-extended.yaml:ro
|
||||||
- ./parsers/s02-enrich/whitelist-trusted.yaml:/etc/crowdsec/parsers/s02-enrich/whitelist-trusted.yaml:ro
|
- ./parsers/s02-enrich/whitelist-trusted.yaml:/etc/crowdsec/parsers/s02-enrich/whitelist-trusted.yaml:ro
|
||||||
@@ -63,6 +64,9 @@ services:
|
|||||||
CROWDSEC_API_KEY: "${CROWDSEC_BOUNCER_KEY_CADDY}"
|
CROWDSEC_API_KEY: "${CROWDSEC_BOUNCER_KEY_CADDY}"
|
||||||
POLL_INTERVAL_SECS: "30"
|
POLL_INTERVAL_SECS: "30"
|
||||||
LISTEN_PORT: "9100"
|
LISTEN_PORT: "9100"
|
||||||
|
GEOIP_CITY_DB: "/geoip/GeoLite2-City.mmdb"
|
||||||
|
volumes:
|
||||||
|
- ./data/geoip:/geoip:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- crowdsec
|
- crowdsec
|
||||||
logging:
|
logging:
|
||||||
@@ -70,3 +74,22 @@ services:
|
|||||||
options:
|
options:
|
||||||
max-size: "50m"
|
max-size: "50m"
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
|
||||||
|
geoipupdate:
|
||||||
|
image: ghcr.io/maxmind/geoipupdate:v7
|
||||||
|
container_name: crowdsec_geoipupdate
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
environment:
|
||||||
|
GEOIPUPDATE_ACCOUNT_ID: "${MAXMIND_ACCOUNT_ID}"
|
||||||
|
GEOIPUPDATE_LICENSE_KEY: "${MAXMIND_LICENSE_KEY}"
|
||||||
|
GEOIPUPDATE_EDITION_IDS: "GeoLite2-City"
|
||||||
|
GEOIPUPDATE_FREQUENCY: "24"
|
||||||
|
volumes:
|
||||||
|
- ./data/geoip:/usr/share/GeoIP
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "2"
|
||||||
|
|||||||
@@ -2,13 +2,17 @@ module crowdsec-exporter
|
|||||||
|
|
||||||
go 1.26.3
|
go 1.26.3
|
||||||
|
|
||||||
require github.com/prometheus/client_golang v1.23.2
|
require (
|
||||||
|
github.com/oschwald/geoip2-golang/v2 v2.1.0
|
||||||
|
github.com/prometheus/client_golang v1.23.2
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.6 // indirect
|
github.com/klauspost/compress v1.18.6 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.67.5 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.20.1 // indirect
|
github.com/prometheus/procfs v0.20.1 // indirect
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
|||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo=
|
||||||
|
github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
|
||||||
|
github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
|
||||||
|
github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
|
|||||||
@@ -4,14 +4,61 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/oschwald/geoip2-golang/v2"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var geoDB *geoip2.Reader
|
||||||
|
|
||||||
|
// countryFlag converts a 2-letter ISO-3166-1 alpha-2 country code into its
|
||||||
|
// emoji flag using the Unicode regional indicator pairs trick (A-Z -> 0x1F1E6-FF).
|
||||||
|
func countryFlag(iso string) string {
|
||||||
|
if len(iso) != 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
a, b := iso[0], iso[1]
|
||||||
|
if a < 'A' || a > 'Z' || b < 'A' || b > 'Z' {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string([]rune{rune(a-'A') + 0x1F1E6, rune(b-'A') + 0x1F1E6})
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupGeo returns "<flag> <country name> (<city>)" for the given IP, falling
|
||||||
|
// back gracefully (empty string, missing flag, missing city) when the DB is
|
||||||
|
// unavailable or the IP can't be resolved.
|
||||||
|
func lookupGeo(ip string) string {
|
||||||
|
if geoDB == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
addr, err := netip.ParseAddr(ip)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
rec, err := geoDB.City(addr)
|
||||||
|
if err != nil || rec == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
iso := rec.Country.ISOCode
|
||||||
|
name := rec.Country.Names.English
|
||||||
|
if name == "" {
|
||||||
|
name = iso
|
||||||
|
}
|
||||||
|
out := name
|
||||||
|
if flag := countryFlag(iso); flag != "" {
|
||||||
|
out = flag + " " + name
|
||||||
|
}
|
||||||
|
if city := rec.City.Names.English; city != "" {
|
||||||
|
out += " (" + city + ")"
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
decisionsActive = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
decisionsActive = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
Name: "crowdsec_decisions_active",
|
Name: "crowdsec_decisions_active",
|
||||||
@@ -28,6 +75,11 @@ var (
|
|||||||
Help: "Unique IPs with at least one active decision across all origins.",
|
Help: "Unique IPs with at least one active decision across all origins.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
decisionRemaining = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_decision_remaining_seconds",
|
||||||
|
Help: "Remaining seconds before each local decision expires. Only origin=crowdsec is exposed to keep cardinality bounded (CAPI/lists can contain tens of thousands of IPs).",
|
||||||
|
}, []string{"origin", "ip", "country", "scenario", "type"})
|
||||||
|
|
||||||
lastFetchUnix = prometheus.NewGauge(prometheus.GaugeOpts{
|
lastFetchUnix = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
Name: "crowdsec_exporter_last_fetch_unix",
|
Name: "crowdsec_exporter_last_fetch_unix",
|
||||||
Help: "Unix timestamp of the last successful LAPI fetch.",
|
Help: "Unix timestamp of the last successful LAPI fetch.",
|
||||||
@@ -44,6 +96,7 @@ type decision struct {
|
|||||||
Scenario string `json:"scenario"`
|
Scenario string `json:"scenario"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -51,6 +104,7 @@ func init() {
|
|||||||
decisionsActive,
|
decisionsActive,
|
||||||
decisionsUniqueIPs,
|
decisionsUniqueIPs,
|
||||||
decisionsUniqueIPsTotal,
|
decisionsUniqueIPsTotal,
|
||||||
|
decisionRemaining,
|
||||||
lastFetchUnix,
|
lastFetchUnix,
|
||||||
fetchErrors,
|
fetchErrors,
|
||||||
)
|
)
|
||||||
@@ -64,7 +118,12 @@ func getenv(k, def string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
||||||
req, _ := http.NewRequest("GET", lapiURL+"/v1/decisions", nil)
|
req, err := http.NewRequest("GET", lapiURL+"/v1/decisions", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[error] build request failed: %v", err)
|
||||||
|
fetchErrors.Set(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
req.Header.Set("X-Api-Key", apiKey)
|
req.Header.Set("X-Api-Key", apiKey)
|
||||||
req.Header.Set("User-Agent", "crowdsec-exporter/1.0")
|
req.Header.Set("User-Agent", "crowdsec-exporter/1.0")
|
||||||
|
|
||||||
@@ -93,6 +152,11 @@ func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
|||||||
counts := map[key]int{}
|
counts := map[key]int{}
|
||||||
ips := map[string]map[string]struct{}{}
|
ips := map[string]map[string]struct{}{}
|
||||||
allIPs := map[string]struct{}{}
|
allIPs := map[string]struct{}{}
|
||||||
|
type localDecision struct {
|
||||||
|
origin, ip, country, scenario, dtype string
|
||||||
|
remaining float64
|
||||||
|
}
|
||||||
|
var locals []localDecision
|
||||||
|
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
origin := d.Origin
|
origin := d.Origin
|
||||||
@@ -115,10 +179,18 @@ func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
|||||||
ips[origin][d.Value] = struct{}{}
|
ips[origin][d.Value] = struct{}{}
|
||||||
allIPs[d.Value] = struct{}{}
|
allIPs[d.Value] = struct{}{}
|
||||||
}
|
}
|
||||||
|
if origin == "crowdsec" {
|
||||||
|
dur, perr := time.ParseDuration(d.Duration)
|
||||||
|
if perr != nil {
|
||||||
|
dur = 0
|
||||||
|
}
|
||||||
|
locals = append(locals, localDecision{origin, d.Value, lookupGeo(d.Value), scenario, dtype, dur.Seconds()})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decisionsActive.Reset()
|
decisionsActive.Reset()
|
||||||
decisionsUniqueIPs.Reset()
|
decisionsUniqueIPs.Reset()
|
||||||
|
decisionRemaining.Reset()
|
||||||
for k, n := range counts {
|
for k, n := range counts {
|
||||||
decisionsActive.WithLabelValues(k.origin, k.scenario, k.dtype).Set(float64(n))
|
decisionsActive.WithLabelValues(k.origin, k.scenario, k.dtype).Set(float64(n))
|
||||||
}
|
}
|
||||||
@@ -126,6 +198,9 @@ func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
|||||||
decisionsUniqueIPs.WithLabelValues(origin).Set(float64(len(s)))
|
decisionsUniqueIPs.WithLabelValues(origin).Set(float64(len(s)))
|
||||||
}
|
}
|
||||||
decisionsUniqueIPsTotal.Set(float64(len(allIPs)))
|
decisionsUniqueIPsTotal.Set(float64(len(allIPs)))
|
||||||
|
for _, l := range locals {
|
||||||
|
decisionRemaining.WithLabelValues(l.origin, l.ip, l.country, l.scenario, l.dtype).Set(l.remaining)
|
||||||
|
}
|
||||||
|
|
||||||
fetchErrors.Set(0)
|
fetchErrors.Set(0)
|
||||||
lastFetchUnix.Set(float64(time.Now().Unix()))
|
lastFetchUnix.Set(float64(time.Now().Unix()))
|
||||||
@@ -147,6 +222,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
port := getenv("LISTEN_PORT", "9100")
|
port := getenv("LISTEN_PORT", "9100")
|
||||||
|
|
||||||
|
geoDBPath := getenv("GEOIP_CITY_DB", "")
|
||||||
|
if geoDBPath != "" {
|
||||||
|
if db, err := geoip2.Open(geoDBPath); err == nil {
|
||||||
|
geoDB = db
|
||||||
|
defer geoDB.Close()
|
||||||
|
log.Printf("[start] GeoIP DB loaded: %s", geoDBPath)
|
||||||
|
} else {
|
||||||
|
log.Printf("[warn] GeoIP DB unavailable (%v); country label will be empty", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: default_ip_remediation
|
||||||
|
filters:
|
||||||
|
- Alert.Remediation == true && Alert.GetScope() == "Ip"
|
||||||
|
decisions:
|
||||||
|
- type: ban
|
||||||
|
duration: 4h
|
||||||
|
duration_expr: Sprintf('%dh', min(168, (GetDecisionsCount(Alert.GetValue())+1)*4))
|
||||||
|
on_success: break
|
||||||
|
|
||||||
|
---
|
||||||
|
name: default_range_remediation
|
||||||
|
filters:
|
||||||
|
- Alert.Remediation == true && Alert.GetScope() == "Range"
|
||||||
|
decisions:
|
||||||
|
- type: ban
|
||||||
|
duration: 4h
|
||||||
|
duration_expr: Sprintf('%dh', min(168, (GetDecisionsCount(Alert.GetValue())+1)*4))
|
||||||
|
on_success: break
|
||||||
@@ -24,19 +24,6 @@
|
|||||||
"list": []
|
"list": []
|
||||||
},
|
},
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"type": "row",
|
|
||||||
"title": "Overview",
|
|
||||||
"collapsed": false,
|
|
||||||
"gridPos": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"w": 24,
|
|
||||||
"h": 1
|
|
||||||
},
|
|
||||||
"panels": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"type": "stat",
|
"type": "stat",
|
||||||
@@ -47,7 +34,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 1,
|
"y": 0,
|
||||||
"w": 6,
|
"w": 6,
|
||||||
"h": 4
|
"h": 4
|
||||||
},
|
},
|
||||||
@@ -112,7 +99,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": 1,
|
"y": 0,
|
||||||
"w": 6,
|
"w": 6,
|
||||||
"h": 4
|
"h": 4
|
||||||
},
|
},
|
||||||
@@ -160,7 +147,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 12,
|
"x": 12,
|
||||||
"y": 1,
|
"y": 0,
|
||||||
"w": 6,
|
"w": 6,
|
||||||
"h": 4
|
"h": 4
|
||||||
},
|
},
|
||||||
@@ -208,7 +195,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 18,
|
"x": 18,
|
||||||
"y": 1,
|
"y": 0,
|
||||||
"w": 6,
|
"w": 6,
|
||||||
"h": 4
|
"h": 4
|
||||||
},
|
},
|
||||||
@@ -246,19 +233,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"type": "row",
|
|
||||||
"title": "Active bans breakdown",
|
|
||||||
"collapsed": false,
|
|
||||||
"gridPos": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 5,
|
|
||||||
"w": 24,
|
|
||||||
"h": 1
|
|
||||||
},
|
|
||||||
"panels": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"type": "piechart",
|
"type": "piechart",
|
||||||
@@ -269,7 +243,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 6,
|
"y": 4,
|
||||||
"w": 12,
|
"w": 12,
|
||||||
"h": 9
|
"h": 9
|
||||||
},
|
},
|
||||||
@@ -323,7 +297,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 12,
|
"x": 12,
|
||||||
"y": 6,
|
"y": 4,
|
||||||
"w": 12,
|
"w": 12,
|
||||||
"h": 9
|
"h": 9
|
||||||
},
|
},
|
||||||
@@ -367,19 +341,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": 12,
|
|
||||||
"type": "row",
|
|
||||||
"title": "Local detection activity (your engine)",
|
|
||||||
"collapsed": false,
|
|
||||||
"gridPos": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 15,
|
|
||||||
"w": 24,
|
|
||||||
"h": 1
|
|
||||||
},
|
|
||||||
"panels": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 14,
|
"id": 14,
|
||||||
"type": "bargauge",
|
"type": "bargauge",
|
||||||
@@ -390,7 +351,7 @@
|
|||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 16,
|
"y": 23,
|
||||||
"w": 24,
|
"w": 24,
|
||||||
"h": 8
|
"h": 8
|
||||||
},
|
},
|
||||||
@@ -428,6 +389,108 @@
|
|||||||
"values": false
|
"values": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "table",
|
||||||
|
"title": "Local bans detail",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 13,
|
||||||
|
"w": 24,
|
||||||
|
"h": 10
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "crowdsec_decision_remaining_seconds{origin=\"crowdsec\"}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {
|
||||||
|
"Time": true,
|
||||||
|
"__name__": true,
|
||||||
|
"instance": true,
|
||||||
|
"job": true,
|
||||||
|
"origin": true
|
||||||
|
},
|
||||||
|
"indexByName": {
|
||||||
|
"country": 0,
|
||||||
|
"ip": 1,
|
||||||
|
"scenario": 2,
|
||||||
|
"type": 3,
|
||||||
|
"Value": 4
|
||||||
|
},
|
||||||
|
"renameByName": {
|
||||||
|
"country": "Country",
|
||||||
|
"ip": "IP",
|
||||||
|
"scenario": "Scenario",
|
||||||
|
"type": "Type",
|
||||||
|
"Value": "Time remaining"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"custom": {
|
||||||
|
"align": "auto",
|
||||||
|
"filterable": true,
|
||||||
|
"inspect": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Time remaining"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custom.cellOptions",
|
||||||
|
"value": {
|
||||||
|
"type": "color-text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"mode": "continuous-GrYlRd"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"showHeader": true,
|
||||||
|
"sortBy": [
|
||||||
|
{
|
||||||
|
"displayName": "Time remaining",
|
||||||
|
"desc": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"footer": {
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user