diff --git a/cmd/karma/alerts.go b/cmd/karma/alerts.go index 1f797c7ea..4e79991db 100644 --- a/cmd/karma/alerts.go +++ b/cmd/karma/alerts.go @@ -109,15 +109,16 @@ func getUpstreams() models.AlertmanagerAPISummary { } u := models.AlertmanagerAPIStatus{ - Name: upstream.Name, - URI: upstream.InternalURI(), - PublicURI: upstream.PublicURI(), - ReadOnly: upstream.ReadOnly, - Headers: map[string]string{}, - Error: upstream.Error(), - Version: upstream.Version(), - Cluster: upstream.ClusterID(), - ClusterMembers: members, + Name: upstream.Name, + URI: upstream.InternalURI(), + PublicURI: upstream.PublicURI(), + ReadOnly: upstream.ReadOnly, + Headers: map[string]string{}, + CORSCredentials: upstream.CORSCredentials, + Error: upstream.Error(), + Version: upstream.Version(), + Cluster: upstream.ClusterID(), + ClusterMembers: members, } if !upstream.ProxyRequests { for k, v := range uri.HeadersForBasicAuth(upstream.URI) { diff --git a/cmd/karma/main.go b/cmd/karma/main.go index 7d898cc64..613c4549d 100644 --- a/cmd/karma/main.go +++ b/cmd/karma/main.go @@ -143,6 +143,7 @@ func setupUpstreams() error { alertmanager.WithReadOnly(s.ReadOnly), alertmanager.WithHTTPTransport(httpTransport), // we will pass a nil unless TLS.CA or TLS.Cert is set alertmanager.WithHTTPHeaders(s.Headers), + alertmanager.WithCORSCredentials(s.CORS.Credentials), ) if err != nil { return fmt.Errorf("Failed to create Alertmanager '%s' with URI '%s': %s", s.Name, uri.SanitizeURI(s.URI), err) diff --git a/cmd/karma/tests/testscript/log_full_config_env.txt b/cmd/karma/tests/testscript/log_full_config_env.txt index c973bd3c2..91012dd53 100644 --- a/cmd/karma/tests/testscript/log_full_config_env.txt +++ b/cmd/karma/tests/testscript/log_full_config_env.txt @@ -84,6 +84,8 @@ level=info msg=" cert: \"\"" level=info msg=" key: \"\"" level=info msg=" insecureSkipVerify: false" level=info msg=" headers: {}" +level=info msg=" cors:" +level=info msg=" credentials: include" level=info msg="alertAcknowledgement:" level=info msg=" enabled: true" level=info msg=" duration: 5m0s" diff --git a/cmd/karma/tests/testscript/log_full_config_file.txt b/cmd/karma/tests/testscript/log_full_config_file.txt index ee9f5677b..894d33f51 100644 --- a/cmd/karma/tests/testscript/log_full_config_file.txt +++ b/cmd/karma/tests/testscript/log_full_config_file.txt @@ -15,12 +15,16 @@ alertmanager: uri: "http://localhost:9094" timeout: 10s readonly: true + cors: + credentials: omit - name: local uri: http://localhost:9095 proxy: true readonly: false headers: X-Auth-Test: some-token-or-other-string + cors: + credentials: same-origin - name: client-auth uri: https://localhost:9096 timeout: 10s @@ -242,6 +246,8 @@ level=info msg=" cert: \"\"" level=info msg=" key: \"\"" level=info msg=" insecureSkipVerify: false" level=info msg=" headers: {}" +level=info msg=" cors:" +level=info msg=" credentials: include" level=info msg=" - name: ha2" level=info msg=" uri: http://localhost:9094" level=info msg=" external_uri: \"\"" @@ -254,6 +260,8 @@ level=info msg=" cert: \"\"" level=info msg=" key: \"\"" level=info msg=" insecureSkipVerify: false" level=info msg=" headers: {}" +level=info msg=" cors:" +level=info msg=" credentials: omit" level=info msg=" - name: local" level=info msg=" uri: http://localhost:9095" level=info msg=" external_uri: \"\"" @@ -267,6 +275,8 @@ level=info msg=" key: \"\"" level=info msg=" insecureSkipVerify: false" level=info msg=" headers:" level=info msg=" X-Auth-Test: some-token-or-other-string" +level=info msg=" cors:" +level=info msg=" credentials: same-origin" level=info msg=" - name: client-auth" level=info msg=" uri: https://localhost:9096" level=info msg=" external_uri: \"\"" @@ -279,6 +289,8 @@ level=info msg=" cert: cert.pem" level=info msg=" key: key.pem" level=info msg=" insecureSkipVerify: false" level=info msg=" headers: {}" +level=info msg=" cors:" +level=info msg=" credentials: include" level=info msg="alertAcknowledgement:" level=info msg=" enabled: true" level=info msg=" duration: 7m0s" diff --git a/internal/alertmanager/models.go b/internal/alertmanager/models.go index 619d00c29..8cc573fce 100644 --- a/internal/alertmanager/models.go +++ b/internal/alertmanager/models.go @@ -59,6 +59,8 @@ type Alertmanager struct { Metrics alertmanagerMetrics // headers to send with each AlertManager request HTTPHeaders map[string]string + // CORS credentials + CORSCredentials string `json:"corsCredentials"` } func (am *Alertmanager) probeVersion() string { diff --git a/internal/alertmanager/upstream.go b/internal/alertmanager/upstream.go index 5ac04afd6..60fe54844 100644 --- a/internal/alertmanager/upstream.go +++ b/internal/alertmanager/upstream.go @@ -156,3 +156,11 @@ func WithExternalURI(uri string) Option { return nil } } + +// WithCORSCredentials option sets fetch CORS credentials policy +func WithCORSCredentials(val string) Option { + return func(am *Alertmanager) error { + am.CORSCredentials = val + return nil + } +} diff --git a/internal/config/config.go b/internal/config/config.go index c069fab1d..e0df17bd7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -49,6 +49,7 @@ func SetupFlags(f *pflag.FlagSet) { "Proxy all client requests to Alertmanager via karma (only used with simplified config)") f.Bool("alertmanager.readonly", false, "Enable read-only mode that disable silence management (only used with simplified config)") + f.String("alertmanager.cors.credentials", "include", "CORS credentials policy for browser fetch requests") f.String("karma.name", "karma", "Name for the karma instance") @@ -280,6 +281,9 @@ func (config *configSchema) Read(flags *pflag.FlagSet) string { if s.Timeout.Seconds() == 0 { config.Alertmanager.Servers[i].Timeout = config.Alertmanager.Timeout } + if s.CORS.Credentials == "" { + config.Alertmanager.Servers[i].CORS.Credentials = config.Alertmanager.CORS.Credentials + } } for labelName, customColors := range config.Labels.Color.Custom { @@ -319,6 +323,7 @@ func (config *configSchema) Read(flags *pflag.FlagSet) string { Proxy: config.Alertmanager.Proxy, ReadOnly: config.Alertmanager.ReadOnly, Headers: make(map[string]string), + CORS: config.Alertmanager.CORS, }, } } @@ -345,6 +350,7 @@ func (config *configSchema) LogValues() { Proxy: s.Proxy, ReadOnly: s.ReadOnly, Headers: s.Headers, + CORS: s.CORS, } servers = append(servers, server) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 68691ea78..a045d95f5 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -35,6 +35,8 @@ func testReadConfig(t *testing.T) { key: "" insecureSkipVerify: false headers: {} + cors: + credentials: include alertAcknowledgement: enabled: false duration: 15m0s diff --git a/internal/config/models.go b/internal/config/models.go index 088dcf094..9a3b68f52 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -5,6 +5,10 @@ import ( "time" ) +type AlertmanagerCORS struct { + Credentials string +} + type AlertmanagerConfig struct { Name string URI string @@ -19,6 +23,7 @@ type AlertmanagerConfig struct { InsecureSkipVerify bool `yaml:"insecureSkipVerify" koanf:"insecureSkipVerify"` } Headers map[string]string + CORS AlertmanagerCORS `yaml:"cors" koanf:"cors"` } type LinkDetectRules struct { @@ -39,12 +44,13 @@ type configSchema struct { Alertmanager struct { Interval time.Duration Servers []AlertmanagerConfig - Name string `yaml:"-" koanf:"name"` - Timeout time.Duration `yaml:"-" koanf:"timeout"` - URI string `yaml:"-" koanf:"uri"` - ExternalURI string `yaml:"-" koanf:"external_uri"` - Proxy bool `yaml:"-" koanf:"proxy"` - ReadOnly bool `yaml:"-" koanf:"readonly"` + Name string `yaml:"-" koanf:"name"` + Timeout time.Duration `yaml:"-" koanf:"timeout"` + URI string `yaml:"-" koanf:"uri"` + ExternalURI string `yaml:"-" koanf:"external_uri"` + Proxy bool `yaml:"-" koanf:"proxy"` + ReadOnly bool `yaml:"-" koanf:"readonly"` + CORS AlertmanagerCORS `yaml:"-" koanf:"cors"` } AlertAcknowledgement struct { Enabled bool diff --git a/internal/models/alertmanager.go b/internal/models/alertmanager.go index 00f9e69f5..900b112c7 100644 --- a/internal/models/alertmanager.go +++ b/internal/models/alertmanager.go @@ -28,13 +28,14 @@ type AlertmanagerAPIStatus struct { // this is the Alertmanager URI used for all requests made by the UI URI string `json:"uri"` // this is the Alertmanager URI used for links in the browser - PublicURI string `json:"publicURI"` - ReadOnly bool `json:"readonly"` - Headers map[string]string `json:"headers"` - Error string `json:"error"` - Version string `json:"version"` - Cluster string `json:"cluster"` - ClusterMembers []string `json:"clusterMembers"` + PublicURI string `json:"publicURI"` + ReadOnly bool `json:"readonly"` + Headers map[string]string `json:"headers"` + CORSCredentials string `json:"corsCredentials"` + Error string `json:"error"` + Version string `json:"version"` + Cluster string `json:"cluster"` + ClusterMembers []string `json:"clusterMembers"` } // AlertmanagerAPICounters returns number of Alertmanager instances in each