mirror of
https://github.com/aykhans/my-self-host-services.git
synced 2026-05-29 15:35:59 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed68045605 | |||
| b19d315f9a | |||
| 8250e08871 | |||
| 5a2c6e2d50 | |||
| 504619b7b4 | |||
| 90b8b5a3c3 |
@@ -51,3 +51,22 @@ services:
|
|||||||
options:
|
options:
|
||||||
max-size: "100m"
|
max-size: "100m"
|
||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
|
||||||
|
exporter:
|
||||||
|
build: ./exporter
|
||||||
|
container_name: crowdsec_exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- caddy
|
||||||
|
environment:
|
||||||
|
CROWDSEC_LAPI_URL: "http://crowdsec:8080"
|
||||||
|
CROWDSEC_API_KEY: "${CROWDSEC_BOUNCER_KEY_CADDY}"
|
||||||
|
POLL_INTERVAL_SECS: "30"
|
||||||
|
LISTEN_PORT: "9100"
|
||||||
|
depends_on:
|
||||||
|
- crowdsec
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "3"
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
FROM golang:1.26-alpine AS builder
|
||||||
|
WORKDIR /src
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY main.go ./
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags='-s -w' -o /exporter .
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=builder /exporter /exporter
|
||||||
|
EXPOSE 9100
|
||||||
|
ENTRYPOINT ["/exporter"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
module crowdsec-exporter
|
||||||
|
|
||||||
|
go 1.26.3
|
||||||
|
|
||||||
|
require github.com/prometheus/client_golang v1.23.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.6 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
|
github.com/prometheus/procfs v0.20.1 // indirect
|
||||||
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
|
golang.org/x/sys v0.44.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||||
|
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
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/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
|
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||||
|
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||||
|
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||||
|
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||||
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
decisionsActive = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_decisions_active",
|
||||||
|
Help: "Active CrowdSec decisions counted directly from LAPI (no gauge drift).",
|
||||||
|
}, []string{"origin", "scenario", "type"})
|
||||||
|
|
||||||
|
decisionsUniqueIPs = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_decisions_unique_ips",
|
||||||
|
Help: "Unique IPs with at least one active decision, grouped by origin.",
|
||||||
|
}, []string{"origin"})
|
||||||
|
|
||||||
|
decisionsUniqueIPsTotal = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_decisions_unique_ips_total",
|
||||||
|
Help: "Unique IPs with at least one active decision across all origins.",
|
||||||
|
})
|
||||||
|
|
||||||
|
lastFetchUnix = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_exporter_last_fetch_unix",
|
||||||
|
Help: "Unix timestamp of the last successful LAPI fetch.",
|
||||||
|
})
|
||||||
|
|
||||||
|
fetchErrors = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||||
|
Name: "crowdsec_exporter_fetch_errors",
|
||||||
|
Help: "1 if the most recent fetch errored, else 0.",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
type decision struct {
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
Scenario string `json:"scenario"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prometheus.MustRegister(
|
||||||
|
decisionsActive,
|
||||||
|
decisionsUniqueIPs,
|
||||||
|
decisionsUniqueIPsTotal,
|
||||||
|
lastFetchUnix,
|
||||||
|
fetchErrors,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getenv(k, def string) string {
|
||||||
|
if v := os.Getenv(k); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAndUpdate(client *http.Client, lapiURL, apiKey string) {
|
||||||
|
req, _ := http.NewRequest("GET", lapiURL+"/v1/decisions", nil)
|
||||||
|
req.Header.Set("X-Api-Key", apiKey)
|
||||||
|
req.Header.Set("User-Agent", "crowdsec-exporter/1.0")
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[error] fetch failed: %v", err)
|
||||||
|
fetchErrors.Set(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
log.Printf("[error] LAPI returned status %d", resp.StatusCode)
|
||||||
|
fetchErrors.Set(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []decision
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||||
|
log.Printf("[error] decode failed: %v", err)
|
||||||
|
fetchErrors.Set(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type key struct{ origin, scenario, dtype string }
|
||||||
|
counts := map[key]int{}
|
||||||
|
ips := map[string]map[string]struct{}{}
|
||||||
|
allIPs := map[string]struct{}{}
|
||||||
|
|
||||||
|
for _, d := range data {
|
||||||
|
origin := d.Origin
|
||||||
|
if origin == "" {
|
||||||
|
origin = "unknown"
|
||||||
|
}
|
||||||
|
scenario := d.Scenario
|
||||||
|
if scenario == "" {
|
||||||
|
scenario = "unknown"
|
||||||
|
}
|
||||||
|
dtype := d.Type
|
||||||
|
if dtype == "" {
|
||||||
|
dtype = "unknown"
|
||||||
|
}
|
||||||
|
counts[key{origin, scenario, dtype}]++
|
||||||
|
if d.Value != "" {
|
||||||
|
if _, ok := ips[origin]; !ok {
|
||||||
|
ips[origin] = map[string]struct{}{}
|
||||||
|
}
|
||||||
|
ips[origin][d.Value] = struct{}{}
|
||||||
|
allIPs[d.Value] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decisionsActive.Reset()
|
||||||
|
decisionsUniqueIPs.Reset()
|
||||||
|
for k, n := range counts {
|
||||||
|
decisionsActive.WithLabelValues(k.origin, k.scenario, k.dtype).Set(float64(n))
|
||||||
|
}
|
||||||
|
for origin, s := range ips {
|
||||||
|
decisionsUniqueIPs.WithLabelValues(origin).Set(float64(len(s)))
|
||||||
|
}
|
||||||
|
decisionsUniqueIPsTotal.Set(float64(len(allIPs)))
|
||||||
|
|
||||||
|
fetchErrors.Set(0)
|
||||||
|
lastFetchUnix.Set(float64(time.Now().Unix()))
|
||||||
|
|
||||||
|
log.Printf("[ok] %d decisions, %d unique IPs across %d origins",
|
||||||
|
len(data), len(allIPs), len(ips))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
lapiURL := getenv("CROWDSEC_LAPI_URL", "http://crowdsec:8080")
|
||||||
|
apiKey := os.Getenv("CROWDSEC_API_KEY")
|
||||||
|
if apiKey == "" {
|
||||||
|
log.Fatal("CROWDSEC_API_KEY env var required")
|
||||||
|
}
|
||||||
|
intervalStr := getenv("POLL_INTERVAL_SECS", "30")
|
||||||
|
interval, err := strconv.Atoi(intervalStr)
|
||||||
|
if err != nil || interval < 1 {
|
||||||
|
log.Fatalf("invalid POLL_INTERVAL_SECS: %q", intervalStr)
|
||||||
|
}
|
||||||
|
port := getenv("LISTEN_PORT", "9100")
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
fetchAndUpdate(client, lapiURL, apiKey)
|
||||||
|
time.Sleep(time.Duration(interval) * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
log.Printf("[start] listening :%s, polling %s every %ds", port, lapiURL, interval)
|
||||||
|
if err := http.ListenAndServe(":"+port, nil); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 1,
|
||||||
|
"id": null,
|
||||||
|
"uid": "crowdsec",
|
||||||
|
"title": "CrowdSec",
|
||||||
|
"tags": [
|
||||||
|
"crowdsec",
|
||||||
|
"security"
|
||||||
|
],
|
||||||
|
"timezone": "browser",
|
||||||
|
"schemaVersion": 39,
|
||||||
|
"version": 1,
|
||||||
|
"refresh": "30s",
|
||||||
|
"time": {
|
||||||
|
"from": "now-24h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "row",
|
||||||
|
"title": "Overview",
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"w": 24,
|
||||||
|
"h": 1
|
||||||
|
},
|
||||||
|
"panels": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"type": "stat",
|
||||||
|
"title": "Total active bans",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 1,
|
||||||
|
"w": 6,
|
||||||
|
"h": 4
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "crowdsec_decisions_unique_ips_total",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"noValue": "0",
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "yellow",
|
||||||
|
"value": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 10000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"textMode": "value",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"type": "stat",
|
||||||
|
"title": "CAPI (community)",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 6,
|
||||||
|
"y": 1,
|
||||||
|
"w": 6,
|
||||||
|
"h": 4
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "crowdsec_decisions_unique_ips{origin=\"CAPI\"}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"noValue": "0",
|
||||||
|
"color": {
|
||||||
|
"mode": "purple"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"textMode": "value",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "stat",
|
||||||
|
"title": "Blocklist subs",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 12,
|
||||||
|
"y": 1,
|
||||||
|
"w": 6,
|
||||||
|
"h": 4
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "crowdsec_decisions_unique_ips{origin=\"lists\"}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"noValue": "0",
|
||||||
|
"color": {
|
||||||
|
"mode": "blue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"textMode": "value",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"type": "stat",
|
||||||
|
"title": "Local detections",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 18,
|
||||||
|
"y": 1,
|
||||||
|
"w": 6,
|
||||||
|
"h": 4
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "crowdsec_decisions_unique_ips{origin=\"crowdsec\"}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"noValue": "0",
|
||||||
|
"color": {
|
||||||
|
"mode": "red"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"textMode": "value",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"type": "row",
|
||||||
|
"title": "Active bans breakdown",
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 5,
|
||||||
|
"w": 24,
|
||||||
|
"h": 1
|
||||||
|
},
|
||||||
|
"panels": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"type": "piechart",
|
||||||
|
"title": "Bans by origin",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 6,
|
||||||
|
"w": 12,
|
||||||
|
"h": 9
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "sum by (origin) (crowdsec_decisions_unique_ips)",
|
||||||
|
"legendFormat": "{{origin}}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"pieType": "donut",
|
||||||
|
"legend": {
|
||||||
|
"displayMode": "table",
|
||||||
|
"placement": "right",
|
||||||
|
"values": [
|
||||||
|
"value",
|
||||||
|
"percent"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single"
|
||||||
|
},
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"type": "piechart",
|
||||||
|
"title": "Top 10 bans by reason",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 12,
|
||||||
|
"y": 6,
|
||||||
|
"w": 12,
|
||||||
|
"h": 9
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "topk(10, sum by (scenario) (crowdsec_decisions_active))",
|
||||||
|
"legendFormat": "{{scenario}}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"pieType": "donut",
|
||||||
|
"legend": {
|
||||||
|
"displayMode": "table",
|
||||||
|
"placement": "right",
|
||||||
|
"values": [
|
||||||
|
"value",
|
||||||
|
"percent"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single"
|
||||||
|
},
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"type": "row",
|
||||||
|
"title": "Local detection activity (your engine)",
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 15,
|
||||||
|
"w": 24,
|
||||||
|
"h": 1
|
||||||
|
},
|
||||||
|
"panels": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"type": "bargauge",
|
||||||
|
"title": "Top scenarios by alerts (total)",
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 16,
|
||||||
|
"w": 24,
|
||||||
|
"h": 8
|
||||||
|
},
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "topk(10, sum by (reason) (cs_alerts))",
|
||||||
|
"legendFormat": "{{reason}}",
|
||||||
|
"refId": "A",
|
||||||
|
"instant": true,
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "prometheusdatasource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"color": {
|
||||||
|
"mode": "continuous-GrYlRd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"displayMode": "gradient",
|
||||||
|
"showUnfilled": true,
|
||||||
|
"valueMode": "color",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -14,9 +14,9 @@ providers:
|
|||||||
disableDeletion: false
|
disableDeletion: false
|
||||||
# Optional: Allow users to edit dashboards in the Grafana UI.
|
# Optional: Allow users to edit dashboards in the Grafana UI.
|
||||||
# Changes will be overwritten on restart unless you save them elsewhere.
|
# Changes will be overwritten on restart unless you save them elsewhere.
|
||||||
editable: true
|
editable: false
|
||||||
updateIntervalSeconds: 10
|
updateIntervalSeconds: 10
|
||||||
allowUiUpdates: true
|
allowUiUpdates: false
|
||||||
# Options specific to the 'file' type provider
|
# Options specific to the 'file' type provider
|
||||||
options:
|
options:
|
||||||
# Path inside the container where Grafana should look for dashboard JSON files.
|
# Path inside the container where Grafana should look for dashboard JSON files.
|
||||||
|
|||||||
@@ -16,3 +16,13 @@ scrape_configs:
|
|||||||
scrape_interval: 2s
|
scrape_interval: 2s
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ["caddy:2019"]
|
- targets: ["caddy:2019"]
|
||||||
|
|
||||||
|
- job_name: crowdsec
|
||||||
|
scrape_interval: 10s
|
||||||
|
static_configs:
|
||||||
|
- targets: ["crowdsec:6060"]
|
||||||
|
|
||||||
|
- job_name: crowdsec_exporter
|
||||||
|
scrape_interval: 30s
|
||||||
|
static_configs:
|
||||||
|
- targets: ["crowdsec_exporter:9100"]
|
||||||
|
|||||||
Reference in New Issue
Block a user