@@ -9,18 +9,21 @@ import (
9
9
"net/http"
10
10
"os"
11
11
"os/signal"
12
+ "slices"
12
13
"strings"
13
14
"syscall"
15
+ "time"
14
16
15
17
"github.com/prometheus/client_golang/prometheus"
16
18
"github.com/prometheus/client_golang/prometheus/promhttp"
19
+ io_prometheus_client "github.com/prometheus/client_model/go"
17
20
log "github.com/sirupsen/logrus"
18
- "golang.org/x/exp/slices"
19
21
"golang.org/x/sync/errgroup"
20
22
21
23
csbouncer "github.com/crowdsecurity/go-cs-bouncer"
22
24
"github.com/crowdsecurity/go-cs-lib/csdaemon"
23
25
"github.com/crowdsecurity/go-cs-lib/csstring"
26
+ "github.com/crowdsecurity/go-cs-lib/ptr"
24
27
"github.com/crowdsecurity/go-cs-lib/version"
25
28
26
29
"github.com/crowdsecurity/crowdsec/pkg/models"
@@ -30,7 +33,11 @@ import (
30
33
"github.com/crowdsecurity/cs-firewall-bouncer/pkg/metrics"
31
34
)
32
35
33
- const name = "crowdsec-firewall-bouncer"
36
+ const bouncerType = "crowdsec-firewall-bouncer"
37
+
38
+ type metricsHandler struct {
39
+ backend * backend.BackendCTX
40
+ }
34
41
35
42
func backendCleanup (backend * backend.BackendCTX ) {
36
43
log .Info ("Shutting down backend" )
@@ -136,6 +143,134 @@ func addDecisions(backend *backend.BackendCTX, decisions []*models.Decision, con
136
143
}
137
144
}
138
145
146
+ func getLabelValue (labels []* io_prometheus_client.LabelPair , key string ) string {
147
+
148
+ for _ , label := range labels {
149
+ if label .GetName () == key {
150
+ return label .GetValue ()
151
+ }
152
+ }
153
+
154
+ return ""
155
+ }
156
+
157
+ // metricsUpdater receives a metrics struct with basic data and populates it with the current metrics.
158
+ func (m metricsHandler ) metricsUpdater (met * models.RemediationComponentsMetrics , updateInterval time.Duration ) {
159
+ log .Debugf ("Updating metrics" )
160
+
161
+ m .backend .CollectMetrics ()
162
+
163
+ //Most of the common fields are set automatically by the metrics provider
164
+ //We only need to care about the metrics themselves
165
+
166
+ promMetrics , err := prometheus .DefaultGatherer .Gather ()
167
+
168
+ if err != nil {
169
+ log .Errorf ("unable to gather prometheus metrics: %s" , err )
170
+ return
171
+ }
172
+
173
+ met .Metrics = append (met .Metrics , & models.DetailedMetrics {
174
+ Meta : & models.MetricsMeta {
175
+ UtcNowTimestamp : ptr .Of (time .Now ().Unix ()),
176
+ WindowSizeSeconds : ptr .Of (int64 (updateInterval .Seconds ())),
177
+ },
178
+ Items : make ([]* models.MetricsDetailItem , 0 ),
179
+ })
180
+
181
+ for _ , metricFamily := range promMetrics {
182
+ for _ , metric := range metricFamily .GetMetric () {
183
+ switch metricFamily .GetName () {
184
+ case metrics .ActiveBannedIPsMetricName :
185
+ //We send the absolute value, as it makes no sense to try to sum them crowdsec side
186
+ labels := metric .GetLabel ()
187
+ value := metric .GetGauge ().GetValue ()
188
+ origin := getLabelValue (labels , "origin" )
189
+ ipType := getLabelValue (labels , "ip_type" )
190
+ log .Debugf ("Sending active decisions for %s %s | current value: %f" , origin , ipType , value )
191
+ met .Metrics [0 ].Items = append (met .Metrics [0 ].Items , & models.MetricsDetailItem {
192
+ Name : ptr .Of ("active_decisions" ),
193
+ Value : ptr .Of (value ),
194
+ Labels : map [string ]string {
195
+ "origin" : origin ,
196
+ "ip_type" : ipType ,
197
+ },
198
+ Unit : ptr .Of ("ip" ),
199
+ })
200
+ case metrics .DroppedBytesMetricName :
201
+ labels := metric .GetLabel ()
202
+ value := metric .GetGauge ().GetValue ()
203
+ origin := getLabelValue (labels , "origin" )
204
+ ipType := getLabelValue (labels , "ip_type" )
205
+ key := origin + ipType
206
+ log .Debugf ("Sending dropped bytes for %s %s %f | current value: %f | previous value: %f\n " , origin , ipType , value - metrics .LastDroppedBytesValue [key ], value , metrics .LastDroppedBytesValue [key ])
207
+ met .Metrics [0 ].Items = append (met .Metrics [0 ].Items , & models.MetricsDetailItem {
208
+ Name : ptr .Of ("dropped" ),
209
+ Value : ptr .Of (value - metrics .LastDroppedBytesValue [key ]),
210
+ Labels : map [string ]string {
211
+ "origin" : origin ,
212
+ "ip_type" : ipType ,
213
+ },
214
+ Unit : ptr .Of ("byte" ),
215
+ })
216
+ metrics .LastDroppedBytesValue [key ] = value
217
+ case metrics .DroppedPacketsMetricName :
218
+ labels := metric .GetLabel ()
219
+ value := metric .GetGauge ().GetValue ()
220
+ origin := getLabelValue (labels , "origin" )
221
+ ipType := getLabelValue (labels , "ip_type" )
222
+ key := origin + ipType
223
+ log .Debugf ("Sending dropped packets for %s %s %f | current value: %f | previous value: %f\n " , origin , ipType , value - metrics .LastDroppedPacketsValue [key ], value , metrics .LastDroppedPacketsValue [key ])
224
+ met .Metrics [0 ].Items = append (met .Metrics [0 ].Items , & models.MetricsDetailItem {
225
+ Name : ptr .Of ("dropped" ),
226
+ Value : ptr .Of (value - metrics .LastDroppedPacketsValue [key ]),
227
+ Labels : map [string ]string {
228
+ "origin" : origin ,
229
+ "ip_type" : ipType ,
230
+ },
231
+ Unit : ptr .Of ("packet" ),
232
+ })
233
+ metrics .LastDroppedPacketsValue [key ] = value
234
+ case metrics .ProcessedBytesMetricName :
235
+ labels := metric .GetLabel ()
236
+ value := metric .GetGauge ().GetValue ()
237
+ ipType := getLabelValue (labels , "ip_type" )
238
+ log .Debugf ("Sending processed bytes for %s %f | current value: %f | previous value: %f\n " , ipType , value - metrics .LastProcessedBytesValue [ipType ], value , metrics .LastProcessedBytesValue [ipType ])
239
+ met .Metrics [0 ].Items = append (met .Metrics [0 ].Items , & models.MetricsDetailItem {
240
+ Name : ptr .Of ("processed" ),
241
+ Value : ptr .Of (value - metrics .LastProcessedBytesValue [ipType ]),
242
+ Labels : map [string ]string {
243
+ "ip_type" : ipType ,
244
+ },
245
+ Unit : ptr .Of ("byte" ),
246
+ })
247
+ metrics .LastProcessedBytesValue [ipType ] = value
248
+ case metrics .ProcessedPacketsMetricName :
249
+ labels := metric .GetLabel ()
250
+ value := metric .GetGauge ().GetValue ()
251
+ ipType := getLabelValue (labels , "ip_type" )
252
+ log .Debugf ("Sending processed packets for %s %f | current value: %f | previous value: %f\n " , ipType , value - metrics .LastProcessedPacketsValue [ipType ], value , metrics .LastProcessedPacketsValue [ipType ])
253
+ met .Metrics [0 ].Items = append (met .Metrics [0 ].Items , & models.MetricsDetailItem {
254
+ Name : ptr .Of ("processed" ),
255
+ Value : ptr .Of (value - metrics .LastProcessedPacketsValue [ipType ]),
256
+ Labels : map [string ]string {
257
+ "ip_type" : ipType ,
258
+ },
259
+ Unit : ptr .Of ("packet" ),
260
+ })
261
+ metrics .LastProcessedPacketsValue [ipType ] = value
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+ func (m metricsHandler ) computeMetricsHandler (next http.Handler ) http.Handler {
268
+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
269
+ m .backend .CollectMetrics ()
270
+ next .ServeHTTP (w , r )
271
+ })
272
+ }
273
+
139
274
func Execute () error {
140
275
configPath := flag .String ("c" , "" , "path to crowdsec-firewall-bouncer.yaml" )
141
276
verbose := flag .Bool ("v" , false , "set verbose mode" )
@@ -176,7 +311,7 @@ func Execute() error {
176
311
log .SetLevel (log .DebugLevel )
177
312
}
178
313
179
- log .Infof ("Starting crowdsec-firewall-bouncer %s" , version .String ())
314
+ log .Infof ("Starting %s %s" , bouncerType , version .String ())
180
315
181
316
backend , err := backend .NewBackend (config )
182
317
if err != nil {
@@ -196,7 +331,7 @@ func Execute() error {
196
331
return err
197
332
}
198
333
199
- bouncer .UserAgent = fmt .Sprintf ("%s/%s" , name , version .String ())
334
+ bouncer .UserAgent = fmt .Sprintf ("%s/%s" , bouncerType , version .String ())
200
335
if err := bouncer .Init (); err != nil {
201
336
return fmt .Errorf ("unable to configure bouncer: %w" , err )
202
337
}
@@ -217,21 +352,27 @@ func Execute() error {
217
352
return errors .New ("bouncer stream halted" )
218
353
})
219
354
220
- if config . PrometheusConfig . Enabled {
221
- if config . Mode == cfg . IptablesMode || config . Mode == cfg . NftablesMode || config . Mode == cfg . IpsetMode || config . Mode == cfg . PfMode {
222
- go backend . CollectMetrics ()
355
+ mHandler := metricsHandler {
356
+ backend : backend ,
357
+ }
223
358
224
- if config .Mode == cfg .IpsetMode {
225
- prometheus .MustRegister (metrics .TotalActiveBannedIPs )
226
- } else {
227
- prometheus .MustRegister (metrics .TotalDroppedBytes , metrics .TotalDroppedPackets , metrics .TotalActiveBannedIPs )
228
- }
229
- }
359
+ metricsProvider , err := csbouncer .NewMetricsProvider (bouncer .APIClient , bouncerType , mHandler .metricsUpdater , log .StandardLogger ())
360
+ if err != nil {
361
+ return fmt .Errorf ("unable to create metrics provider: %w" , err )
362
+ }
230
363
231
- prometheus .MustRegister (csbouncer .TotalLAPICalls , csbouncer .TotalLAPIError )
364
+ g .Go (func () error {
365
+ return metricsProvider .Run (ctx )
366
+ })
367
+
368
+ if config .Mode == cfg .IptablesMode || config .Mode == cfg .NftablesMode || config .Mode == cfg .IpsetMode || config .Mode == cfg .PfMode {
369
+ prometheus .MustRegister (metrics .TotalDroppedBytes , metrics .TotalDroppedPackets , metrics .TotalActiveBannedIPs , metrics .TotalProcessedBytes , metrics .TotalProcessedPackets )
370
+ }
232
371
372
+ prometheus .MustRegister (csbouncer .TotalLAPICalls , csbouncer .TotalLAPIError )
373
+ if config .PrometheusConfig .Enabled {
233
374
go func () {
234
- http .Handle ("/metrics" , promhttp .Handler ())
375
+ http .Handle ("/metrics" , mHandler . computeMetricsHandler ( promhttp .Handler () ))
235
376
236
377
listenOn := net .JoinHostPort (
237
378
config .PrometheusConfig .ListenAddress ,
0 commit comments