From 565bd08c43c53f8ac1ac6acc1fde688a303dfbd5 Mon Sep 17 00:00:00 2001 From: Dmytro Kozlov Date: Thu, 10 Mar 2022 13:09:12 +0200 Subject: [PATCH] Issue-1824: added flags and different auth types support (#2287) * vmalert/notifier: added flags and different auth types support Co-authored-by: hagen1778 --- app/vmalert/README.md | 51 ++++++++++++++++++++++++ app/vmalert/datasource/init.go | 19 +++++++-- app/vmalert/datasource/vm_test.go | 54 ++++++++++++++++++++++++++ app/vmalert/notifier/alertmanager.go | 13 ++++++- app/vmalert/notifier/init.go | 27 ++++++++++++- app/vmalert/remoteread/init.go | 19 +++++++-- app/vmalert/remotewrite/init.go | 18 +++++++-- app/vmalert/utils/auth.go | 58 ++++++++++++++++++++++++---- docs/vmalert.md | 51 ++++++++++++++++++++++++ 9 files changed, 289 insertions(+), 21 deletions(-) diff --git a/app/vmalert/README.md b/app/vmalert/README.md index d1940f22f..8820d563f 100644 --- a/app/vmalert/README.md +++ b/app/vmalert/README.md @@ -514,6 +514,16 @@ The shortlist of configuration flags is the following: Lookback defines how far into the past to look when evaluating queries. For example, if the datasource.lookback=5m then param "time" with value now()-5m will be added to every query. -datasource.maxIdleConnections int Defines the number of idle (keep-alive connections) to each configured datasource. Consider setting this value equal to the value: groups_total * group.concurrency. Too low a value may result in a high number of sockets in TIME_WAIT state. (default 100) + -datasource.oauth2.clientID string + Optional OAuth2 clientID to use for -datasource.url. + -datasource.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -datasource.url. + -datasource.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -datasource.url. + -datasource.oauth2.scopes string + Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';' + -datasource.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -datasource.url. -datasource.queryStep duration queryStep defines how far a value can fallback to when evaluating queries. For example, if datasource.queryStep=15s then param "step" with value "15s" will be added to every query.If queryStep isn't specified, rule's evaluationInterval will be used instead. -datasource.roundDigits int @@ -606,8 +616,29 @@ The shortlist of configuration flags is the following: -notifier.basicAuth.username array Optional basic auth username for -notifier.url Supports an array of values separated by comma or specified via multiple flags. + -notifier.bearerToken array + Optional bearer token for -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.bearerTokenFile array + Optional path to bearer token file for -notifier.url + Supports an array of values separated by comma or specified via multiple flags. -notifier.config string Path to configuration file for notifiers + -notifier.oauth2.clientID array + Optional OAuth2 clientID to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.clientSecret array + Optional OAuth2 clientSecret to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.clientSecretFile array + Optional OAuth2 clientSecretFile to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.scopes array + Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.tokenUrl array + Optional OAuth2 tokenURL to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. -notifier.suppressDuplicateTargetErrors Whether to suppress 'duplicate target' errors during discovery -notifier.tlsCAFile array @@ -654,6 +685,16 @@ The shortlist of configuration flags is the following: Whether to ignore errors from remote storage when restoring alerts state on startup. (default true) -remoteRead.lookback duration Lookback defines how far to look into past for alerts timeseries. For example, if lookback=1h then range from now() to now()-1h will be scanned. (default 1h0m0s) + -remoteRead.oauth2.clientID string + Optional OAuth2 clientID to use for -remoteRead.url. + -remoteRead.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -remoteRead.url. + -remoteRead.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -remoteRead.url. + -remoteRead.oauth2.scopes string + Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'. + -remoteRead.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -remoteRead.url. -remoteRead.tlsCAFile string Optional path to TLS CA file to use for verifying connections to -remoteRead.url. By default system CA is used -remoteRead.tlsCertFile string @@ -686,6 +727,16 @@ The shortlist of configuration flags is the following: Defines defines max number of timeseries to be flushed at once (default 1000) -remoteWrite.maxQueueSize int Defines the max number of pending datapoints to remote write endpoint (default 100000) + -remoteWrite.oauth2.clientID string + Optional OAuth2 clientID to use for -remoteWrite.url. + -remoteWrite.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -remoteWrite.url. + -remoteWrite.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -remoteWrite.url. + -remoteWrite.oauth2.scopes string + Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. + -remoteWrite.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -notifier.url. -remoteWrite.tlsCAFile string Optional path to TLS CA file to use for verifying connections to -remoteWrite.url. By default system CA is used -remoteWrite.tlsCertFile string diff --git a/app/vmalert/datasource/init.go b/app/vmalert/datasource/init.go index 7efd40693..f36059f51 100644 --- a/app/vmalert/datasource/init.go +++ b/app/vmalert/datasource/init.go @@ -13,12 +13,14 @@ import ( var ( addr = flag.String("datasource.url", "", "VictoriaMetrics or vmselect url. Required parameter. "+ "E.g. http://127.0.0.1:8428") - appendTypePrefix = flag.Bool("datasource.appendTypePrefix", false, "Whether to add type prefix to -datasource.url based on the query type. Set to true if sending different query types to the vmselect URL.") + appendTypePrefix = flag.Bool("datasource.appendTypePrefix", false, "Whether to add type prefix to -datasource.url based on the query type. Set to true if sending different query types to the vmselect URL.") + basicAuthUsername = flag.String("datasource.basicAuth.username", "", "Optional basic auth username for -datasource.url") basicAuthPassword = flag.String("datasource.basicAuth.password", "", "Optional basic auth password for -datasource.url") basicAuthPasswordFile = flag.String("datasource.basicAuth.passwordFile", "", "Optional path to basic auth password to use for -datasource.url") - bearerToken = flag.String("datasource.bearerToken", "", "Optional bearer auth token to use for -datasource.url.") - bearerTokenFile = flag.String("datasource.bearerTokenFile", "", "Optional path to bearer token file to use for -datasource.url.") + + bearerToken = flag.String("datasource.bearerToken", "", "Optional bearer auth token to use for -datasource.url.") + bearerTokenFile = flag.String("datasource.bearerTokenFile", "", "Optional path to bearer token file to use for -datasource.url.") tlsInsecureSkipVerify = flag.Bool("datasource.tlsInsecureSkipVerify", false, "Whether to skip tls verification when connecting to -datasource.url") tlsCertFile = flag.String("datasource.tlsCertFile", "", "Optional path to client-side TLS certificate file to use when connecting to -datasource.url") @@ -26,6 +28,12 @@ var ( tlsCAFile = flag.String("datasource.tlsCAFile", "", `Optional path to TLS CA file to use for verifying connections to -datasource.url. By default, system CA is used`) tlsServerName = flag.String("datasource.tlsServerName", "", `Optional TLS server name to use for connections to -datasource.url. By default, the server name from -datasource.url is used`) + oauth2ClientID = flag.String("datasource.oauth2.clientID", "", "Optional OAuth2 clientID to use for -datasource.url. ") + oauth2ClientSecret = flag.String("datasource.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -datasource.url.") + oauth2ClientSecretFile = flag.String("datasource.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -datasource.url. ") + oauth2TokenURL = flag.String("datasource.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -datasource.url.") + oauth2Scopes = flag.String("datasource.oauth2.scopes", "", "Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';'") + lookBack = flag.Duration("datasource.lookback", 0, `Lookback defines how far into the past to look when evaluating queries. For example, if the datasource.lookback=5m then param "time" with value now()-5m will be added to every query.`) queryStep = flag.Duration("datasource.queryStep", 0, "queryStep defines how far a value can fallback to when evaluating queries. "+ "For example, if datasource.queryStep=15s then param \"step\" with value \"15s\" will be added to every query."+ @@ -64,7 +72,10 @@ func Init(extraParams url.Values) (QuerierBuilder, error) { extraParams.Set("round_digits", fmt.Sprintf("%d", *roundDigits)) } - authCfg, err := utils.AuthConfig(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile, *bearerToken, *bearerTokenFile) + authCfg, err := utils.AuthConfig( + utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), + utils.WithBearer(*bearerToken, *bearerTokenFile), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) } diff --git a/app/vmalert/datasource/vm_test.go b/app/vmalert/datasource/vm_test.go index 81e12b2f6..613cc7ae5 100644 --- a/app/vmalert/datasource/vm_test.go +++ b/app/vmalert/datasource/vm_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/utils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" ) @@ -517,6 +518,59 @@ func TestRequestParams(t *testing.T) { } } +func TestAuthConfig(t *testing.T) { + var testCases = []struct { + name string + vmFn func() *VMStorage + checkFn func(t *testing.T, r *http.Request) + }{ + { + name: "basic auth", + vmFn: func() *VMStorage { + cfg, err := utils.AuthConfig(utils.WithBasicAuth("foo", "bar", "")) + if err != nil { + t.Errorf("Error get auth config: %s", err) + } + return &VMStorage{authCfg: cfg} + }, + checkFn: func(t *testing.T, r *http.Request) { + u, p, _ := r.BasicAuth() + checkEqualString(t, "foo", u) + checkEqualString(t, "bar", p) + }, + }, + { + name: "bearer auth", + vmFn: func() *VMStorage { + cfg, err := utils.AuthConfig(utils.WithBearer("foo", "")) + if err != nil { + t.Errorf("Error get auth config: %s", err) + } + return &VMStorage{authCfg: cfg} + }, + checkFn: func(t *testing.T, r *http.Request) { + reqToken := r.Header.Get("Authorization") + splitToken := strings.Split(reqToken, "Bearer ") + if len(splitToken) != 2 { + t.Errorf("expected two items got %d", len(splitToken)) + } + token := splitToken[1] + checkEqualString(t, "foo", token) + }, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + vm := tt.vmFn() + req, err := vm.newRequestPOST() + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + tt.checkFn(t, req) + }) + } +} + func checkEqualString(t *testing.T, exp, got string) { t.Helper() if got != exp { diff --git a/app/vmalert/notifier/alertmanager.go b/app/vmalert/notifier/alertmanager.go index 4ecc5a55e..1caeea692 100644 --- a/app/vmalert/notifier/alertmanager.go +++ b/app/vmalert/notifier/alertmanager.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "net/http" + "strings" "time" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/utils" @@ -112,11 +113,19 @@ func NewAlertManager(alertManagerURL string, fn AlertURLGenerator, authCfg proma return nil, fmt.Errorf("failed to create transport: %w", err) } - ba := &promauth.BasicAuthConfig{} + ba := new(promauth.BasicAuthConfig) + oauth := new(promauth.OAuth2Config) if authCfg.BasicAuth != nil { ba = authCfg.BasicAuth } - aCfg, err := utils.AuthConfig(ba.Username, ba.Password.String(), ba.PasswordFile, authCfg.BearerToken.String(), authCfg.BearerTokenFile) + if authCfg.OAuth2 != nil { + oauth = authCfg.OAuth2 + } + + aCfg, err := utils.AuthConfig( + utils.WithBasicAuth(ba.Username, ba.Password.String(), ba.PasswordFile), + utils.WithBearer(authCfg.BearerToken.String(), authCfg.BearerTokenFile), + utils.WithOAuth(oauth.ClientID, oauth.ClientSecretFile, oauth.ClientSecretFile, oauth.TokenURL, strings.Join(oauth.Scopes, ";"))) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) } diff --git a/app/vmalert/notifier/init.go b/app/vmalert/notifier/init.go index 784751d02..8efa9ecb1 100644 --- a/app/vmalert/notifier/init.go +++ b/app/vmalert/notifier/init.go @@ -15,11 +15,15 @@ var ( configPath = flag.String("notifier.config", "", "Path to configuration file for notifiers") suppressDuplicateTargetErrors = flag.Bool("notifier.suppressDuplicateTargetErrors", false, "Whether to suppress 'duplicate target' errors during discovery") - addrs = flagutil.NewArray("notifier.url", "Prometheus alertmanager URL, e.g. http://127.0.0.1:9093") + addrs = flagutil.NewArray("notifier.url", "Prometheus alertmanager URL, e.g. http://127.0.0.1:9093") + basicAuthUsername = flagutil.NewArray("notifier.basicAuth.username", "Optional basic auth username for -notifier.url") basicAuthPassword = flagutil.NewArray("notifier.basicAuth.password", "Optional basic auth password for -notifier.url") basicAuthPasswordFile = flagutil.NewArray("notifier.basicAuth.passwordFile", "Optional path to basic auth password file for -notifier.url") + bearerToken = flagutil.NewArray("notifier.bearerToken", "Optional bearer token for -notifier.url") + bearerTokenFile = flagutil.NewArray("notifier.bearerTokenFile", "Optional path to bearer token file for -notifier.url") + tlsInsecureSkipVerify = flagutil.NewArrayBool("notifier.tlsInsecureSkipVerify", "Whether to skip tls verification when connecting to -notifier.url") tlsCertFile = flagutil.NewArray("notifier.tlsCertFile", "Optional path to client-side TLS certificate file to use when connecting to -notifier.url") tlsKeyFile = flagutil.NewArray("notifier.tlsKeyFile", "Optional path to client-side TLS certificate key to use when connecting to -notifier.url") @@ -27,6 +31,17 @@ var ( "By default system CA is used") tlsServerName = flagutil.NewArray("notifier.tlsServerName", "Optional TLS server name to use for connections to -notifier.url. "+ "By default the server name from -notifier.url is used") + + oauth2ClientID = flagutil.NewArray("notifier.oauth2.clientID", "Optional OAuth2 clientID to use for -notifier.url. "+ + "If multiple args are set, then they are applied independently for the corresponding -notifier.url") + oauth2ClientSecret = flagutil.NewArray("notifier.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -notifier.url. "+ + "If multiple args are set, then they are applied independently for the corresponding -notifier.url") + oauth2ClientSecretFile = flagutil.NewArray("notifier.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -notifier.url. "+ + "If multiple args are set, then they are applied independently for the corresponding -notifier.url") + oauth2TokenURL = flagutil.NewArray("notifier.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for -notifier.url. "+ + "If multiple args are set, then they are applied independently for the corresponding -notifier.url") + oauth2Scopes = flagutil.NewArray("notifier.oauth2.scopes", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. "+ + "If multiple args are set, then they are applied independently for the corresponding -notifier.url") ) // cw holds a configWatcher for configPath configuration file @@ -111,7 +126,17 @@ func notifiersFromFlags(gen AlertURLGenerator) ([]Notifier, error) { Password: promauth.NewSecret(basicAuthPassword.GetOptionalArg(i)), PasswordFile: basicAuthPasswordFile.GetOptionalArg(i), }, + BearerToken: promauth.NewSecret(bearerToken.GetOptionalArg(i)), + BearerTokenFile: bearerTokenFile.GetOptionalArg(i), + OAuth2: &promauth.OAuth2Config{ + ClientID: oauth2ClientID.GetOptionalArg(i), + ClientSecret: promauth.NewSecret(oauth2ClientSecret.GetOptionalArg(i)), + ClientSecretFile: oauth2ClientSecretFile.GetOptionalArg(i), + Scopes: strings.Split(oauth2Scopes.GetOptionalArg(i), ";"), + TokenURL: oauth2TokenURL.GetOptionalArg(i), + }, } + addr = strings.TrimSuffix(addr, "/") am, err := NewAlertManager(addr+alertManagerPath, gen, authCfg, time.Minute) if err != nil { diff --git a/app/vmalert/remoteread/init.go b/app/vmalert/remoteread/init.go index 427b5def2..d5f9e0a4a 100644 --- a/app/vmalert/remoteread/init.go +++ b/app/vmalert/remoteread/init.go @@ -13,11 +13,13 @@ var ( addr = flag.String("remoteRead.url", "", "Optional URL to VictoriaMetrics or vmselect that will be used to restore alerts "+ "state. This configuration makes sense only if `vmalert` was configured with `remoteWrite.url` before and has been successfully persisted its state. "+ "E.g. http://127.0.0.1:8428. See also -remoteRead.disablePathAppend") + basicAuthUsername = flag.String("remoteRead.basicAuth.username", "", "Optional basic auth username for -remoteRead.url") basicAuthPassword = flag.String("remoteRead.basicAuth.password", "", "Optional basic auth password for -remoteRead.url") basicAuthPasswordFile = flag.String("remoteRead.basicAuth.passwordFile", "", "Optional path to basic auth password to use for -remoteRead.url") - bearerToken = flag.String("remoteRead.bearerToken", "", "Optional bearer auth token to use for -remoteRead.url.") - bearerTokenFile = flag.String("remoteRead.bearerTokenFile", "", "Optional path to bearer token file to use for -remoteRead.url.") + + bearerToken = flag.String("remoteRead.bearerToken", "", "Optional bearer auth token to use for -remoteRead.url.") + bearerTokenFile = flag.String("remoteRead.bearerTokenFile", "", "Optional path to bearer token file to use for -remoteRead.url.") tlsInsecureSkipVerify = flag.Bool("remoteRead.tlsInsecureSkipVerify", false, "Whether to skip tls verification when connecting to -remoteRead.url") tlsCertFile = flag.String("remoteRead.tlsCertFile", "", "Optional path to client-side TLS certificate file to use when connecting to -remoteRead.url") @@ -26,6 +28,13 @@ var ( "By default system CA is used") tlsServerName = flag.String("remoteRead.tlsServerName", "", "Optional TLS server name to use for connections to -remoteRead.url. "+ "By default the server name from -remoteRead.url is used") + + oauth2ClientID = flag.String("remoteRead.oauth2.clientID", "", "Optional OAuth2 clientID to use for -remoteRead.url.") + oauth2ClientSecret = flag.String("remoteRead.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -remoteRead.url.") + oauth2ClientSecretFile = flag.String("remoteRead.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -remoteRead.url.") + oauth2TokenURL = flag.String("remoteRead.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -remoteRead.url. ") + oauth2Scopes = flag.String("remoteRead.oauth2.scopes", "", "Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'.") + disablePathAppend = flag.Bool("remoteRead.disablePathAppend", false, "Whether to disable automatic appending of '/api/v1/query' path to the configured -remoteRead.url.") ) @@ -39,7 +48,11 @@ func Init() (datasource.QuerierBuilder, error) { if err != nil { return nil, fmt.Errorf("failed to create transport: %w", err) } - authCfg, err := utils.AuthConfig(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile, *bearerToken, *bearerTokenFile) + + authCfg, err := utils.AuthConfig( + utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), + utils.WithBearer(*bearerToken, *bearerTokenFile), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) } diff --git a/app/vmalert/remotewrite/init.go b/app/vmalert/remotewrite/init.go index 3fdac9201..1f927f8a9 100644 --- a/app/vmalert/remotewrite/init.go +++ b/app/vmalert/remotewrite/init.go @@ -13,11 +13,13 @@ var ( addr = flag.String("remoteWrite.url", "", "Optional URL to VictoriaMetrics or vminsert where to persist alerts state "+ "and recording rules results in form of timeseries. For example, if -remoteWrite.url=http://127.0.0.1:8428 is specified, "+ "then the alerts state will be written to http://127.0.0.1:8428/api/v1/write . See also -remoteWrite.disablePathAppend") + basicAuthUsername = flag.String("remoteWrite.basicAuth.username", "", "Optional basic auth username for -remoteWrite.url") basicAuthPassword = flag.String("remoteWrite.basicAuth.password", "", "Optional basic auth password for -remoteWrite.url") basicAuthPasswordFile = flag.String("remoteWrite.basicAuth.passwordFile", "", "Optional path to basic auth password to use for -remoteWrite.url") - bearerToken = flag.String("remoteWrite.bearerToken", "", "Optional bearer auth token to use for -remoteWrite.url.") - bearerTokenFile = flag.String("remoteWrite.bearerTokenFile", "", "Optional path to bearer token file to use for -remoteWrite.url.") + + bearerToken = flag.String("remoteWrite.bearerToken", "", "Optional bearer auth token to use for -remoteWrite.url.") + bearerTokenFile = flag.String("remoteWrite.bearerTokenFile", "", "Optional path to bearer token file to use for -remoteWrite.url.") maxQueueSize = flag.Int("remoteWrite.maxQueueSize", 1e5, "Defines the max number of pending datapoints to remote write endpoint") maxBatchSize = flag.Int("remoteWrite.maxBatchSize", 1e3, "Defines defines max number of timeseries to be flushed at once") @@ -31,6 +33,13 @@ var ( "By default system CA is used") tlsServerName = flag.String("remoteWrite.tlsServerName", "", "Optional TLS server name to use for connections to -remoteWrite.url. "+ "By default the server name from -remoteWrite.url is used") + + oauth2ClientID = flag.String("remoteWrite.oauth2.clientID", "", "Optional OAuth2 clientID to use for -remoteWrite.url.") + oauth2ClientSecret = flag.String("remoteWrite.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -remoteWrite.url.") + oauth2ClientSecretFile = flag.String("remoteWrite.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url.") + oauth2TokenURL = flag.String("remoteWrite.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -notifier.url.") + oauth2Scopes = flag.String("remoteWrite.oauth2.scopes", "", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'.") + disablePathAppend = flag.Bool("remoteWrite.disablePathAppend", false, "Whether to disable automatic appending of '/api/v1/write' path to the configured -remoteWrite.url.") ) @@ -46,7 +55,10 @@ func Init(ctx context.Context) (*Client, error) { return nil, fmt.Errorf("failed to create transport: %w", err) } - authCfg, err := utils.AuthConfig(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile, *bearerToken, *bearerTokenFile) + authCfg, err := utils.AuthConfig( + utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), + utils.WithBearer(*bearerToken, *bearerTokenFile), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) } diff --git a/app/vmalert/utils/auth.go b/app/vmalert/utils/auth.go index 20ff5715b..060b79db7 100644 --- a/app/vmalert/utils/auth.go +++ b/app/vmalert/utils/auth.go @@ -1,18 +1,60 @@ package utils import ( + "strings" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" ) +// AuthConfigOptions options which helps build promauth.Config +type AuthConfigOptions func(config *promauth.HTTPClientConfig) + // AuthConfig returns promauth.Config based on the given params -func AuthConfig(baUser, baPass, baFile, bearerToken, bearerTokenFile string) (*promauth.Config, error) { - var baCfg *promauth.BasicAuthConfig - if baUser != "" || baPass != "" || baFile != "" { - baCfg = &promauth.BasicAuthConfig{ - Username: baUser, - Password: promauth.NewSecret(baPass), - PasswordFile: baFile, +func AuthConfig(filterOptions ...AuthConfigOptions) (*promauth.Config, error) { + authCfg := &promauth.HTTPClientConfig{} + for _, option := range filterOptions { + option(authCfg) + } + + return authCfg.NewConfig(".") +} + +// WithBasicAuth returns AuthConfigOptions and initialized promauth.BasicAuthConfig based on given params +func WithBasicAuth(username, password, passwordFile string) AuthConfigOptions { + return func(config *promauth.HTTPClientConfig) { + if username != "" || password != "" || passwordFile != "" { + config.BasicAuth = &promauth.BasicAuthConfig{ + Username: username, + Password: promauth.NewSecret(password), + PasswordFile: passwordFile, + } + } + } +} + +// WithBearer returns AuthConfigOptions and set BearerToken or BearerTokenFile based on given params +func WithBearer(token, tokenFile string) AuthConfigOptions { + return func(config *promauth.HTTPClientConfig) { + if token != "" { + config.BearerToken = promauth.NewSecret(token) + } + if tokenFile != "" { + config.BearerTokenFile = tokenFile + } + } +} + +// WithOAuth returns AuthConfigOptions and set OAuth params based on given params +func WithOAuth(clientID, clientSecret, clientSecretFile, tokenURL, scopes string) AuthConfigOptions { + return func(config *promauth.HTTPClientConfig) { + if clientSecretFile != "" || clientSecret != "" { + config.OAuth2 = &promauth.OAuth2Config{ + ClientID: clientID, + ClientSecret: promauth.NewSecret(clientSecret), + ClientSecretFile: clientSecretFile, + TokenURL: tokenURL, + Scopes: strings.Split(scopes, ";"), + } } } - return promauth.NewConfig(".", nil, baCfg, bearerToken, bearerTokenFile, nil, nil) } diff --git a/docs/vmalert.md b/docs/vmalert.md index 0a8e63d67..8c0342ea3 100644 --- a/docs/vmalert.md +++ b/docs/vmalert.md @@ -518,6 +518,16 @@ The shortlist of configuration flags is the following: Lookback defines how far into the past to look when evaluating queries. For example, if the datasource.lookback=5m then param "time" with value now()-5m will be added to every query. -datasource.maxIdleConnections int Defines the number of idle (keep-alive connections) to each configured datasource. Consider setting this value equal to the value: groups_total * group.concurrency. Too low a value may result in a high number of sockets in TIME_WAIT state. (default 100) + -datasource.oauth2.clientID string + Optional OAuth2 clientID to use for -datasource.url. + -datasource.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -datasource.url. + -datasource.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -datasource.url. + -datasource.oauth2.scopes string + Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';' + -datasource.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -datasource.url. -datasource.queryStep duration queryStep defines how far a value can fallback to when evaluating queries. For example, if datasource.queryStep=15s then param "step" with value "15s" will be added to every query.If queryStep isn't specified, rule's evaluationInterval will be used instead. -datasource.roundDigits int @@ -610,8 +620,29 @@ The shortlist of configuration flags is the following: -notifier.basicAuth.username array Optional basic auth username for -notifier.url Supports an array of values separated by comma or specified via multiple flags. + -notifier.bearerToken array + Optional bearer token for -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.bearerTokenFile array + Optional path to bearer token file for -notifier.url + Supports an array of values separated by comma or specified via multiple flags. -notifier.config string Path to configuration file for notifiers + -notifier.oauth2.clientID array + Optional OAuth2 clientID to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.clientSecret array + Optional OAuth2 clientSecret to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.clientSecretFile array + Optional OAuth2 clientSecretFile to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.scopes array + Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.tokenUrl array + Optional OAuth2 tokenURL to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url + Supports an array of values separated by comma or specified via multiple flags. -notifier.suppressDuplicateTargetErrors Whether to suppress 'duplicate target' errors during discovery -notifier.tlsCAFile array @@ -658,6 +689,16 @@ The shortlist of configuration flags is the following: Whether to ignore errors from remote storage when restoring alerts state on startup. (default true) -remoteRead.lookback duration Lookback defines how far to look into past for alerts timeseries. For example, if lookback=1h then range from now() to now()-1h will be scanned. (default 1h0m0s) + -remoteRead.oauth2.clientID string + Optional OAuth2 clientID to use for -remoteRead.url. + -remoteRead.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -remoteRead.url. + -remoteRead.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -remoteRead.url. + -remoteRead.oauth2.scopes string + Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'. + -remoteRead.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -remoteRead.url. -remoteRead.tlsCAFile string Optional path to TLS CA file to use for verifying connections to -remoteRead.url. By default system CA is used -remoteRead.tlsCertFile string @@ -690,6 +731,16 @@ The shortlist of configuration flags is the following: Defines defines max number of timeseries to be flushed at once (default 1000) -remoteWrite.maxQueueSize int Defines the max number of pending datapoints to remote write endpoint (default 100000) + -remoteWrite.oauth2.clientID string + Optional OAuth2 clientID to use for -remoteWrite.url. + -remoteWrite.oauth2.clientSecret string + Optional OAuth2 clientSecret to use for -remoteWrite.url. + -remoteWrite.oauth2.clientSecretFile string + Optional OAuth2 clientSecretFile to use for -remoteWrite.url. + -remoteWrite.oauth2.scopes string + Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. + -remoteWrite.oauth2.tokenUrl string + Optional OAuth2 tokenURL to use for -notifier.url. -remoteWrite.tlsCAFile string Optional path to TLS CA file to use for verifying connections to -remoteWrite.url. By default system CA is used -remoteWrite.tlsCertFile string