mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
basic OAuth2 support for remoteWrite and scrape targets (#1316)
* adds OAuth2 support for remoteWrite and scrapping * adds tests changes init
This commit is contained in:
parent
e05dd475f0
commit
5b8176c68e
11 changed files with 396 additions and 10 deletions
|
@ -44,6 +44,17 @@ 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")
|
||||||
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")
|
||||||
|
|
||||||
|
clientID = 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")
|
||||||
|
clientSecret = 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")
|
||||||
|
clientSecretFile = 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")
|
||||||
|
tokenURL = flagutil.NewArray("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 token url to use for -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."+
|
||||||
|
"If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url")
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
|
@ -53,6 +64,8 @@ type client struct {
|
||||||
fq *persistentqueue.FastQueue
|
fq *persistentqueue.FastQueue
|
||||||
hc *http.Client
|
hc *http.Client
|
||||||
|
|
||||||
|
authCfg *promauth.Config
|
||||||
|
|
||||||
rl rateLimiter
|
rl rateLimiter
|
||||||
|
|
||||||
bytesSent *metrics.Counter
|
bytesSent *metrics.Counter
|
||||||
|
@ -72,6 +85,7 @@ func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqu
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Panicf("FATAL: cannot initialize TLS config: %s", err)
|
logger.Panicf("FATAL: cannot initialize TLS config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
Dial: statDial,
|
Dial: statDial,
|
||||||
TLSClientConfig: tlsCfg,
|
TLSClientConfig: tlsCfg,
|
||||||
|
@ -108,10 +122,18 @@ func newClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persistentqu
|
||||||
}
|
}
|
||||||
authHeader = "Bearer " + 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,
|
authHeader: authHeader,
|
||||||
|
authCfg: authCfg,
|
||||||
fq: fq,
|
fq: fq,
|
||||||
hc: &http.Client{
|
hc: &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
|
@ -160,7 +182,7 @@ func getTLSConfig(argIdx int) (*tls.Config, error) {
|
||||||
if c.CAFile == "" && c.CertFile == "" && c.KeyFile == "" && c.ServerName == "" && !c.InsecureSkipVerify {
|
if c.CAFile == "" && c.CertFile == "" && c.KeyFile == "" && c.ServerName == "" && !c.InsecureSkipVerify {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
cfg, err := promauth.NewConfig(".", nil, nil, "", "", c)
|
cfg, err := promauth.NewConfig(".", nil, nil, "", "", nil, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot populate TLS config: %w", err)
|
return nil, fmt.Errorf("cannot populate TLS config: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -168,6 +190,25 @@ func getTLSConfig(argIdx int) (*tls.Config, error) {
|
||||||
return tlsCfg, nil
|
return tlsCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAuthConfig(argIdx int) (*promauth.Config, error) {
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return nil, fmt.Errorf("cannot populate OAuth2 config for remoteWrite idx: %d, err: %w", argIdx, err)
|
||||||
|
}
|
||||||
|
return authCfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) runWorker() {
|
func (c *client) runWorker() {
|
||||||
var ok bool
|
var ok bool
|
||||||
var block []byte
|
var block []byte
|
||||||
|
@ -229,6 +270,11 @@ again:
|
||||||
if c.authHeader != "" {
|
if c.authHeader != "" {
|
||||||
req.Header.Set("Authorization", c.authHeader)
|
req.Header.Set("Authorization", c.authHeader)
|
||||||
}
|
}
|
||||||
|
// 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()
|
||||||
resp, err := c.hc.Do(req)
|
resp, err := c.hc.Do(req)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -31,7 +31,7 @@ require (
|
||||||
github.com/valyala/gozstd v1.10.0
|
github.com/valyala/gozstd v1.10.0
|
||||||
github.com/valyala/histogram v1.1.2
|
github.com/valyala/histogram v1.1.2
|
||||||
github.com/valyala/quicktemplate v1.6.3
|
github.com/valyala/quicktemplate v1.6.3
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015
|
||||||
google.golang.org/api v0.47.0
|
google.golang.org/api v0.47.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -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:9z/aRmTpWT1rIm4EN+qTJTZqgEdLGZ4xRMgvA276UEA=
|
github.com/influxdata/influxdb v1.9.0 h1:KefL3i2JdNgsZwKRlHkkV+sYs4ejJ+aGF6glBUoKKio=
|
||||||
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=
|
||||||
|
@ -1033,6 +1033,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||||
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 h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
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/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=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
|
@ -2,6 +2,7 @@ package promauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -11,6 +12,8 @@ import (
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TLSConfig represents TLS config.
|
// TLSConfig represents TLS config.
|
||||||
|
@ -46,6 +49,7 @@ type HTTPClientConfig struct {
|
||||||
BasicAuth *BasicAuthConfig `yaml:"basic_auth,omitempty"`
|
BasicAuth *BasicAuthConfig `yaml:"basic_auth,omitempty"`
|
||||||
BearerToken string `yaml:"bearer_token,omitempty"`
|
BearerToken string `yaml:"bearer_token,omitempty"`
|
||||||
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
|
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
|
||||||
|
OAuth2 *OAuth2Config `yaml:"oauth2,omitempty"`
|
||||||
TLSConfig *TLSConfig `yaml:"tls_config,omitempty"`
|
TLSConfig *TLSConfig `yaml:"tls_config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +62,69 @@ type ProxyClientConfig struct {
|
||||||
TLSConfig *TLSConfig `yaml:"proxy_tls_config,omitempty"`
|
TLSConfig *TLSConfig `yaml:"proxy_tls_config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OAuth2Config represent OAuth2 configuration
|
||||||
|
type OAuth2Config struct {
|
||||||
|
ClientID string `yaml:"client_id"`
|
||||||
|
ClientSecretFile string `yaml:"client_secret_file"`
|
||||||
|
Scopes []string `yaml:"scopes"`
|
||||||
|
TokenURL string `yaml:"token_url"`
|
||||||
|
// mu guards tokenSource and client Secret
|
||||||
|
mu sync.Mutex
|
||||||
|
ClientSecret string `yaml:"client_secret"`
|
||||||
|
tokenSource oauth2.TokenSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OAuth2Config) refreshTokenSourceLocked() {
|
||||||
|
cfg := clientcredentials.Config{
|
||||||
|
ClientID: o.ClientID,
|
||||||
|
ClientSecret: o.ClientSecret,
|
||||||
|
TokenURL: o.TokenURL,
|
||||||
|
Scopes: o.Scopes,
|
||||||
|
}
|
||||||
|
o.tokenSource = cfg.TokenSource(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate checks given configs.
|
||||||
|
func (o *OAuth2Config) validate() error {
|
||||||
|
if o.TokenURL == "" {
|
||||||
|
return fmt.Errorf("token url cannot be empty")
|
||||||
|
}
|
||||||
|
if o.ClientSecret == "" && o.ClientSecretFile == "" {
|
||||||
|
return fmt.Errorf("ClientSecret or ClientSecretFile must be set")
|
||||||
|
}
|
||||||
|
if o.ClientSecret != "" && o.ClientSecretFile != "" {
|
||||||
|
return fmt.Errorf("only one option can be set ClientSecret or ClientSecretFile, provided both")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OAuth2Config) getAuthHeader() (string, error) {
|
||||||
|
var needUpdate bool
|
||||||
|
if o.ClientSecretFile != "" {
|
||||||
|
newSecret, err := readPasswordFromFile(o.ClientSecretFile)
|
||||||
|
if err != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// Config is auth config.
|
// Config is auth config.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Optional TLS config
|
// Optional TLS config
|
||||||
|
@ -131,16 +198,16 @@ func (ac *Config) NewTLSConfig() *tls.Config {
|
||||||
|
|
||||||
// NewConfig creates auth config for the given hcc.
|
// NewConfig creates auth config for the given hcc.
|
||||||
func (hcc *HTTPClientConfig) NewConfig(baseDir string) (*Config, error) {
|
func (hcc *HTTPClientConfig) NewConfig(baseDir string) (*Config, error) {
|
||||||
return NewConfig(baseDir, hcc.Authorization, hcc.BasicAuth, hcc.BearerToken, hcc.BearerTokenFile, hcc.TLSConfig)
|
return NewConfig(baseDir, hcc.Authorization, hcc.BasicAuth, hcc.BearerToken, hcc.BearerTokenFile, hcc.OAuth2, hcc.TLSConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates auth config for the given pcc.
|
// NewConfig creates auth config for the given pcc.
|
||||||
func (pcc *ProxyClientConfig) NewConfig(baseDir string) (*Config, error) {
|
func (pcc *ProxyClientConfig) NewConfig(baseDir string) (*Config, error) {
|
||||||
return NewConfig(baseDir, pcc.Authorization, pcc.BasicAuth, pcc.BearerToken, pcc.BearerTokenFile, pcc.TLSConfig)
|
return NewConfig(baseDir, pcc.Authorization, pcc.BasicAuth, pcc.BearerToken, pcc.BearerTokenFile, nil, pcc.TLSConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, tlsConfig *TLSConfig) (*Config, error) {
|
func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, oauth *OAuth2Config, tlsConfig *TLSConfig) (*Config, error) {
|
||||||
var getAuthHeader func() string
|
var getAuthHeader func() string
|
||||||
authDigest := ""
|
authDigest := ""
|
||||||
if az != nil {
|
if az != nil {
|
||||||
|
@ -230,6 +297,30 @@ 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 getAuthHeader != nil {
|
||||||
|
return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`")
|
||||||
|
}
|
||||||
|
if err := oauth.validate(); err != nil {
|
||||||
|
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 {
|
||||||
|
h, err := oauth.getAuthHeader()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("cannot get OAuth2 header: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
}
|
||||||
var tlsRootCA *x509.CertPool
|
var tlsRootCA *x509.CertPool
|
||||||
var tlsCertificate *tls.Certificate
|
var tlsCertificate *tls.Certificate
|
||||||
tlsServerName := ""
|
tlsServerName := ""
|
||||||
|
|
125
lib/promauth/config_test.go
Normal file
125
lib/promauth/config_test.go
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package promauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewConfig(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
baseDir string
|
||||||
|
az *Authorization
|
||||||
|
basicAuth *BasicAuthConfig
|
||||||
|
bearerToken string
|
||||||
|
bearerTokenFile string
|
||||||
|
oauth *OAuth2Config
|
||||||
|
tlsConfig *TLSConfig
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OAuth2 config",
|
||||||
|
args: args{
|
||||||
|
oauth: &OAuth2Config{
|
||||||
|
ClientID: "some-id",
|
||||||
|
ClientSecret: "some-secret",
|
||||||
|
TokenURL: "http://localhost:8511",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OAuth2 config with file",
|
||||||
|
args: args{
|
||||||
|
oauth: &OAuth2Config{
|
||||||
|
ClientID: "some-id",
|
||||||
|
ClientSecretFile: "testdata/test_secretfile.txt",
|
||||||
|
TokenURL: "http://localhost:8511",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "OAuth2 want err",
|
||||||
|
args: args{
|
||||||
|
oauth: &OAuth2Config{
|
||||||
|
ClientID: "some-id",
|
||||||
|
ClientSecret: "some-secret",
|
||||||
|
ClientSecretFile: "testdata/test_secretfile.txt",
|
||||||
|
TokenURL: "http://localhost:8511",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic Auth config",
|
||||||
|
args: args{
|
||||||
|
basicAuth: &BasicAuthConfig{
|
||||||
|
Username: "user",
|
||||||
|
Password: "password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic Auth config with file",
|
||||||
|
args: args{
|
||||||
|
basicAuth: &BasicAuthConfig{
|
||||||
|
Username: "user",
|
||||||
|
PasswordFile: "testdata/test_secretfile.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "want Authorization",
|
||||||
|
args: args{
|
||||||
|
az: &Authorization{
|
||||||
|
Type: "Bearer ",
|
||||||
|
Credentials: "Value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "token file",
|
||||||
|
args: args{
|
||||||
|
bearerTokenFile: "testdata/test_secretfile.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "token with tls",
|
||||||
|
args: args{
|
||||||
|
bearerToken: "some-token",
|
||||||
|
tlsConfig: &TLSConfig{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.args.oauth != nil {
|
||||||
|
r := http.NewServeMux()
|
||||||
|
r.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write([]byte(`{"access_token":"some-token","token_type": "Bearer "}`))
|
||||||
|
|
||||||
|
})
|
||||||
|
mock := httptest.NewServer(r)
|
||||||
|
tt.args.oauth.TokenURL = mock.URL
|
||||||
|
}
|
||||||
|
got, err := NewConfig(tt.args.baseDir, tt.args.az, tt.args.basicAuth, tt.args.bearerToken, tt.args.bearerTokenFile, tt.args.oauth, tt.args.tlsConfig)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("NewConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != nil {
|
||||||
|
if ah := got.GetAuthHeader(); ah == "" {
|
||||||
|
t.Fatalf("unexpected empty auth header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
1
lib/promauth/testdata/test_secretfile.txt
vendored
Normal file
1
lib/promauth/testdata/test_secretfile.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
secret-content
|
|
@ -50,7 +50,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||||
}
|
}
|
||||||
token = ""
|
token = ""
|
||||||
}
|
}
|
||||||
ac, err := promauth.NewConfig(baseDir, nil, ba, token, "", sdc.TLSConfig)
|
ac, err := promauth.NewConfig(baseDir, nil, ba, token, "", nil, sdc.TLSConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot parse auth config: %w", err)
|
return nil, fmt.Errorf("cannot parse auth config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string, swcFunc ScrapeWorkConstructorFu
|
||||||
tlsConfig := promauth.TLSConfig{
|
tlsConfig := promauth.TLSConfig{
|
||||||
CAFile: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
|
CAFile: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
|
||||||
}
|
}
|
||||||
acNew, err := promauth.NewConfig(".", nil, nil, "", "/var/run/secrets/kubernetes.io/serviceaccount/token", &tlsConfig)
|
acNew, err := promauth.NewConfig(".", nil, nil, "", "/var/run/secrets/kubernetes.io/serviceaccount/token", nil, &tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot initialize service account auth: %w; probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?", err)
|
return nil, fmt.Errorf("cannot initialize service account auth: %w; probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
|
||||||
port: sdc.Port,
|
port: sdc.Port,
|
||||||
}
|
}
|
||||||
if sdc.TLSConfig != nil {
|
if sdc.TLSConfig != nil {
|
||||||
ac, err := promauth.NewConfig(baseDir, nil, nil, "", "", sdc.TLSConfig)
|
ac, err := promauth.NewConfig(baseDir, nil, nil, "", "", nil, sdc.TLSConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
120
vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go
generated
vendored
Normal file
120
vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package clientcredentials implements the OAuth2.0 "client credentials" token flow,
|
||||||
|
// also known as the "two-legged OAuth 2.0".
|
||||||
|
//
|
||||||
|
// This should be used when the client is acting on its own behalf or when the client
|
||||||
|
// is the resource owner. It may also be used when requesting access to protected
|
||||||
|
// resources based on an authorization previously arranged with the authorization
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
// See https://tools.ietf.org/html/rfc6749#section-4.4
|
||||||
|
package clientcredentials // import "golang.org/x/oauth2/clientcredentials"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config describes a 2-legged OAuth2 flow, with both the
|
||||||
|
// client application information and the server's endpoint URLs.
|
||||||
|
type Config struct {
|
||||||
|
// ClientID is the application's ID.
|
||||||
|
ClientID string
|
||||||
|
|
||||||
|
// ClientSecret is the application's secret.
|
||||||
|
ClientSecret string
|
||||||
|
|
||||||
|
// TokenURL is the resource server's token endpoint
|
||||||
|
// URL. This is a constant specific to each server.
|
||||||
|
TokenURL string
|
||||||
|
|
||||||
|
// Scope specifies optional requested permissions.
|
||||||
|
Scopes []string
|
||||||
|
|
||||||
|
// EndpointParams specifies additional parameters for requests to the token endpoint.
|
||||||
|
EndpointParams url.Values
|
||||||
|
|
||||||
|
// AuthStyle optionally specifies how the endpoint wants the
|
||||||
|
// client ID & client secret sent. The zero value means to
|
||||||
|
// auto-detect.
|
||||||
|
AuthStyle oauth2.AuthStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token uses client credentials to retrieve a token.
|
||||||
|
//
|
||||||
|
// The provided context optionally controls which HTTP client is used. See the oauth2.HTTPClient variable.
|
||||||
|
func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) {
|
||||||
|
return c.TokenSource(ctx).Token()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client returns an HTTP client using the provided token.
|
||||||
|
// The token will auto-refresh as necessary.
|
||||||
|
//
|
||||||
|
// The provided context optionally controls which HTTP client
|
||||||
|
// is returned. See the oauth2.HTTPClient variable.
|
||||||
|
//
|
||||||
|
// The returned Client and its Transport should not be modified.
|
||||||
|
func (c *Config) Client(ctx context.Context) *http.Client {
|
||||||
|
return oauth2.NewClient(ctx, c.TokenSource(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenSource returns a TokenSource that returns t until t expires,
|
||||||
|
// automatically refreshing it as necessary using the provided context and the
|
||||||
|
// client ID and client secret.
|
||||||
|
//
|
||||||
|
// Most users will use Config.Client instead.
|
||||||
|
func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
|
||||||
|
source := &tokenSource{
|
||||||
|
ctx: ctx,
|
||||||
|
conf: c,
|
||||||
|
}
|
||||||
|
return oauth2.ReuseTokenSource(nil, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenSource struct {
|
||||||
|
ctx context.Context
|
||||||
|
conf *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token refreshes the token by using a new client credentials request.
|
||||||
|
// tokens received this way do not include a refresh token
|
||||||
|
func (c *tokenSource) Token() (*oauth2.Token, error) {
|
||||||
|
v := url.Values{
|
||||||
|
"grant_type": {"client_credentials"},
|
||||||
|
}
|
||||||
|
if len(c.conf.Scopes) > 0 {
|
||||||
|
v.Set("scope", strings.Join(c.conf.Scopes, " "))
|
||||||
|
}
|
||||||
|
for k, p := range c.conf.EndpointParams {
|
||||||
|
// Allow grant_type to be overridden to allow interoperability with
|
||||||
|
// non-compliant implementations.
|
||||||
|
if _, ok := v[k]; ok && k != "grant_type" {
|
||||||
|
return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k)
|
||||||
|
}
|
||||||
|
v[k] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v, internal.AuthStyle(c.conf.AuthStyle))
|
||||||
|
if err != nil {
|
||||||
|
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||||
|
return nil, (*oauth2.RetrieveError)(rErr)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t := &oauth2.Token{
|
||||||
|
AccessToken: tk.AccessToken,
|
||||||
|
TokenType: tk.TokenType,
|
||||||
|
RefreshToken: tk.RefreshToken,
|
||||||
|
Expiry: tk.Expiry,
|
||||||
|
}
|
||||||
|
return t.WithExtra(tk.Raw), nil
|
||||||
|
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -235,7 +235,7 @@ golang.org/x/lint/golint
|
||||||
# golang.org/x/mod v0.4.2
|
# golang.org/x/mod v0.4.2
|
||||||
golang.org/x/mod/module
|
golang.org/x/mod/module
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
# golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/net/context
|
golang.org/x/net/context
|
||||||
golang.org/x/net/context/ctxhttp
|
golang.org/x/net/context/ctxhttp
|
||||||
|
@ -251,6 +251,7 @@ golang.org/x/net/trace
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/oauth2
|
golang.org/x/oauth2
|
||||||
golang.org/x/oauth2/authhandler
|
golang.org/x/oauth2/authhandler
|
||||||
|
golang.org/x/oauth2/clientcredentials
|
||||||
golang.org/x/oauth2/google
|
golang.org/x/oauth2/google
|
||||||
golang.org/x/oauth2/google/internal/externalaccount
|
golang.org/x/oauth2/google/internal/externalaccount
|
||||||
golang.org/x/oauth2/internal
|
golang.org/x/oauth2/internal
|
||||||
|
|
Loading…
Reference in a new issue