lib/promauth: follow-up after 5b8176c68e

This commit is contained in:
Aliaksandr Valialkin 2021-05-22 17:59:23 +03:00
parent 5b8176c68e
commit 78dddfb98f
7 changed files with 194 additions and 136 deletions

View file

@ -669,12 +669,18 @@ See the docs at https://docs.victoriametrics.com/vmagent.html .
-remoteWrite.basicAuth.password array -remoteWrite.basicAuth.password array
Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.basicAuth.passwordFile array
Optional path to basic auth password to use for -remoteWrite.url. The file is re-read every second. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.basicAuth.username array -remoteWrite.basicAuth.username array
Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.bearerToken array -remoteWrite.bearerToken array
Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.bearerTokenFile array
Optional path to bearer token file to use for -remoteWrite.url. The token is re-read from the file every second. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.flushInterval duration -remoteWrite.flushInterval duration
Interval for flushing the data to remote storage. This option takes effect only when less than 10K data points per second are pushed to -remoteWrite.url (default 1s) Interval for flushing the data to remote storage. This option takes effect only when less than 10K data points per second are pushed to -remoteWrite.url (default 1s)
-remoteWrite.label array -remoteWrite.label array
@ -690,6 +696,21 @@ See the docs at https://docs.victoriametrics.com/vmagent.html .
Supports the following optional suffixes for size values: KB, MB, GB, KiB, MiB, GiB (default 0) Supports the following optional suffixes for size values: KB, MB, GB, KiB, MiB, GiB (default 0)
-remoteWrite.maxHourlySeries int -remoteWrite.maxHourlySeries int
The maximum number of unique series vmagent can send to remote storage systems during the last hour. Excess series are logged and dropped. This can be useful for limiting series cardinality. See also -remoteWrite.maxDailySeries The maximum number of unique series vmagent can send to remote storage systems during the last hour. Excess series are logged and dropped. This can be useful for limiting series cardinality. See also -remoteWrite.maxDailySeries
-remoteWrite.oauth2.clientID array
Optional OAuth2 clientID to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.clientSecret array
Optional OAuth2 clientSecret to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.clientSecretFile array
Optional OAuth2 clientSecretFile to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.scopes array
Optional OAuth2 scopes to use for -remoteWrite.url. Scopes must be delimited by ';'. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.tokenUrl array
Optional OAuth2 tokenURL to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.proxyURL array -remoteWrite.proxyURL array
Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234 Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.

View file

