Skip to content

Commit

Permalink
ruler: Add support for alertmanager header authorization (#6136)
Browse files Browse the repository at this point in the history
  • Loading branch information
periklis committed May 17, 2022
1 parent 6fb86c4 commit 6e9517e
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Main

* [6136](https://github.com/grafana/loki/pull/6136) **periklis**: Add support for alertmanager header authorization
* [6102](https://github.com/grafana/loki/pull/6102) **timchenko-a**: Add multi-tenancy support to lambda-promtail
* [5971](https://github.com/grafana/loki/pull/5971) **kavirajk**: Record statistics about metadata queries such as labels and series queries in `metrics.go` as well
* [5790](https://github.com/grafana/loki/pull/5790) **chaudum**: Add UDP support for Promtail's syslog target.
Expand Down
20 changes: 20 additions & 0 deletions docs/sources/configuration/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,26 @@ wal_cleaner:
# CLI flag: -ruler.alertmanager-url
[alertmanager_url: <string> | default = ""]


alertmanager_client:
# Sets the `Authorization` header on every remote write request with the
# configured username and password.
# password and password_file are mutually exclusive.
basic_auth:
[username: <string>]
[password: <secret>]

# Optional `Authorization` header configuration.
authorization:
# Sets the authentication type.
[type: <string> | default: Bearer]
# Sets the credentials. It is mutually exclusive with
# `credentials_file`.
[credentials: <secret>]
# Sets the credentials to the credentials read from the configured file.
# It is mutually exclusive with `credentials`.
[credentials_file: <filename>]

# Use DNS SRV records to discover Alertmanager hosts.
# CLI flag: -ruler.alertmanager-discovery
[enable_alertmanager_discovery: <boolean> | default = false]
Expand Down
21 changes: 19 additions & 2 deletions pkg/ruler/base/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ import (
)

type NotifierConfig struct {
TLS tls.ClientConfig `yaml:",inline"`
BasicAuth util.BasicAuth `yaml:",inline"`
TLS tls.ClientConfig `yaml:",inline"`
BasicAuth util.BasicAuth `yaml:",inline"`
HeaderAuth util.HeaderAuth `yaml:",inline"`
}

func (cfg *NotifierConfig) RegisterFlags(f *flag.FlagSet) {
cfg.TLS.RegisterFlagsWithPrefix("ruler.alertmanager-client", f)
cfg.BasicAuth.RegisterFlagsWithPrefix("ruler.alertmanager-client.", f)
cfg.HeaderAuth.RegisterFlagsWithPrefix("ruler.alertmanager-client.", f)
}

// rulerNotifier bundles a notifier.Manager together with an associated
Expand Down Expand Up @@ -197,5 +199,20 @@ func amConfigFromURL(rulerConfig *Config, url *url.URL, apiVersion config.Alertm
}
}

if rulerConfig.Notifier.HeaderAuth.IsEnabled() {
if rulerConfig.Notifier.HeaderAuth.Credentials != "" {
amConfig.HTTPClientConfig.Authorization = &config_util.Authorization{
Type: rulerConfig.Notifier.HeaderAuth.Type,
Credentials: config_util.Secret(rulerConfig.Notifier.HeaderAuth.Credentials),
}
} else if rulerConfig.Notifier.HeaderAuth.CredentialsFile != "" {
amConfig.HTTPClientConfig.Authorization = &config_util.Authorization{
Type: rulerConfig.Notifier.HeaderAuth.Type,
CredentialsFile: rulerConfig.Notifier.HeaderAuth.CredentialsFile,
}

}
}

return amConfig
}
72 changes: 72 additions & 0 deletions pkg/ruler/base/notifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,78 @@ func TestBuildNotifierConfig(t *testing.T) {
},
},
},
{
name: "with Header Authorization",
cfg: &Config{
AlertmanagerURL: "http://alertmanager-0.default.svc.cluster.local/alertmanager",
Notifier: NotifierConfig{
HeaderAuth: util.HeaderAuth{
Type: "Bearer",
Credentials: "jacob",
},
},
},
ncfg: &config.Config{
AlertingConfig: config.AlertingConfig{
AlertmanagerConfigs: []*config.AlertmanagerConfig{
{
HTTPClientConfig: config_util.HTTPClientConfig{
Authorization: &config_util.Authorization{
Type: "Bearer",
Credentials: config_util.Secret("jacob"),
},
},
APIVersion: "v1",
Scheme: "http",
PathPrefix: "/alertmanager",
ServiceDiscoveryConfigs: discovery.Configs{
discovery.StaticConfig{
{
Targets: []model.LabelSet{{"__address__": "alertmanager-0.default.svc.cluster.local"}},
},
},
},
},
},
},
},
},
{
name: "with Header Authorization and credentials file",
cfg: &Config{
AlertmanagerURL: "http://alertmanager-0.default.svc.cluster.local/alertmanager",
Notifier: NotifierConfig{
HeaderAuth: util.HeaderAuth{
Type: "Bearer",
CredentialsFile: "/path/to/secret/file",
},
},
},
ncfg: &config.Config{
AlertingConfig: config.AlertingConfig{
AlertmanagerConfigs: []*config.AlertmanagerConfig{
{
HTTPClientConfig: config_util.HTTPClientConfig{
Authorization: &config_util.Authorization{
Type: "Bearer",
CredentialsFile: "/path/to/secret/file",
},
},
APIVersion: "v1",
Scheme: "http",
PathPrefix: "/alertmanager",
ServiceDiscoveryConfigs: discovery.Configs{
discovery.StaticConfig{
{
Targets: []model.LabelSet{{"__address__": "alertmanager-0.default.svc.cluster.local"}},
},
},
},
},
},
},
},
},
{
name: "with external labels",
cfg: &Config{
Expand Down
18 changes: 18 additions & 0 deletions pkg/util/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ func (b BasicAuth) IsEnabled() bool {
return b.Username != "" || b.Password != ""
}

// HeaderAuth condigures header based authorization for HTTP clients.
type HeaderAuth struct {
Type string `yaml:"type,omitempty"`
Credentials string `yaml:"credentials,omitempty"`
CredentialsFile string `yaml:"credentials_file,omitempty"`
}

func (h *HeaderAuth) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
f.StringVar(&h.Type, prefix+"type", "Bearer", "HTTP Header authorization type (default: Bearer).")
f.StringVar(&h.Credentials, prefix+"credentials", "", "HTTP Header authorization credentials.")
f.StringVar(&h.CredentialsFile, prefix+"credentials-file", "", "HTTP Header authorization credentials file.")
}

// IsEnabled returns false if header authorization isn't enabled.
func (h HeaderAuth) IsEnabled() bool {
return h.Credentials != "" || h.CredentialsFile != ""
}

// WriteJSONResponse writes some JSON as a HTTP response.
func WriteJSONResponse(w http.ResponseWriter, v interface{}) {
w.Header().Set("Content-Type", "application/json")
Expand Down

0 comments on commit 6e9517e

Please sign in to comment.