From 5ee3ec5dbd0443af9ea2b870c6035c6a017d7d48 Mon Sep 17 00:00:00 2001 From: Jean-Louis Dupond Date: Tue, 17 Jul 2018 11:12:46 +0200 Subject: [PATCH] Add ip restriction for the prometheus_client output Sometimes you want to protect the exported /metrics path from the prometheus_client. It was already possible to secure it with a password. This patch adds the option to allow only defined CIDR ranges to access the /metrics. --- etc/telegraf.conf | 3 ++ plugins/outputs/prometheus_client/README.md | 3 ++ .../prometheus_client/prometheus_client.go | 30 +++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/etc/telegraf.conf b/etc/telegraf.conf index 38942adee0a6a..a11dbe8ed951a 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -747,6 +747,9 @@ # #basic_username = "Foo" # #basic_password = "Bar" # +# ## IP Ranges which are allowed to access metrics +# #ip_range = ["192.168.0.0/24", "192.168.1.0/30"] +# # ## Interval to expire metrics and not deliver to prometheus, 0 == no expiration # # expiration_interval = "60s" # diff --git a/plugins/outputs/prometheus_client/README.md b/plugins/outputs/prometheus_client/README.md index 6cb0cc59e1817..d68cafe9d7899 100644 --- a/plugins/outputs/prometheus_client/README.md +++ b/plugins/outputs/prometheus_client/README.md @@ -18,6 +18,9 @@ This plugin starts a [Prometheus](https://prometheus.io/) Client, it exposes all basic_username = "Foo" basic_password = "Bar" + # IP Ranges which are allowed to access metrics + ip_range = ["192.168.0.0/24", "192.168.1.0/30"] + # Path to publish the metrics on, defaults to /metrics path = "/metrics" diff --git a/plugins/outputs/prometheus_client/prometheus_client.go b/plugins/outputs/prometheus_client/prometheus_client.go index b82c72cf05810..9634e92275ece 100644 --- a/plugins/outputs/prometheus_client/prometheus_client.go +++ b/plugins/outputs/prometheus_client/prometheus_client.go @@ -5,6 +5,7 @@ import ( "crypto/subtle" "fmt" "log" + "net" "net/http" "os" "regexp" @@ -58,6 +59,7 @@ type PrometheusClient struct { TLSKey string `toml:"tls_key"` BasicUsername string `toml:"basic_username"` BasicPassword string `toml:"basic_password"` + IPRange []string `toml:"ip_range"` ExpirationInterval internal.Duration `toml:"expiration_interval"` Path string `toml:"path"` CollectorsExclude []string `toml:"collectors_exclude"` @@ -84,6 +86,9 @@ var sampleConfig = ` #basic_username = "Foo" #basic_password = "Bar" + ## IP Ranges which are allowed to access metrics + #ip_range = ["192.168.0.0/24", "192.168.1.0/30"] + ## Interval to expire metrics and not deliver to prometheus, 0 == no expiration # expiration_interval = "60s" @@ -96,7 +101,7 @@ var sampleConfig = ` string_as_label = true ` -func (p *PrometheusClient) basicAuth(h http.Handler) http.Handler { +func (p *PrometheusClient) auth(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if p.BasicUsername != "" && p.BasicPassword != "" { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) @@ -110,6 +115,27 @@ func (p *PrometheusClient) basicAuth(h http.Handler) http.Handler { } } + if len(p.IPRange) > 0 { + matched := false + remoteIPs, _, _ := net.SplitHostPort(r.RemoteAddr) + remoteIP := net.ParseIP(remoteIPs) + for _, iprange := range p.IPRange { + _, ipNet, err := net.ParseCIDR(iprange) + if err != nil { + http.Error(w, "Config Error in ip_range setting", 500) + return + } + if ipNet.Contains(remoteIP) { + matched = true + break + } + } + if !matched { + http.Error(w, "Not authorized", 401) + return + } + } + h.ServeHTTP(w, r) }) } @@ -146,7 +172,7 @@ func (p *PrometheusClient) Start() error { } mux := http.NewServeMux() - mux.Handle(p.Path, p.basicAuth(promhttp.HandlerFor( + mux.Handle(p.Path, p.auth(promhttp.HandlerFor( registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}))) p.server = &http.Server{