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/invalid_alertmanager_cors_credentials.txt b/cmd/karma/tests/testscript/invalid_alertmanager_cors_credentials.txt
new file mode 100644
index 000000000..f0b87303a
--- /dev/null
+++ b/cmd/karma/tests/testscript/invalid_alertmanager_cors_credentials.txt
@@ -0,0 +1,12 @@
+# Raises an error if we cors.credentials value is incorrect
+karma.bin-should-fail --log.format=text --log.config=false --log.level=error --config.file karma.yaml
+! stdout .
+stderr 'msg="Invalid cors.credentials value ''foo'' for alertmanager ''am1'', allowed options: omit, inclue, same-origin'
+
+-- karma.yaml --
+alertmanager:
+ servers:
+ - name: am1
+ uri: https://localhost:9093
+ cors:
+ credentials: foo
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/cmd/karma/tests/testscript/log_full_config_file_invalid_values.txt b/cmd/karma/tests/testscript/log_full_config_file_invalid_values.txt
index f9121037e..409faa013 100644
--- a/cmd/karma/tests/testscript/log_full_config_file_invalid_values.txt
+++ b/cmd/karma/tests/testscript/log_full_config_file_invalid_values.txt
@@ -11,6 +11,8 @@ alertmanager:
uri: "http://localhost:9093"
timeout: bbb
proxy: YEs
+ cors:
+ credentials: foo
- name: ha2
uri: "http://localhost:9094"
timeout: 11
@@ -58,6 +60,7 @@ ui:
-- expected.stderr --
level=fatal msg="Failed to unmarshal configuration: 12 error(s) decoding:\n\n* 'Alertmanager.Servers[2].Headers[0]' expected a map, got 'string'\n* cannot parse 'Alertmanager.Servers[0].Proxy' as bool: strconv.ParseBool: parsing \"YEs\": invalid syntax\n* cannot parse 'Annotations.Default.Hidden' as bool: strconv.ParseBool: parsing \"z\": invalid syntax\n* cannot parse 'UI.alertsPerGroup' as int: strconv.ParseInt: parsing \"5a\": invalid syntax\n* cannot parse 'UI.colorTitlebar' as bool: strconv.ParseBool: parsing \"yum\": invalid syntax\n* cannot parse 'UI.hideFiltersWhenIdle' as bool: strconv.ParseBool: parsing \"z\": invalid syntax\n* cannot parse 'UI.minimalGroupWidth' as int: strconv.ParseInt: parsing \"abc4\": invalid syntax\n* cannot parse 'alertAcknowledgement.Enabled' as bool: strconv.ParseBool: parsing \"zzz\": invalid syntax\n* error decoding 'Alertmanager.Interval': time: invalid duration jjs88\n* error decoding 'Alertmanager.Servers[0].Timeout': time: invalid duration bbb\n* error decoding 'Alertmanager.Servers[2].Timeout': time: invalid duration z\n* error decoding 'UI.Refresh': time: unknown unit sm in duration 10sm"
+level=fatal msg="Invalid alertmanager.cors.credentials value '', allowed options: omit, inclue, same-origin"
level=fatal msg="Invalid grid.sorting.order value '', allowed options: disabled, startsAt, label"
level=fatal msg="Invalid ui.collapseGroups value '', allowed options: expanded, collapsed, collapsedOnMobile"
level=fatal msg="Invalid ui.theme value '', allowed options: light, dark, auto"
diff --git a/demo/karma.yaml b/demo/karma.yaml
index e2cd5ccb9..6133589b2 100644
--- a/demo/karma.yaml
+++ b/demo/karma.yaml
@@ -5,10 +5,14 @@ alertmanager:
uri: "http://localhost:9093"
timeout: 10s
proxy: true
+ cors:
+ credentials: same-origin
- name: ha2
uri: "http://localhost:9094"
timeout: 10s
proxy: true
+ cors:
+ credentials: same-origin
alertAcknowledgement:
enabled: true
duration: 15m0s
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index 96b80211a..33caa7fb6 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -50,6 +50,8 @@ alertmanager:
insecureSkipVerify: bool
headers:
any: string
+ cors:
+ credentials: string
```
- `interval` - how often alerts should be refreshed, a string in
@@ -103,6 +105,15 @@ alertmanager:
- `headers` - a map with a list of key: values which are header: value.
These custom headers will be sent with every request to the alert manager
instance.
+- `cors:credentials` - sets the
+ [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) credentials
+ settings for browser requests,
+ [see docs](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
+ for the list of possible values.
+ By default credentials are included in all requests (`include`), set it to
+ `omit` or `same-origin` if Alertmanager is configured to respond with
+ `Access-Control-Allow-Origin: *`,
+ [see docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials).
Note: there are multiple supported combination of URI settings which result in
a slightly different behavior. Settings that control it are:
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..883094aa9 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")
@@ -276,10 +277,20 @@ func (config *configSchema) Read(flags *pflag.FlagSet) string {
log.Fatalf("silenceform.author.populate_from_header.value_re is required when silenceform.author.populate_from_header.header is set")
}
+ if !slices.StringInSlice([]string{"omit", "include", "same-origin"}, config.Alertmanager.CORS.Credentials) {
+ log.Fatalf("Invalid alertmanager.cors.credentials value '%s', allowed options: omit, inclue, same-origin", config.Alertmanager.CORS.Credentials)
+ }
+
for i, s := range config.Alertmanager.Servers {
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
+ }
+ if !slices.StringInSlice([]string{"omit", "include", "same-origin"}, config.Alertmanager.Servers[i].CORS.Credentials) {
+ log.Fatalf("Invalid cors.credentials value '%s' for alertmanager '%s', allowed options: omit, inclue, same-origin", config.Alertmanager.Servers[i].CORS.Credentials, s.Name)
+ }
}
for labelName, customColors := range config.Labels.Color.Custom {
@@ -319,6 +330,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 +357,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..610dd94d6 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
@@ -314,6 +316,22 @@ func TestInvalidUITheme(t *testing.T) {
}
}
+func TestInvalidCORSCredentials(t *testing.T) {
+ resetEnv()
+ os.Setenv("ALERTMANAGER_CORS_CREDENTIALS", "foo")
+
+ log.SetLevel(log.PanicLevel)
+ defer func() { log.StandardLogger().ExitFunc = nil }()
+ var wasFatal bool
+ log.StandardLogger().ExitFunc = func(int) { wasFatal = true }
+
+ mockConfigRead()
+
+ if !wasFatal {
+ t.Error("Invalid alertmanager.cors.credentials value didn't cause log.Fatal()")
+ }
+}
+
func TestDefaultConfig(t *testing.T) {
resetEnv()
log.SetLevel(log.ErrorLevel)
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
diff --git a/ui/src/Components/AlertAck/index.js b/ui/src/Components/AlertAck/index.js
index 657682e6b..63bc222e3 100644
--- a/ui/src/Components/AlertAck/index.js
+++ b/ui/src/Components/AlertAck/index.js
@@ -146,6 +146,7 @@ const AlertAck = observer(
body: JSON.stringify(
this.submitState.silencesByCluster[cluster].payload
),
+ credentials: am.corsCredentials,
headers: {
"Content-Type": "application/json",
...am.headers
diff --git a/ui/src/Components/AlertAck/index.test.js b/ui/src/Components/AlertAck/index.test.js
index 03e20c8b0..58155561f 100644
--- a/ui/src/Components/AlertAck/index.test.js
+++ b/ui/src/Components/AlertAck/index.test.js
@@ -37,6 +37,7 @@ beforeEach(() => {
publicURI: "http://example.com",
readonly: false,
headers: { foo: "bar" },
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -265,6 +266,7 @@ describe("", () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -276,6 +278,7 @@ describe("", () => {
publicURI: "http://am2.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -314,6 +317,7 @@ describe("", () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.js
index 1c8a122ef..3b633f702 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Alert/AlertMenu.test.js
@@ -27,6 +27,7 @@ beforeEach(() => {
publicURI: "http://example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.js
index a1c6df152..221a159b6 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/GroupHeader/GroupMenu.test.js
@@ -25,6 +25,7 @@ beforeEach(() => {
publicURI: "http://example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
diff --git a/ui/src/Components/ManagedSilence/DeleteSilence.js b/ui/src/Components/ManagedSilence/DeleteSilence.js
index c9caee3f2..60c352965 100644
--- a/ui/src/Components/ManagedSilence/DeleteSilence.js
+++ b/ui/src/Components/ManagedSilence/DeleteSilence.js
@@ -150,7 +150,8 @@ const DeleteSilenceModalContent = observer(
this.deleteState.fetch = FetchDelete(
`${alertmanager.uri}/api/v2/silence/${silence.id}`,
{
- headers: alertmanager.headers
+ headers: alertmanager.headers,
+ credentials: alertmanager.corsCredentials
}
)
.then(result => {
diff --git a/ui/src/Components/ManagedSilence/DeleteSilence.test.js b/ui/src/Components/ManagedSilence/DeleteSilence.test.js
index c30e6000e..b5f47ea77 100644
--- a/ui/src/Components/ManagedSilence/DeleteSilence.test.js
+++ b/ui/src/Components/ManagedSilence/DeleteSilence.test.js
@@ -207,6 +207,18 @@ describe("", () => {
});
});
+ it("uses CORS credentials from alertmanager config", async () => {
+ alertStore.data.upstreams.instances[0].corsCredentials = "omit";
+ await VerifyResponse({ status: "success" });
+ expect(fetch.mock.calls[1][0]).toBe(
+ "http://localhost:9093/api/v2/silence/04d37636-2350-4878-b382-e0b50353230f"
+ );
+ expect(fetch.mock.calls[1][1]).toMatchObject({
+ credentials: "omit",
+ method: "DELETE"
+ });
+ });
+
it("'Confirm' button is no-op after successful DELETE", async () => {
const tree = await VerifyResponse({ status: "success" });
expect(fetch.mock.calls[1][0]).toBe(
diff --git a/ui/src/Components/ManagedSilence/SilenceComment.test.js b/ui/src/Components/ManagedSilence/SilenceComment.test.js
index c9aa463f2..b2de351fa 100644
--- a/ui/src/Components/ManagedSilence/SilenceComment.test.js
+++ b/ui/src/Components/ManagedSilence/SilenceComment.test.js
@@ -46,6 +46,7 @@ const MockMultipleClusters = () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -57,6 +58,7 @@ const MockMultipleClusters = () => {
publicURI: "http://am2.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -68,6 +70,7 @@ const MockMultipleClusters = () => {
publicURI: "http://am3.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "second",
diff --git a/ui/src/Components/ManagedSilence/SilenceDetails.test.js b/ui/src/Components/ManagedSilence/SilenceDetails.test.js
index c711568ed..731581a16 100644
--- a/ui/src/Components/ManagedSilence/SilenceDetails.test.js
+++ b/ui/src/Components/ManagedSilence/SilenceDetails.test.js
@@ -37,7 +37,8 @@ beforeEach(() => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
diff --git a/ui/src/Components/ManagedSilence/index.stories.js b/ui/src/Components/ManagedSilence/index.stories.js
index e950a95e0..9733bc73f 100644
--- a/ui/src/Components/ManagedSilence/index.stories.js
+++ b/ui/src/Components/ManagedSilence/index.stories.js
@@ -33,7 +33,8 @@ storiesOf("ManagedSilence", module)
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
@@ -49,6 +50,7 @@ storiesOf("ManagedSilence", module)
publicURI: "http://example.com",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ro",
diff --git a/ui/src/Components/ManagedSilence/index.test.js b/ui/src/Components/ManagedSilence/index.test.js
index ab299a74e..47b63fee9 100644
--- a/ui/src/Components/ManagedSilence/index.test.js
+++ b/ui/src/Components/ManagedSilence/index.test.js
@@ -36,7 +36,8 @@ beforeEach(() => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
@@ -99,7 +100,8 @@ describe("", () => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
});
});
@@ -115,7 +117,8 @@ describe("", () => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
},
{
name: "am2",
@@ -126,7 +129,8 @@ describe("", () => {
readonly: true,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1", "am2"] }
@@ -144,7 +148,8 @@ describe("", () => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
});
});
diff --git a/ui/src/Components/SilenceModal/AlertManagerInput/index.test.js b/ui/src/Components/SilenceModal/AlertManagerInput/index.test.js
index 71cf47468..e785859e0 100644
--- a/ui/src/Components/SilenceModal/AlertManagerInput/index.test.js
+++ b/ui/src/Components/SilenceModal/AlertManagerInput/index.test.js
@@ -29,6 +29,7 @@ beforeEach(() => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -40,6 +41,7 @@ beforeEach(() => {
publicURI: "http://am2.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -51,6 +53,7 @@ beforeEach(() => {
publicURI: "http://am3.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "am3",
diff --git a/ui/src/Components/SilenceModal/Browser/index.test.js b/ui/src/Components/SilenceModal/Browser/index.test.js
index 25ce5a651..b730efe59 100644
--- a/ui/src/Components/SilenceModal/Browser/index.test.js
+++ b/ui/src/Components/SilenceModal/Browser/index.test.js
@@ -39,7 +39,8 @@ beforeEach(() => {
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
diff --git a/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.js b/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.js
index 8a805db15..fe33d6beb 100644
--- a/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.js
+++ b/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.js
@@ -109,6 +109,7 @@ const SilenceSubmitProgress = observer(
this.submitState.fetch = FetchPost(`${am.uri}/api/v2/silences`, {
body: JSON.stringify(payload),
+ credentials: am.corsCredentials,
headers: {
"Content-Type": "application/json",
...am.headers
diff --git a/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.test.js b/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.test.js
index baf40f331..0dfaf79eb 100644
--- a/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.test.js
+++ b/ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitProgress.test.js
@@ -17,6 +17,7 @@ beforeEach(() => {
publicURI: "http://example.com",
readonly: false,
headers: { foo: "bar" },
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "mockAlertmanager",
@@ -80,6 +81,16 @@ describe("", () => {
});
});
+ it("uses CORS credentials from alertmanager config", async () => {
+ alertStore.data.upstreams.instances[0].corsCredentials = "same-origin";
+ MountedSilenceSubmitProgress();
+ expect(fetch.mock.calls[0][0]).toBe("http://localhost/api/v2/silences");
+ expect(fetch.mock.calls[0][1]).toMatchObject({
+ credentials: "same-origin",
+ method: "POST"
+ });
+ });
+
it("will retry on another cluster member after fetch failure", async () => {
fetch
.mockRejectOnce(new Error("mock error message"))
@@ -93,6 +104,7 @@ describe("", () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -104,6 +116,7 @@ describe("", () => {
publicURI: "http://am2.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -151,6 +164,7 @@ describe("", () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -198,6 +212,7 @@ describe("", () => {
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
@@ -209,6 +224,7 @@ describe("", () => {
publicURI: "http://am2.example.com",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
diff --git a/ui/src/Components/SilenceModal/index.stories.js b/ui/src/Components/SilenceModal/index.stories.js
index abc9a6b21..ffe501cbf 100644
--- a/ui/src/Components/SilenceModal/index.stories.js
+++ b/ui/src/Components/SilenceModal/index.stories.js
@@ -49,6 +49,7 @@ storiesOf("SilenceModal", module)
publicURI: "http://example.com",
readonly: false,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -123,6 +124,7 @@ storiesOf("SilenceModal", module)
publicURI: "http://example.com",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -190,7 +192,8 @@ storiesOf("SilenceModal", module)
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
@@ -253,7 +256,8 @@ storiesOf("SilenceModal", module)
readonly: false,
error: "",
version: "0.17.0",
- headers: {}
+ headers: {},
+ corsCredentials: "include"
}
],
clusters: { am: ["am1"] }
diff --git a/ui/src/Models/API.js b/ui/src/Models/API.js
index 7f4d5af63..5d7ff6dfd 100644
--- a/ui/src/Models/API.js
+++ b/ui/src/Models/API.js
@@ -71,6 +71,8 @@ const APIAlertmanagerUpstream = PropTypes.exact({
publicURI: PropTypes.string.isRequired,
readonly: PropTypes.bool.isRequired,
headers: PropTypes.object.isRequired,
+ corsCredentials: PropTypes.oneOf(["omit", "same-origin", "include"])
+ .isRequired,
error: PropTypes.string.isRequired,
version: PropTypes.string.isRequired,
clusterMembers: PropTypes.arrayOf(PropTypes.string).isRequired
diff --git a/ui/src/Stores/AlertStore.test.js b/ui/src/Stores/AlertStore.test.js
index f02cd8f62..2d3200567 100644
--- a/ui/src/Stores/AlertStore.test.js
+++ b/ui/src/Stores/AlertStore.test.js
@@ -32,6 +32,7 @@ describe("AlertStore.data", () => {
publicURI: "http://example.com:8080",
readonly: false,
headers: { foo: "bar" },
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -43,6 +44,7 @@ describe("AlertStore.data", () => {
publicURI: "http://example.com",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -68,6 +70,7 @@ describe("AlertStore.data", () => {
publicURI: "http://example.com:8080",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
@@ -79,6 +82,7 @@ describe("AlertStore.data", () => {
publicURI: "http://example.com",
readonly: true,
headers: {},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "default",
diff --git a/ui/src/__mocks__/Alerts.js b/ui/src/__mocks__/Alerts.js
index 1244a7d4f..3937effda 100644
--- a/ui/src/__mocks__/Alerts.js
+++ b/ui/src/__mocks__/Alerts.js
@@ -74,6 +74,7 @@ const MockAlertmanager = () => ({
headers: {
Authorization: "Basic foo bar"
},
+ corsCredentials: "include",
error: "",
version: "0.17.0",
clusterMembers: ["default"]