From e59ea00518b9a147394edcde31f20f772aeec47f Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 17 Sep 2024 21:11:16 +0200 Subject: [PATCH] Alerting: Add TLS, QoS and retain options to the MQTT receiver (#92331) --- .github/CODEOWNERS | 1 + devenv/docker/blocks/mqtt/README.md | 31 +++++++++ devenv/docker/blocks/mqtt/build/Dockerfile | 9 +++ devenv/docker/blocks/mqtt/build/gen_certs.sh | 18 +++++ devenv/docker/blocks/mqtt/build/san.cnf | 7 ++ devenv/docker/blocks/mqtt/docker-compose.yaml | 12 ++++ devenv/docker/blocks/mqtt/nanomq.conf | 40 +++++++++++ devenv/docker/blocks/mqtt/nanomq_acl.conf | 7 ++ devenv/docker/blocks/mqtt/nanomq_pwd.conf | 2 + .../administration/provisioning/index.md | 23 +++++-- .../file-provisioning/index.md | 14 +++- go.mod | 6 +- go.sum | 12 ++-- go.work.sum | 4 +- .../ngalert/api/compat_contact_points.go | 6 ++ .../ngalert/api/compat_contact_points_test.go | 26 +++++++ .../api/tooling/definitions/contact_points.go | 25 ++++--- .../channels_config/available_channels.go | 69 ++++++++++++++++++- .../receivers/form/fields/OptionField.tsx | 38 ++++++---- 19 files changed, 310 insertions(+), 40 deletions(-) create mode 100644 devenv/docker/blocks/mqtt/README.md create mode 100644 devenv/docker/blocks/mqtt/build/Dockerfile create mode 100755 devenv/docker/blocks/mqtt/build/gen_certs.sh create mode 100644 devenv/docker/blocks/mqtt/build/san.cnf create mode 100644 devenv/docker/blocks/mqtt/docker-compose.yaml create mode 100644 devenv/docker/blocks/mqtt/nanomq.conf create mode 100644 devenv/docker/blocks/mqtt/nanomq_acl.conf create mode 100644 devenv/docker/blocks/mqtt/nanomq_pwd.conf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6221d497bb5..9858f0a295b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -199,6 +199,7 @@ /devenv/docker/blocks/mariadb/ @grafana/oss-big-tent /devenv/docker/blocks/memcached/ @grafana/grafana-backend-group /devenv/docker/blocks/mimir_backend/ @grafana/alerting-backend +/devenv/docker/blocks/mqtt/ @grafana/alerting-backend /devenv/docker/blocks/mssql/ @grafana/partner-datasources /devenv/docker/blocks/mssql_arm64/ @grafana/partner-datasources /devenv/docker/blocks/mssql_tests/ @grafana/partner-datasources diff --git a/devenv/docker/blocks/mqtt/README.md b/devenv/docker/blocks/mqtt/README.md new file mode 100644 index 00000000000..6724fa17666 --- /dev/null +++ b/devenv/docker/blocks/mqtt/README.md @@ -0,0 +1,31 @@ +# NanoMQ MQTT broker + +Starts a [NanoMQ MQTT broker](https://nanomq.io/docs/en/latest/). + +## Authentication + +The broker is configured to use a simple username/password authentication. +See [./nanomq_pwd.conf](./nanomq_pwd.conf) for the default credentials. + +## TLS Certificates + +If you want to configure an MQTT contact point in Grafana Alerting with TLS, you need to provide a certificate and key. + +You can find them in `/etc/certs` directory in the container: + +``` shell +docker exec devenv-mqtt-1 ls /etc/certs/ +``` + +### CA Certificate + +``` shell +docker exec devenv-mqtt-1 cat /etc/certs/ca.pem +``` + +### Client certificates + +``` shell +docker exec devenv-mqtt-1 cat /etc/certs/client.pem +docker exec devenv-mqtt-1 cat /etc/certs/client.key +``` diff --git a/devenv/docker/blocks/mqtt/build/Dockerfile b/devenv/docker/blocks/mqtt/build/Dockerfile new file mode 100644 index 00000000000..e3f97cdba74 --- /dev/null +++ b/devenv/docker/blocks/mqtt/build/Dockerfile @@ -0,0 +1,9 @@ +FROM emqx/nanomq:0.21.11-full + +RUN apt-get update && apt-get install -y \ + openssl \ + && rm -rf /var/lib/apt/lists/* + +COPY ./san.cnf /etc/certs/san.cnf +COPY ./gen_certs.sh /etc/certs/gen_certs.sh +RUN /etc/certs/gen_certs.sh diff --git a/devenv/docker/blocks/mqtt/build/gen_certs.sh b/devenv/docker/blocks/mqtt/build/gen_certs.sh new file mode 100755 index 00000000000..c1ea129d60c --- /dev/null +++ b/devenv/docker/blocks/mqtt/build/gen_certs.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +DAYS_VALID=3650 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Create CA certificate +openssl genpkey -algorithm RSA -out "$SCRIPT_DIR/ca.key" +openssl req -new -x509 -days $DAYS_VALID -key "$SCRIPT_DIR/ca.key" -out "$SCRIPT_DIR/ca.pem" -subj "/CN=My CA" + +# Create server certificate +openssl genpkey -algorithm RSA -out "$SCRIPT_DIR/server.key" +openssl req -new -key "$SCRIPT_DIR/server.key" -out "$SCRIPT_DIR/server.csr" -subj "/CN=localhost" +openssl x509 -req -days $DAYS_VALID -in "$SCRIPT_DIR/server.csr" -CA "$SCRIPT_DIR/ca.pem" -CAkey "$SCRIPT_DIR/ca.key" -CAcreateserial -out "$SCRIPT_DIR/server.pem" -extfile "$SCRIPT_DIR/san.cnf" -extensions v3_req + +# Create client key and certificate +openssl genpkey -algorithm RSA -out "$SCRIPT_DIR/client.key" +openssl req -new -key "$SCRIPT_DIR/client.key" -out "$SCRIPT_DIR/client.csr" -subj "/CN=Client" +openssl x509 -req -days $DAYS_VALID -in "$SCRIPT_DIR/client.csr" -CA "$SCRIPT_DIR/ca.pem" -CAkey "$SCRIPT_DIR/ca.key" -CAcreateserial -out "$SCRIPT_DIR/client.pem" -extfile "$SCRIPT_DIR/san.cnf" -extensions v3_req diff --git a/devenv/docker/blocks/mqtt/build/san.cnf b/devenv/docker/blocks/mqtt/build/san.cnf new file mode 100644 index 00000000000..d7148ac8d4f --- /dev/null +++ b/devenv/docker/blocks/mqtt/build/san.cnf @@ -0,0 +1,7 @@ +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +IP.1 = 127.0.0.1 +IP.2 = ::1 diff --git a/devenv/docker/blocks/mqtt/docker-compose.yaml b/devenv/docker/blocks/mqtt/docker-compose.yaml new file mode 100644 index 00000000000..bdd5437f500 --- /dev/null +++ b/devenv/docker/blocks/mqtt/docker-compose.yaml @@ -0,0 +1,12 @@ + mqtt: + build: + context: docker/blocks/mqtt/build + ports: + - "127.0.0.1:1883:1883" # MQTT + - "127.0.0.1:8883:8883" # MQTT over TLS + - "127.0.0.1:8083:8083" # MQTT over WS + - "127.0.0.1:8443:8443" # MQTT over WSS + volumes: + - ${PWD}/docker/blocks/mqtt/nanomq.conf:/etc/nanomq.conf + - ${PWD}/docker/blocks/mqtt/nanomq_pwd.conf:/etc/nanomq_pwd.conf + - ${PWD}/docker/blocks/mqtt/nanomq_acl.conf:/etc/nanomq_acl.conf diff --git a/devenv/docker/blocks/mqtt/nanomq.conf b/devenv/docker/blocks/mqtt/nanomq.conf new file mode 100644 index 00000000000..90a27f22404 --- /dev/null +++ b/devenv/docker/blocks/mqtt/nanomq.conf @@ -0,0 +1,40 @@ +log { + to=console + level=info +} + +listeners.tcp { + bind = "0.0.0.0:1883" +} + + +listeners.ssl { + bind = "0.0.0.0:8883" + + keyfile = "/etc/certs/server.key" + certfile = "/etc/certs/server.pem" + cacertfile = "/etc/certs/ca.pem" + + # Change these settings to true if you want to deny + # access for clients that don't have a certificate. + verify_peer = false + fail_if_no_peer_cert = false +} + +listeners.ws { + bind = "0.0.0.0:8083" +} + +listeners.wss { + bind = "0.0.0.0:8443" +} + +auth { + allow_anonymous = false + no_match = deny + deny_action = disconnect + password = {include "/etc/nanomq_pwd.conf"} + acl = { + include "/etc/nanomq_acl.conf" + } +} diff --git a/devenv/docker/blocks/mqtt/nanomq_acl.conf b/devenv/docker/blocks/mqtt/nanomq_acl.conf new file mode 100644 index 00000000000..cc12fe9090b --- /dev/null +++ b/devenv/docker/blocks/mqtt/nanomq_acl.conf @@ -0,0 +1,7 @@ +rules = [ + {"permit": "allow", "username": "grafana", "action": "subscribe", "topics": ["#"]} + {"permit": "allow", "username": "grafana", "action": "publish", "topics": ["#"]} + {"permit": "allow", "username": "admin", "action": "subscribe", "topics": ["#"]} + {"permit": "allow", "username": "admin", "action": "publish", "topics": ["#"]} + {"permit": "deny"} +] diff --git a/devenv/docker/blocks/mqtt/nanomq_pwd.conf b/devenv/docker/blocks/mqtt/nanomq_pwd.conf new file mode 100644 index 00000000000..7fd24d15e41 --- /dev/null +++ b/devenv/docker/blocks/mqtt/nanomq_pwd.conf @@ -0,0 +1,2 @@ +admin:admin +grafana:grafana diff --git a/docs/sources/administration/provisioning/index.md b/docs/sources/administration/provisioning/index.md index 22526d4a05c..12e3abce9d9 100644 --- a/docs/sources/administration/provisioning/index.md +++ b/docs/sources/administration/provisioning/index.md @@ -506,15 +506,26 @@ The following sections detail the supported settings and secure settings for eac #### Alert notification `MQTT` +| Name | Secure setting | +| ------------- | -------------- | +| brokerUrl | | +| clientId | | +| topic | | +| messageFormat | | +| username | | +| password | yes | +| retain | | +| qos | | +| tlsConfig | | + +##### TLS config + | Name | Secure setting | | ------------------ | -------------- | -| brokerUrl | | -| clientId | | -| topic | | -| messageFormat | -| username | | -| password | yes | | insecureSkipVerify | | +| clientCertificate | yes | +| clientKey | yes | +| caCertificate | yes | #### Alert notification `pagerduty` diff --git a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md index f90c5bfa9db..11d92dc2243 100644 --- a/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md +++ b/docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md @@ -362,8 +362,20 @@ settings: username: grafana # password: password1 + # + qos: 0 # - insecureSkipVerify: false + retain: false + # + tlsConfig: + # + insecureSkipVerify: false + # + clientCertificate: certificate in PEM format + # + clientKey: key in PEM format + # + caCertificate: CA certificate in PEM format ``` {{< /collapse >}} diff --git a/go.mod b/go.mod index fae3c94b651..0a027711cd6 100644 --- a/go.mod +++ b/go.mod @@ -73,9 +73,9 @@ require ( github.com/googleapis/gax-go/v2 v2.13.0 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20240829185616-8454ac21d7e5 // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 // @grafana/identity-access-team - github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team + github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10 // @grafana/alerting-backend + github.com/grafana/authlib v0.0.0-20240827201526-24af227df935 // @grafana/identity-access-team + github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd // @grafana/identity-access-team github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics diff --git a/go.sum b/go.sum index faa2f92390a..81961225018 100644 --- a/go.sum +++ b/go.sum @@ -2255,12 +2255,12 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20240829185616-8454ac21d7e5 h1:gQprHfu5/GT/mpPuRm3QVL+7+0j1QsvKJuPIzQAsezM= -github.com/grafana/alerting v0.0.0-20240829185616-8454ac21d7e5/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= -github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9 h1:e+kFqd2sECBhbxOV1NoVxsudLygNQuu9bO+7FjNTkXo= -github.com/grafana/authlib v0.0.0-20240906122029-0100695765b9/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= -github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= -github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10 h1:oDbLKM34O+JUF9EQFS+9aYhdYoeNfUpXqNjFCLIxwF4= +github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= +github.com/grafana/authlib v0.0.0-20240827201526-24af227df935 h1:nT4UY61s2flsiLkU2jDqtqFhOLwqh355+8ZhnavKoMQ= +github.com/grafana/authlib v0.0.0-20240827201526-24af227df935/go.mod h1:ER7bMzNNWTN/5Zl3pwqfgS6XEhcanjrvL7lOp8Ow6oc= +github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd h1:sIlR7n38/MnZvX2qxDEszywXdI5soCwQ78aTDSARvus= +github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= diff --git a/go.work.sum b/go.work.sum index c7e5d42c9bb..66172150efb 100644 --- a/go.work.sum +++ b/go.work.sum @@ -559,12 +559,14 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18 h1:3cQ+d+fkNL2EqpARaBVG34KlVz7flDujYfDx3njvdh8= +github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= github.com/grafana/gomemcache v0.0.0-20240229205252-cd6a66d6fb56/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= github.com/grafana/thema v0.0.0-20230511182720-3146087fcc26 h1:HX927q4X1n451pnGb8U0wq74i8PCzuxVjzv7TyD10kc= github.com/grafana/thema v0.0.0-20230511182720-3146087fcc26/go.mod h1:Pn9nfzCk7nV0mvNgwusgCjCROZP6nm4GpwTnmEhLT24= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= diff --git a/pkg/services/ngalert/api/compat_contact_points.go b/pkg/services/ngalert/api/compat_contact_points.go index e0fa3b777dc..d10c3859598 100644 --- a/pkg/services/ngalert/api/compat_contact_points.go +++ b/pkg/services/ngalert/api/compat_contact_points.go @@ -401,6 +401,12 @@ func (c contactPointsExtension) UpdateStructDescriptor(structDescriptor *jsonite desc.Decoder = codec desc.Encoder = codec } + if structDescriptor.Type == reflect2.TypeOf(definitions.MqttIntegration{}) { + codec := &numberAsStringCodec{ignoreError: true} + desc := structDescriptor.GetField("QoS") + desc.Decoder = codec + desc.Encoder = codec + } } type emailAddressCodec struct{} diff --git a/pkg/services/ngalert/api/compat_contact_points_test.go b/pkg/services/ngalert/api/compat_contact_points_test.go index 8700212edba..918b686d1c6 100644 --- a/pkg/services/ngalert/api/compat_contact_points_test.go +++ b/pkg/services/ngalert/api/compat_contact_points_test.go @@ -184,4 +184,30 @@ func TestContactPointFromContactPointExports(t *testing.T) { require.Nil(t, result.OnCall[1].MaxAlerts) require.Nil(t, result.OnCall[2].MaxAlerts) }) + + t.Run("mqtt with optional numbers as string", func(t *testing.T) { + export := definitions.ContactPointExport{ + Name: "test", + Receivers: []definitions.ReceiverExport{ + { + Type: "mqtt", + Settings: definitions.RawMessage(`{ "qos" : "112" }`), + }, + { + Type: "mqtt", + Settings: definitions.RawMessage(`{ "qos" : "test" }`), + }, + { + Type: "mqtt", + Settings: definitions.RawMessage(`{ "qos" : null }`), + }, + }, + } + result, err := ContactPointFromContactPointExport(export) + require.NoError(t, err) + require.Len(t, result.Mqtt, 3) + require.Equal(t, int64(112), *result.Mqtt[0].QoS) + require.Nil(t, result.Mqtt[1].QoS) + require.Nil(t, result.Mqtt[2].QoS) + }) } diff --git a/pkg/services/ngalert/api/tooling/definitions/contact_points.go b/pkg/services/ngalert/api/tooling/definitions/contact_points.go index dfcd878df6d..0cd8f792e4e 100644 --- a/pkg/services/ngalert/api/tooling/definitions/contact_points.go +++ b/pkg/services/ngalert/api/tooling/definitions/contact_points.go @@ -85,17 +85,26 @@ type LineIntegration struct { Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"` } +type TLSConfig struct { + InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" hcl:"insecure_skip_verify"` + TLSCACertificate *Secret `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty" hcl:"ca_certificate"` + TLSClientCertificate *Secret `json:"clientCertificate,omitempty" yaml:"clientCertificate,omitempty" hcl:"client_certificate"` + TLSClientKey *Secret `json:"clientKey,omitempty" yaml:"clientKey,omitempty" hcl:"client_key"` +} + type MqttIntegration struct { DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"` - BrokerURL *string `json:"brokerUrl,omitempty" yaml:"brokerUrl,omitempty" hcl:"broker_url"` - ClientID *string `json:"clientId,omitempty" yaml:"clientId,omitempty" hcl:"client_id"` - Topic *string `json:"topic,omitempty" yaml:"topic,omitempty" hcl:"topic"` - Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"` - MessageFormat *string `json:"messageFormat,omitempty" yaml:"messageFormat,omitempty" hcl:"message_format"` - Username *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"username"` - Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"password"` - InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" hcl:"insecure_skip_verify"` + BrokerURL *string `json:"brokerUrl,omitempty" yaml:"brokerUrl,omitempty" hcl:"broker_url"` + ClientID *string `json:"clientId,omitempty" yaml:"clientId,omitempty" hcl:"client_id"` + Topic *string `json:"topic,omitempty" yaml:"topic,omitempty" hcl:"topic"` + Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"` + MessageFormat *string `json:"messageFormat,omitempty" yaml:"messageFormat,omitempty" hcl:"message_format"` + Username *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"username"` + Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"password"` + QoS *int64 `json:"qos,omitempty" yaml:"qos,omitempty" hcl:"qos"` + Retain *bool `json:"retain,omitempty" yaml:"retain,omitempty" hcl:"retain"` + TLSConfig *TLSConfig `json:"tlsConfig,omitempty" yaml:"tlsConfig,omitempty" hcl:"tls_config,block"` } type OnCallIntegration struct { diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels.go b/pkg/services/ngalert/notifier/channels_config/available_channels.go index 33a29ae2c0f..4a6433ebb5e 100644 --- a/pkg/services/ngalert/notifier/channels_config/available_channels.go +++ b/pkg/services/ngalert/notifier/channels_config/available_channels.go @@ -1325,12 +1325,75 @@ func GetAvailableNotifiers() []*NotifierPlugin { Secure: true, }, { - Label: "Disable certificate verification", + Label: "QoS", + Element: ElementTypeSelect, + SelectOptions: []SelectOption{ + { + Value: "0", + Label: "At most once (0)", + }, + { + Value: "1", + Label: "At least once (1)", + }, + { + Value: "2", + Label: "Exactly once (2)", + }, + }, + Description: "The quality of service to use when sending the message.", + PropertyName: "qos", + Required: false, + }, + { + Label: "Retain", + Description: "If set to true, the message will be retained by the broker.", Element: ElementTypeCheckbox, - Description: "Do not verify the broker's certificate chain and host name.", - PropertyName: "insecureSkipVerify", + PropertyName: "retain", Required: false, }, + { + Label: "TLS", + PropertyName: "tlsConfig", + Description: "TLS configuration options", + Element: ElementTypeSubform, + SubformOptions: []NotifierOption{ + { + Label: "Disable certificate verification", + Element: ElementTypeCheckbox, + Description: "Do not verify the broker's certificate chain and host name.", + PropertyName: "insecureSkipVerify", + Required: false, + }, + { + Label: "CA Certificate", + Element: ElementTypeTextArea, + Description: "Certificate in PEM format to use when verifying the broker's certificate chain.", + InputType: InputTypeText, + PropertyName: "caCertificate", + Required: false, + Secure: true, + }, + { + Label: "Client Certificate", + Element: ElementTypeTextArea, + Description: "Client certificate in PEM format to use when connecting to the broker.", + InputType: InputTypeText, + PropertyName: "clientCertificate", + Required: false, + Secure: true, + }, + { + Label: "Client Key", + Element: ElementTypeTextArea, + Description: "Client key in PEM format to use when connecting to the broker.", + InputType: InputTypeText, + PropertyName: "clientKey", + Required: false, + Secure: true, + }, + }, + }, }, }, { diff --git a/public/app/features/alerting/unified/components/receivers/form/fields/OptionField.tsx b/public/app/features/alerting/unified/components/receivers/form/fields/OptionField.tsx index e46e5b7fc80..b28228c4ab0 100644 --- a/public/app/features/alerting/unified/components/receivers/form/fields/OptionField.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/fields/OptionField.tsx @@ -4,7 +4,17 @@ import { FC, useEffect } from 'react'; import { Controller, DeepMap, FieldError, useFormContext } from 'react-hook-form'; import { GrafanaTheme2 } from '@grafana/data'; -import { Checkbox, Field, Input, RadioButtonList, SecretInput, Select, TextArea, useStyles2 } from '@grafana/ui'; +import { + Checkbox, + Field, + Input, + RadioButtonList, + SecretInput, + SecretTextArea, + Select, + TextArea, + useStyles2, +} from '@grafana/ui'; import { NotificationChannelOption, NotificationChannelSecureFields } from 'app/types'; import { KeyValueMapInput } from './KeyValueMapInput'; @@ -222,17 +232,21 @@ const OptionInput: FC = ({ name={name} onSelectTemplate={onSelectTemplate} > -