Skip to content

Commit

Permalink
feat(config): Add support for "dynamic" configuration (#216)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
thenodon authored May 3, 2023
1 parent f974037 commit 1007fc1
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 3 deletions.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Prometheus exporter for FortiGate® firewalls.

* [Supported Metrics](#supported-metrics)
* [Usage](#usage)
+ [Dynamic configuration](#dynamic-configuration)
+ [Available CLI parameters](#available-cli-parameters)
+ [Fortigate Configuration](#fortigate-configuration)
+ [Prometheus Configuration](#prometheus-configuration)
Expand Down Expand Up @@ -324,6 +325,39 @@ Special cases:

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

### Dynamic configuration
In use cases where the Fortigates that is to be scraped through the fortigate-exporter is configured in
Prometheus using some discovery method it becomes problematic that the `fortigate-key.yaml` configuration also
has to be updated for each fortigate, and that the fortigate-exporter needs to be restarted on each change.
For that scenario the token can be passed as a query parameter, `token`, to the fortigate.

Example:
```bash
curl 'localhost:9710/probe?target=https://192.168.2.31&token=ghi6eItWzWewgbrFMsazvBVwDjZzzb'
```
It is also possible to pass a `profile` query parameter. The value will match an entry in the `fortigate-key.yaml`
file, but only to use the `probes` section for include/exclude directives.

Example:
```bash
curl 'localhost:9710/probe?target=https://192.168.2.31&token=ghi6eItWzWewgbrFMsazvBVwDjZzzb&profile=fs124e'
```
The `profile=fs124e` would match the following entry in `fortigate-key.yaml`.

Example:
```yaml
fs124e:
# token: not used
probes:
include:
- System
- Firewall
exclude:
- System/LinkMonitor
```
### Available CLI parameters
| flag | default value | description |
Expand Down Expand Up @@ -437,6 +471,40 @@ An example configuration for Prometheus looks something like this:
replacement: '[::1]:9710'
```

If using [Dynamic configuration](#dynamic-configuration):
```yaml
- job_name: 'fortigate_exporter'
metrics_path: /probe
file_sd_configs:
- files:
- /etc/prometheus/file_sd/fws/*.yml
params:
profile:
- fs124e
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [token]
target_label: __param_token
- source_labels: [__param_target]
regex: '(?:.+)(?::\/\/)([^:]*).*'
target_label: instance
- target_label: __address__
replacement: '[::1]:9710'
- action: labeldrop
regex: token
```
> Make sure to use the last labeldrop on the `token` label so that the tokens is not be part of your time series.

> Since `token` is a label it will be shown in the Prometheus webgui at `http://<your prometheus>:9090/targets`.
>
> **Make sure you protect your Prometheus if you add the token part of your prometheus config**
>
> Some options to protect Prometheus:
> - Only expose UI to localhost --web.listen-address="127.0.0.1:9090"
> - Basic authentication access - https://prometheus.io/docs/guides/basic-auth/
> - **It is your responsibility!**

### Docker

You can either use the automatic builds on
Expand Down
11 changes: 10 additions & 1 deletion pkg/probe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ func ProbeHandler(w http.ResponseWriter, r *http.Request) {
savedConfig := config.GetConfig()

params := r.URL.Query()
paramMap := make(map[string]string)
target := params.Get("target")
paramMap["target"] = params.Get("target")
if params.Get("token") != "" {
paramMap["token"] = params.Get("token")
}
if params.Get("profile") != "" {
paramMap["profile"] = params.Get("profile")
}

if target == "" {
http.Error(w, "Target parameter missing or empty", http.StatusBadRequest)
return
Expand All @@ -37,7 +46,7 @@ func ProbeHandler(w http.ResponseWriter, r *http.Request) {
start := time.Now()
pc := &ProbeCollector{}
registry.MustRegister(pc)
success, err := pc.Probe(ctx, target, &http.Client{}, savedConfig)
success, err := pc.Probe(ctx, paramMap, &http.Client{}, savedConfig)
if err != nil {
log.Printf("Probe request rejected; error is: %v", err)
http.Error(w, fmt.Sprintf("probe: %v", err), http.StatusBadRequest)
Expand Down
12 changes: 10 additions & 2 deletions pkg/probe/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ type probeDetailedFunc struct {
function probeFunc
}

func (p *ProbeCollector) Probe(ctx context.Context, target string, hc *http.Client, savedConfig config.FortiExporterConfig) (bool, error) {
tgt, err := url.Parse(target)
func (p *ProbeCollector) Probe(ctx context.Context, target map[string]string, hc *http.Client, savedConfig config.FortiExporterConfig) (bool, error) {
tgt, err := url.Parse(target["target"])
if err != nil {
return false, fmt.Errorf("url.Parse failed: %v", err)
}
Expand All @@ -62,6 +62,14 @@ func (p *ProbeCollector) Probe(ctx context.Context, target string, hc *http.Clie
Scheme: tgt.Scheme,
Host: tgt.Host,
}

if target["token"] != "" && savedConfig.AuthKeys[config.Target(target["target"])].Token == "" {
// Add the target and its apikey to the savedConfig and use, if exists, a target entry as a template for include/exclude
// This will only happend the "first" time
savedConfig.AuthKeys[config.Target(target["target"])] = config.TargetAuth{Token: config.Token(target["token"]),
Probes: savedConfig.AuthKeys[config.Target(target["profile"])].Probes}
}

c, err := fortiHTTP.NewFortiClient(ctx, u, hc, savedConfig)
if err != nil {
return false, err
Expand Down

0 comments on commit 1007fc1

Please sign in to comment.