@ -2,8 +2,6 @@ package remotewrite
import ( import (
"bytes" "bytes"
"crypto/tls"
"encoding/base64"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -42,25 +40,30 @@ var (
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
basicAuthPassword = flagutil.NewArray("remoteWrite.basicAuth.password", "Optional basic auth password to use for -remoteWrite.url. "+ basicAuthPassword = flagutil.NewArray("remoteWrite.basicAuth.password", "Optional basic auth password to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
basicAuthPasswordFile = flagutil.NewArray("remoteWrite.basicAuth.passwordFile", "Optional path to basic auth password to use for -remoteWrite.url. "+
"The file is re-read every second. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
bearerToken = flagutil.NewArray("remoteWrite.bearerToken", "Optional bearer auth token to use for -remoteWrite.url. "+ bearerToken = flagutil.NewArray("remoteWrite.bearerToken", "Optional bearer auth token to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
bearerTokenFile = flagutil.NewArray("remoteWrite.bearerTokenFile", "Optional path to bearer token file to use for -remoteWrite.url. "+
"The token is re-read from the file every second. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
clientID = flagutil.NewArray("remoteWrite.oauth2.clientID", "Optional OAuth2 clientID to use for -remoteWrite.url."+ oauth2ClientID = flagutil.NewArray("remoteWrite.oauth2.clientID", "Optional OAuth2 clientID to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
clientSecret = flagutil.NewArray("remoteWrite.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -remoteWrite.url."+ oauth2ClientSecret = flagutil.NewArray("remoteWrite.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
clientSecretFile = flagutil.NewArray("remoteWrite.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url."+ oauth2ClientSecretFile = flagutil.NewArray("remoteWrite.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
tokenURL = flagutil.NewArray("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 token url to use for -remoteWrite.url."+ oauth2TokenURL = flagutil.NewArray("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for -remoteWrite.url. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
oAuth2Scopes = flagutil.NewArray("remoteWrite.oauth2.scopes", "Optional OAuth2 scopes to use for -remoteWrite.url."+ oauth2Scopes = flagutil.NewArray("remoteWrite.oauth2.scopes", "Optional OAuth2 scopes to use for -remoteWrite.url. Scopes must be delimited by ';'. "+
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url") "If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
) )
type client struct { type client struct {
sanitizedURL string sanitizedURL string
remoteWriteURL string remoteWriteURL string
authHeader string
fq *persistentqueue.FastQueue fq *persistentqueue.FastQueue
hc *http.Client hc *http.Client
@ -81,11 +84,11 @@ type client struct {
} }
func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqueue.FastQueue, concurrency int) *client { func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqueue.FastQueue, concurrency int) *client {
tlsCfg, err := getTLSConfig(argIdx) authCfg, err := getAuthConfig(argIdx)
if err != nil { if err != nil {
logger.Panicf("FATAL: cannot initialize TLS config: %s", err) logger.Panicf("FATAL: cannot initialize auth config: %s", err)
} }
tlsCfg := authCfg.NewTLSConfig()
tr := &http.Transport{ tr := &http.Transport{
Dial: statDial, Dial: statDial,
TLSClientConfig: tlsCfg, TLSClientConfig: tlsCfg,
@ -106,33 +109,9 @@ func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqu
} }
tr.Proxy = http.ProxyURL(urlProxy) tr.Proxy = http.ProxyURL(urlProxy)
} }
authHeader := ""
username := basicAuthUsername.GetOptionalArg(argIdx)
password := basicAuthPassword.GetOptionalArg(argIdx)
if len(username) > 0 || len(password) > 0 {
// See https://en.wikipedia.org/wiki/Basic_access_authentication
token := username + ":" + password
token64 := base64.StdEncoding.EncodeToString([]byte(token))
authHeader = "Basic " + token64
}
token := bearerToken.GetOptionalArg(argIdx)
if len(token) > 0 {
if authHeader != "" {
logger.Fatalf("`-remoteWrite.bearerToken`=%q cannot be set when `-remoteWrite.basicAuth.*` flags are set", token)
}
authHeader = "Bearer " + token
}
authCfg, err := getAuthConfig(argIdx)
if err != nil {
logger.Fatalf("FATAL: cannot create OAuth2 config for remoteWrite idx: %d, err: %s", argIdx, err)
}
if authCfg != nil && authHeader != "" {
logger.Fatalf("`-remoteWrite.bearerToken`=%q or `-remoteWrite.basicAuth.* cannot be set when `-remoteWrite.oauth2.*` flags are set", token)
}
c := &client{ c := &client{
sanitizedURL: sanitizedURL, sanitizedURL: sanitizedURL,
remoteWriteURL: remoteWriteURL, remoteWriteURL: remoteWriteURL,
authHeader: authHeader,
authCfg: authCfg, authCfg: authCfg,
fq: fq, fq: fq,
hc: &http.Client{ hc: &http.Client{
@ -171,38 +150,44 @@ func (c *client) MustStop() {
logger.Infof("stopped client for -remoteWrite.url=%q", c.sanitizedURL) logger.Infof("stopped client for -remoteWrite.url=%q", c.sanitizedURL)
} }
func getTLSConfig(argIdx int) (*tls.Config, error) { func getAuthConfig(argIdx int) (*promauth.Config, error) {
c := &promauth.TLSConfig{ username := basicAuthUsername.GetOptionalArg(argIdx)
password := basicAuthPassword.GetOptionalArg(argIdx)
passwordFile := basicAuthPasswordFile.GetOptionalArg(argIdx)
var basicAuthCfg *promauth.BasicAuthConfig
if username != "" || password != "" || passwordFile != "" {
basicAuthCfg = &promauth.BasicAuthConfig{
Username: username,
Password: password,
PasswordFile: passwordFile,
}
}
token := bearerToken.GetOptionalArg(argIdx)
tokenFile := bearerTokenFile.GetOptionalArg(argIdx)
var oauth2Cfg *promauth.OAuth2Config
clientSecret := oauth2ClientSecret.GetOptionalArg(argIdx)
clientSecretFile := oauth2ClientSecretFile.GetOptionalArg(argIdx)
if clientSecretFile != "" || clientSecret != "" {
oauth2Cfg = &promauth.OAuth2Config{
ClientID: oauth2ClientID.GetOptionalArg(argIdx),
ClientSecret: clientSecret,
ClientSecretFile: clientSecretFile,
TokenURL: oauth2TokenURL.GetOptionalArg(argIdx),
Scopes: strings.Split(oauth2Scopes.GetOptionalArg(argIdx), ";"),
}
}
tlsCfg := &promauth.TLSConfig{
CAFile: tlsCAFile.GetOptionalArg(argIdx), CAFile: tlsCAFile.GetOptionalArg(argIdx),
CertFile: tlsCertFile.GetOptionalArg(argIdx), CertFile: tlsCertFile.GetOptionalArg(argIdx),
KeyFile: tlsKeyFile.GetOptionalArg(argIdx), KeyFile: tlsKeyFile.GetOptionalArg(argIdx),
ServerName: tlsServerName.GetOptionalArg(argIdx), ServerName: tlsServerName.GetOptionalArg(argIdx),
InsecureSkipVerify: tlsInsecureSkipVerify.GetOptionalArg(argIdx), InsecureSkipVerify: tlsInsecureSkipVerify.GetOptionalArg(argIdx),
} }
if c.CAFile == "" && c.CertFile == "" && c.KeyFile == "" && c.ServerName == "" && !c.InsecureSkipVerify {
return nil, nil
}
cfg, err := promauth.NewConfig(".", nil, nil, "", "", nil, c)
if err != nil {
return nil, fmt.Errorf("cannot populate TLS config: %w", err)
}
tlsCfg := cfg.NewTLSConfig()
return tlsCfg, nil
}
func getAuthConfig(argIdx int) (*promauth.Config, error) { authCfg, err := promauth.NewConfig(".", nil, basicAuthCfg, token, tokenFile, oauth2Cfg, tlsCfg)
oAuth2Cfg := &promauth.OAuth2Config{
ClientID: clientID.GetOptionalArg(argIdx),
ClientSecret: clientSecret.GetOptionalArg(argIdx),
ClientSecretFile: clientSecretFile.GetOptionalArg(argIdx),
TokenURL: tokenURL.GetOptionalArg(argIdx),
Scopes: strings.Split(oAuth2Scopes.GetOptionalArg(argIdx), ";"),
}
if oAuth2Cfg.ClientSecretFile == "" && oAuth2Cfg.ClientSecret == "" {
return nil, nil
}
authCfg, err := promauth.NewConfig("", nil, nil, "", "", oAuth2Cfg, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot populate OAuth2 config for remoteWrite idx: %d, err: %w", argIdx, err) return nil, fmt.Errorf("cannot populate OAuth2 config for remoteWrite idx: %d, err: %w", argIdx, err)
} }
@ -267,13 +252,8 @@ again:
h.Set("Content-Type", "application/x-protobuf") h.Set("Content-Type", "application/x-protobuf")
h.Set("Content-Encoding", "snappy") h.Set("Content-Encoding", "snappy")
h.Set("X-Prometheus-Remote-Write-Version", "0.1.0") h.Set("X-Prometheus-Remote-Write-Version", "0.1.0")
if c.authHeader != "" { if ah := c.authCfg.GetAuthHeader(); ah != "" {
req.Header.Set("Authorization", c.authHeader) req.Header.Set("Authorization", ah)
}
// add oauth2 header on best effort.
// remote storage may return error with incorrect authorization.
if c.authCfg != nil {
req.Header.Set("Authorization", c.authCfg.GetAuthHeader())
} }
startTime := time.Now() startTime := time.Now()

View file

@ -20,6 +20,8 @@ sort: 15
* FEATURE: automatically detect memory and cpu limits for VictoriaMetrics components running under [cgroup v2](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) environments such as [HashiCorp Nomad](https://www.nomadproject.io/). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1269). * FEATURE: automatically detect memory and cpu limits for VictoriaMetrics components running under [cgroup v2](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) environments such as [HashiCorp Nomad](https://www.nomadproject.io/). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1269).
* FEATURE: vmauth: allow `-auth.config` reloading via `/-/reload` http endpoint. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1194). * FEATURE: vmauth: allow `-auth.config` reloading via `/-/reload` http endpoint. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1194).
* FEATURE: add `timezone_offset(tz)` function. It returns offset in seconds for the given timezone `tz` relative to UTC. This can be useful when combining with datetime-related functions. For example, `day_of_week(time()+timezone_offset("America/Los_Angeles"))` would return weekdays for `America/Los_Angeles` time zone. Special `Local` time zone can be used for returning an offset for the time zone set on the host where VictoriaMetrics runs. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1306) and [MetricsQL docs](https://docs.victoriametrics.com/MetricsQL.html) for more details. * FEATURE: add `timezone_offset(tz)` function. It returns offset in seconds for the given timezone `tz` relative to UTC. This can be useful when combining with datetime-related functions. For example, `day_of_week(time()+timezone_offset("America/Los_Angeles"))` would return weekdays for `America/Los_Angeles` time zone. Special `Local` time zone can be used for returning an offset for the time zone set on the host where VictoriaMetrics runs. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1306) and [MetricsQL docs](https://docs.victoriametrics.com/MetricsQL.html) for more details.
* FEATURE: vmagent: add support for OAuth2 authorization for scrape targets and service discovery in the same way as Prometheus does. See [these docs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#oauth2).
* FEATURE: vmagent: add support for OAuth2 authorization when writing data to `-remoteWrite.url`. See `-remoteWrite.oauth2.*` config params in `/path/to/vmagent -help` output.
* BUGFIX: vmagent: do not retry scraping targets, which don't support HTTP. This should reduce CPU load and network usage at `vmagent` and at scrape target. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1289). * BUGFIX: vmagent: do not retry scraping targets, which don't support HTTP. This should reduce CPU load and network usage at `vmagent` and at scrape target. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1289).
* BUGFIX: vmagent: fix possible race when refreshing `role: endpoints` and `role: endpointslices` scrape targets in `kubernetes_sd_config`. Prevoiusly `pod` objects could be updated after the related `endpoints` object update. This could lead to missing scrape targets. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240). * BUGFIX: vmagent: fix possible race when refreshing `role: endpoints` and `role: endpointslices` scrape targets in `kubernetes_sd_config`. Prevoiusly `pod` objects could be updated after the related `endpoints` object update. This could lead to missing scrape targets. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240).

View file

@ -673,12 +673,18 @@ See the docs at https://docs.victoriametrics.com/vmagent.html .
-remoteWrite.basicAuth.password array -remoteWrite.basicAuth.password array
Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.basicAuth.passwordFile array
Optional path to basic auth password to use for -remoteWrite.url. The file is re-read every second. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.basicAuth.username array -remoteWrite.basicAuth.username array
Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.bearerToken array -remoteWrite.bearerToken array
Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.bearerTokenFile array
Optional path to bearer token file to use for -remoteWrite.url. The token is re-read from the file every second. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.flushInterval duration -remoteWrite.flushInterval duration
Interval for flushing the data to remote storage. This option takes effect only when less than 10K data points per second are pushed to -remoteWrite.url (default 1s) Interval for flushing the data to remote storage. This option takes effect only when less than 10K data points per second are pushed to -remoteWrite.url (default 1s)
-remoteWrite.label array -remoteWrite.label array
@ -694,6 +700,21 @@ See the docs at https://docs.victoriametrics.com/vmagent.html .
Supports the following optional suffixes for size values: KB, MB, GB, KiB, MiB, GiB (default 0) Supports the following optional suffixes for size values: KB, MB, GB, KiB, MiB, GiB (default 0)
-remoteWrite.maxHourlySeries int -remoteWrite.maxHourlySeries int
The maximum number of unique series vmagent can send to remote storage systems during the last hour. Excess series are logged and dropped. This can be useful for limiting series cardinality. See also -remoteWrite.maxDailySeries The maximum number of unique series vmagent can send to remote storage systems during the last hour. Excess series are logged and dropped. This can be useful for limiting series cardinality. See also -remoteWrite.maxDailySeries
-remoteWrite.oauth2.clientID array
Optional OAuth2 clientID to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.clientSecret array
Optional OAuth2 clientSecret to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.clientSecretFile array
Optional OAuth2 clientSecretFile to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.scopes array
Optional OAuth2 scopes to use for -remoteWrite.url. Scopes must be delimited by ';'. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.oauth2.tokenUrl array
Optional OAuth2 tokenURL to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
Supports an array of values separated by comma or specified via multiple flags.
-remoteWrite.proxyURL array -remoteWrite.proxyURL array
Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234 Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234
Supports an array of values separated by comma or specified via multiple flags. Supports an array of values separated by comma or specified via multiple flags.

4
go.sum
View file

@ -533,7 +533,7 @@ github.com/influxdata/flux v0.113.0/go.mod h1:3TJtvbm/Kwuo5/PEo5P6HUzwVg4bXWkb2w
github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA= github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA=
github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ=
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
github.com/influxdata/influxdb v1.9.0 h1:KefL3i2JdNgsZwKRlHkkV+sYs4ejJ+aGF6glBUoKKio= github.com/influxdata/influxdb v1.9.0 h1:9z/aRmTpWT1rIm4EN+qTJTZqgEdLGZ4xRMgvA276UEA=
github.com/influxdata/influxdb v1.9.0/go.mod h1:UEe3MeD9AaP5rlPIes102IhYua3FhIWZuOXNHxDjSrI= github.com/influxdata/influxdb v1.9.0/go.mod h1:UEe3MeD9AaP5rlPIes102IhYua3FhIWZuOXNHxDjSrI=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo= github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=
@ -1031,8 +1031,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=

View file

@ -8,6 +8,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"sync" "sync"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
@ -64,65 +65,93 @@ type ProxyClientConfig struct {
// OAuth2Config represent OAuth2 configuration // OAuth2Config represent OAuth2 configuration
type OAuth2Config struct { type OAuth2Config struct {
ClientID string `yaml:"client_id"` ClientID string `yaml:"client_id"`
ClientSecretFile string `yaml:"client_secret_file"` ClientSecret string `yaml:"client_secret"`
Scopes []string `yaml:"scopes"` ClientSecretFile string `yaml:"client_secret_file"`
TokenURL string `yaml:"token_url"` Scopes []string `yaml:"scopes"`
// mu guards tokenSource and client Secret TokenURL string `yaml:"token_url"`
mu sync.Mutex EndpointParams map[string]string `yaml:"endpoint_params"`
ClientSecret string `yaml:"client_secret"`
tokenSource oauth2.TokenSource
} }
func (o *OAuth2Config) refreshTokenSourceLocked() { // String returns string representation of o.
cfg := clientcredentials.Config{ func (o *OAuth2Config) String() string {
ClientID: o.ClientID, return fmt.Sprintf("clientID=%q, clientSecret=%q, clientSecretFile=%q, Scopes=%q, tokenURL=%q, endpointParams=%q",
ClientSecret: o.ClientSecret, o.ClientID, o.ClientSecret, o.ClientSecretFile, o.Scopes, o.TokenURL, o.EndpointParams)
TokenURL: o.TokenURL,
Scopes: o.Scopes,
}
o.tokenSource = cfg.TokenSource(context.Background())
} }
// validate checks given configs.
func (o *OAuth2Config) validate() error { func (o *OAuth2Config) validate() error {
if o.TokenURL == "" { if o.ClientID == "" {
return fmt.Errorf("token url cannot be empty") return fmt.Errorf("client_id cannot be empty")
} }
if o.ClientSecret == "" && o.ClientSecretFile == "" { if o.ClientSecret == "" && o.ClientSecretFile == "" {
return fmt.Errorf("ClientSecret or ClientSecretFile must be set") return fmt.Errorf("ClientSecret or ClientSecretFile must be set")
} }
if o.ClientSecret != "" && o.ClientSecretFile != "" { if o.ClientSecret != "" && o.ClientSecretFile != "" {
return fmt.Errorf("only one option can be set ClientSecret or ClientSecretFile, provided both") return fmt.Errorf("ClientSecret and ClientSecretFile cannot be set simultaneously")
}
if o.TokenURL == "" {
return fmt.Errorf("token_url cannot be empty")
} }
return nil return nil
} }
func (o *OAuth2Config) getAuthHeader() (string, error) { type oauth2ConfigInternal struct {
var needUpdate bool mu sync.Mutex
if o.ClientSecretFile != "" { cfg *clientcredentials.Config
newSecret, err := readPasswordFromFile(o.ClientSecretFile) clientSecretFile string
if err != nil { tokenSource oauth2.TokenSource
return "", fmt.Errorf("cannot read OAuth2 config file with path: %s, err: %w", o.ClientSecretFile, err) }
}
o.mu.Lock()
if o.ClientSecret != newSecret {
o.ClientSecret = newSecret
needUpdate = true
}
o.mu.Unlock()
}
o.mu.Lock()
defer o.mu.Unlock()
if needUpdate {
o.refreshTokenSourceLocked()
}
t, err := o.tokenSource.Token()
if err != nil {
return "", fmt.Errorf("cannot fetch token for OAuth2 client: %w", err)
}
return t.Type() + " " + t.AccessToken, nil func newOAuth2ConfigInternal(baseDir string, o *OAuth2Config) (*oauth2ConfigInternal, error) {
if err := o.validate(); err != nil {
return nil, err
}
oi := &oauth2ConfigInternal{
cfg: &clientcredentials.Config{
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
TokenURL: o.TokenURL,
Scopes: o.Scopes,
EndpointParams: urlValuesFromMap(o.EndpointParams),
},
}
if o.ClientSecretFile != "" {
oi.clientSecretFile = getFilepath(baseDir, o.ClientSecretFile)
secret, err := readPasswordFromFile(oi.clientSecretFile)
if err != nil {
return nil, fmt.Errorf("cannot read OAuth2 secret from %q: %w", oi.clientSecretFile, err)
}
oi.cfg.ClientSecret = secret
}
oi.tokenSource = oi.cfg.TokenSource(context.Background())
return oi, nil
}
func urlValuesFromMap(m map[string]string) url.Values {
result := make(url.Values, len(m))
for k, v := range m {
result[k] = []string{v}
}
return result
}
func (oi *oauth2ConfigInternal) getTokenSource() (oauth2.TokenSource, error) {
oi.mu.Lock()
defer oi.mu.Unlock()
if oi.clientSecretFile == "" {
return oi.tokenSource, nil
}
newSecret, err := readPasswordFromFile(oi.clientSecretFile)
if err != nil {
return nil, fmt.Errorf("cannot read OAuth2 secret from %q: %w", oi.clientSecretFile, err)
}
if newSecret == oi.cfg.ClientSecret {
return oi.tokenSource, nil
}
oi.cfg.ClientSecret = newSecret
oi.tokenSource = oi.cfg.TokenSource(context.Background())
return oi.tokenSource, nil
} }
// Config is auth config. // Config is auth config.
@ -207,7 +236,7 @@ func (pcc *ProxyClientConfig) NewConfig(baseDir string) (*Config, error) {
} }
// NewConfig creates auth config from the given args. // NewConfig creates auth config from the given args.
func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, oauth *OAuth2Config, tlsConfig *TLSConfig) (*Config, error) { func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, o *OAuth2Config, tlsConfig *TLSConfig) (*Config, error) {
var getAuthHeader func() string var getAuthHeader func() string
authDigest := "" authDigest := ""
if az != nil { if az != nil {
@ -297,29 +326,28 @@ func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, be
} }
authDigest = fmt.Sprintf("bearer(token=%q)", bearerToken) authDigest = fmt.Sprintf("bearer(token=%q)", bearerToken)
} }
if oauth != nil { if o != nil {
if getAuthHeader != nil { if getAuthHeader != nil {
return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`") return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`")
} }
if err := oauth.validate(); err != nil { oi, err := newOAuth2ConfigInternal(baseDir, o)
if err != nil {
return nil, err return nil, err
} }
if oauth.ClientSecretFile != "" {
secret, err := readPasswordFromFile(oauth.ClientSecretFile)
if err != nil {
return nil, err
}
oauth.ClientSecret = secret
}
oauth.refreshTokenSourceLocked()
getAuthHeader = func() string { getAuthHeader = func() string {
h, err := oauth.getAuthHeader() ts, err := oi.getTokenSource()
if err != nil { if err != nil {
logger.Errorf("cannot get OAuth2 header: %s", err) logger.Errorf("cannot get OAuth2 tokenSource: %s", err)
return "" return ""
} }
return h t, err := ts.Token()
if err != nil {
logger.Errorf("cannot get OAuth2 token: %s", err)
return ""
}
return t.Type() + " " + t.AccessToken
} }
authDigest = fmt.Sprintf("oauth2(%s)", o.String())
} }
var tlsRootCA *x509.CertPool var tlsRootCA *x509.CertPool
var tlsCertificate *tls.Certificate var tlsCertificate *tls.Certificate

View file

@ -17,9 +17,10 @@ func TestNewConfig(t *testing.T) {
tlsConfig *TLSConfig tlsConfig *TLSConfig
} }
tests := []struct { tests := []struct {
name string name string
args args args args
wantErr bool wantErr bool
expectHeader string
}{ }{
{ {
name: "OAuth2 config", name: "OAuth2 config",
@ -30,6 +31,7 @@ func TestNewConfig(t *testing.T) {
TokenURL: "http://localhost:8511", TokenURL: "http://localhost:8511",
}, },
}, },
expectHeader: "Bearer some-token",
}, },
{ {
name: "OAuth2 config with file", name: "OAuth2 config with file",
@ -40,8 +42,8 @@ func TestNewConfig(t *testing.T) {
TokenURL: "http://localhost:8511", TokenURL: "http://localhost:8511",
}, },
}, },
expectHeader: "Bearer some-token",
}, },
{ {
name: "OAuth2 want err", name: "OAuth2 want err",
args: args{ args: args{
@ -62,6 +64,7 @@ func TestNewConfig(t *testing.T) {
Password: "password", Password: "password",
}, },
}, },
expectHeader: "Basic dXNlcjpwYXNzd29yZA==",
}, },
{ {
name: "basic Auth config with file", name: "basic Auth config with file",
@ -71,21 +74,24 @@ func TestNewConfig(t *testing.T) {
PasswordFile: "testdata/test_secretfile.txt", PasswordFile: "testdata/test_secretfile.txt",
}, },
}, },
expectHeader: "Basic dXNlcjpzZWNyZXQtY29udGVudA==",
}, },
{ {
name: "want Authorization", name: "want Authorization",
args: args{ args: args{
az: &Authorization{ az: &Authorization{
Type: "Bearer ", Type: "Bearer",
Credentials: "Value", Credentials: "Value",
}, },
}, },
expectHeader: "Bearer Value",
}, },
{ {
name: "token file", name: "token file",
args: args{ args: args{
bearerTokenFile: "testdata/test_secretfile.txt", bearerTokenFile: "testdata/test_secretfile.txt",
}, },
expectHeader: "Bearer secret-content",
}, },
{ {
name: "token with tls", name: "token with tls",
@ -95,6 +101,7 @@ func TestNewConfig(t *testing.T) {
InsecureSkipVerify: true, InsecureSkipVerify: true,
}, },
}, },
expectHeader: "Bearer some-token",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -103,7 +110,7 @@ func TestNewConfig(t *testing.T) {
r := http.NewServeMux() r := http.NewServeMux()
r.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { r.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"access_token":"some-token","token_type": "Bearer "}`)) w.Write([]byte(`{"access_token":"some-token","token_type": "Bearer"}`))
}) })
mock := httptest.NewServer(r) mock := httptest.NewServer(r)
@ -115,8 +122,9 @@ func TestNewConfig(t *testing.T) {
return return
} }
if got != nil { if got != nil {
if ah := got.GetAuthHeader(); ah == "" { ah := got.GetAuthHeader()
t.Fatalf("unexpected empty auth header") if ah != tt.expectHeader {
t.Fatalf("unexpected auth header; got %q; want %q", ah, tt.expectHeader)
} }
} }