Skip to content

Commit 1007fc1

Browse files
authored
feat(config): Add support for "dynamic" configuration (#216)
* feat: Add support for managed switches * feat: Add dynamic configuration * fix: update to go 1.18 * doc(readme): enforce text about protecting the api tokens in Prometheus
1 parent f974037 commit 1007fc1

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Prometheus exporter for FortiGate® firewalls.
1111

1212
* [Supported Metrics](#supported-metrics)
1313
* [Usage](#usage)
14+
+ [Dynamic configuration](#dynamic-configuration)
1415
+ [Available CLI parameters](#available-cli-parameters)
1516
+ [Fortigate Configuration](#fortigate-configuration)
1617
+ [Prometheus Configuration](#prometheus-configuration)
@@ -324,6 +325,39 @@ Special cases:
324325

325326
To probe a FortiGate, do something like `curl 'localhost:9710/probe?target=https://my-fortigate'`
326327

328+
### Dynamic configuration
329+
In use cases where the Fortigates that is to be scraped through the fortigate-exporter is configured in
330+
Prometheus using some discovery method it becomes problematic that the `fortigate-key.yaml` configuration also
331+
has to be updated for each fortigate, and that the fortigate-exporter needs to be restarted on each change.
332+
For that scenario the token can be passed as a query parameter, `token`, to the fortigate.
333+
334+
Example:
335+
```bash
336+
curl 'localhost:9710/probe?target=https://192.168.2.31&token=ghi6eItWzWewgbrFMsazvBVwDjZzzb'
337+
```
338+
It is also possible to pass a `profile` query parameter. The value will match an entry in the `fortigate-key.yaml`
339+
file, but only to use the `probes` section for include/exclude directives.
340+
341+
Example:
342+
```bash
343+
curl 'localhost:9710/probe?target=https://192.168.2.31&token=ghi6eItWzWewgbrFMsazvBVwDjZzzb&profile=fs124e'
344+
```
345+
The `profile=fs124e` would match the following entry in `fortigate-key.yaml`.
346+
347+
Example:
348+
```yaml
349+
fs124e:
350+
# token: not used
351+
probes:
352+
include:
353+
- System
354+
- Firewall
355+
exclude:
356+
- System/LinkMonitor
357+
```
358+
359+
360+
327361
### Available CLI parameters
328362
329363
| flag | default value | description |
@@ -437,6 +471,40 @@ An example configuration for Prometheus looks something like this:
437471
replacement: '[::1]:9710'
438472
```
439473

474+
If using [Dynamic configuration](#dynamic-configuration):
475+
```yaml
476+
- job_name: 'fortigate_exporter'
477+
metrics_path: /probe
478+
file_sd_configs:
479+
- files:
480+
- /etc/prometheus/file_sd/fws/*.yml
481+
params:
482+
profile:
483+
- fs124e
484+
relabel_configs:
485+
- source_labels: [__address__]
486+
target_label: __param_target
487+
- source_labels: [token]
488+
target_label: __param_token
489+
- source_labels: [__param_target]
490+
regex: '(?:.+)(?::\/\/)([^:]*).*'
491+
target_label: instance
492+
- target_label: __address__
493+
replacement: '[::1]:9710'
494+
- action: labeldrop
495+
regex: token
496+
```
497+
> Make sure to use the last labeldrop on the `token` label so that the tokens is not be part of your time series.
498+
499+
> Since `token` is a label it will be shown in the Prometheus webgui at `http://<your prometheus>:9090/targets`.
500+
>
501+
> **Make sure you protect your Prometheus if you add the token part of your prometheus config**
502+
>
503+
> Some options to protect Prometheus:
504+
> - Only expose UI to localhost --web.listen-address="127.0.0.1:9090"
505+
> - Basic authentication access - https://prometheus.io/docs/guides/basic-auth/
506+
> - **It is your responsibility!**
507+
440508
### Docker
441509

442510
You can either use the automatic builds on

pkg/probe/main.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,16 @@ func ProbeHandler(w http.ResponseWriter, r *http.Request) {
1616
savedConfig := config.GetConfig()
1717

1818
params := r.URL.Query()
19+
paramMap := make(map[string]string)
1920
target := params.Get("target")
21+
paramMap["target"] = params.Get("target")
22+
if params.Get("token") != "" {
23+
paramMap["token"] = params.Get("token")
24+
}
25+
if params.Get("profile") != "" {
26+
paramMap["profile"] = params.Get("profile")
27+
}
28+
2029
if target == "" {
2130
http.Error(w, "Target parameter missing or empty", http.StatusBadRequest)
2231
return
@@ -37,7 +46,7 @@ func ProbeHandler(w http.ResponseWriter, r *http.Request) {
3746
start := time.Now()
3847
pc := &ProbeCollector{}
3948
registry.MustRegister(pc)
40-
success, err := pc.Probe(ctx, target, &http.Client{}, savedConfig)
49+
success, err := pc.Probe(ctx, paramMap, &http.Client{}, savedConfig)
4150
if err != nil {
4251
log.Printf("Probe request rejected; error is: %v", err)
4352
http.Error(w, fmt.Sprintf("probe: %v", err), http.StatusBadRequest)

pkg/probe/probe.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ type probeDetailedFunc struct {
4747
function probeFunc
4848
}
4949

50-
func (p *ProbeCollector) Probe(ctx context.Context, target string, hc *http.Client, savedConfig config.FortiExporterConfig) (bool, error) {
51-
tgt, err := url.Parse(target)
50+
func (p *ProbeCollector) Probe(ctx context.Context, target map[string]string, hc *http.Client, savedConfig config.FortiExporterConfig) (bool, error) {
51+
tgt, err := url.Parse(target["target"])
5252
if err != nil {
5353
return false, fmt.Errorf("url.Parse failed: %v", err)
5454
}
@@ -62,6 +62,14 @@ func (p *ProbeCollector) Probe(ctx context.Context, target string, hc *http.Clie
6262
Scheme: tgt.Scheme,
6363
Host: tgt.Host,
6464
}
65+
66+
if target["token"] != "" && savedConfig.AuthKeys[config.Target(target["target"])].Token == "" {
67+
// Add the target and its apikey to the savedConfig and use, if exists, a target entry as a template for include/exclude
68+
// This will only happend the "first" time
69+
savedConfig.AuthKeys[config.Target(target["target"])] = config.TargetAuth{Token: config.Token(target["token"]),
70+
Probes: savedConfig.AuthKeys[config.Target(target["profile"])].Probes}
71+
}
72+
6573
c, err := fortiHTTP.NewFortiClient(ctx, u, hc, savedConfig)
6674
if err != nil {
6775
return false, err

0 commit comments

Comments
 (0)