mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Support tls config for webhook receiver (#93513)
Adds the ability to configure tls settings on the webhook receiver (e.g. to skip server certificate validation)
This commit is contained in:
@@ -289,14 +289,15 @@ type WebhookIntegration struct {
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
|
||||
HTTPMethod *string `json:"httpMethod,omitempty" yaml:"httpMethod,omitempty" hcl:"http_method"`
|
||||
MaxAlerts *int64 `json:"maxAlerts,omitempty" yaml:"maxAlerts,omitempty" hcl:"max_alerts"`
|
||||
AuthorizationScheme *string `json:"authorization_scheme,omitempty" yaml:"authorization_scheme,omitempty" hcl:"authorization_scheme"`
|
||||
AuthorizationCredentials *Secret `json:"authorization_credentials,omitempty" yaml:"authorization_credentials,omitempty" hcl:"authorization_credentials"`
|
||||
User *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"basic_auth_user"`
|
||||
Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"basic_auth_password"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
HTTPMethod *string `json:"httpMethod,omitempty" yaml:"httpMethod,omitempty" hcl:"http_method"`
|
||||
MaxAlerts *int64 `json:"maxAlerts,omitempty" yaml:"maxAlerts,omitempty" hcl:"max_alerts"`
|
||||
AuthorizationScheme *string `json:"authorization_scheme,omitempty" yaml:"authorization_scheme,omitempty" hcl:"authorization_scheme"`
|
||||
AuthorizationCredentials *Secret `json:"authorization_credentials,omitempty" yaml:"authorization_credentials,omitempty" hcl:"authorization_credentials"`
|
||||
User *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"basic_auth_user"`
|
||||
Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"basic_auth_password"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
TLSConfig *TLSConfig `json:"tlsConfig,omitempty" yaml:"tlsConfig,omitempty" hcl:"tlsConfig,block"`
|
||||
}
|
||||
|
||||
type WecomIntegration struct {
|
||||
|
||||
@@ -964,6 +964,48 @@ func GetAvailableNotifiers() []*NotifierPlugin {
|
||||
PropertyName: "message",
|
||||
Placeholder: alertingTemplates.DefaultMessageEmbed,
|
||||
},
|
||||
{
|
||||
Label: "TLS",
|
||||
PropertyName: "tlsConfig",
|
||||
Description: "TLS configuration options",
|
||||
Element: ElementTypeSubform,
|
||||
SubformOptions: []NotifierOption{
|
||||
{
|
||||
Label: "Disable certificate verification",
|
||||
Element: ElementTypeCheckbox,
|
||||
Description: "Do not verify the server'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 server'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 server.",
|
||||
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 server.",
|
||||
InputType: InputTypeText,
|
||||
PropertyName: "clientKey",
|
||||
Required: false,
|
||||
Secure: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestGetSecretKeysForContactPointType(t *testing.T) {
|
||||
{receiverType: "sensugo", expectedSecretFields: []string{"apikey"}},
|
||||
{receiverType: "teams", expectedSecretFields: []string{}},
|
||||
{receiverType: "telegram", expectedSecretFields: []string{"bottoken"}},
|
||||
{receiverType: "webhook", expectedSecretFields: []string{"password", "authorization_credentials"}},
|
||||
{receiverType: "webhook", expectedSecretFields: []string{"password", "authorization_credentials", "tlsConfig.caCertificate", "tlsConfig.clientCertificate", "tlsConfig.clientKey"}},
|
||||
{receiverType: "wecom", expectedSecretFields: []string{"url", "secret"}},
|
||||
{receiverType: "prometheus-alertmanager", expectedSecretFields: []string{"basicAuthPassword"}},
|
||||
{receiverType: "discord", expectedSecretFields: []string{"url"}},
|
||||
|
||||
@@ -22,6 +22,7 @@ func (s sender) SendWebhook(ctx context.Context, cmd *receivers.SendWebhookSetti
|
||||
HttpHeader: cmd.HTTPHeader,
|
||||
ContentType: cmd.ContentType,
|
||||
Validation: cmd.Validation,
|
||||
TLSConfig: cmd.TLSConfig,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -42,6 +43,7 @@ type SendWebhookSync struct {
|
||||
HttpHeader map[string]string
|
||||
ContentType string
|
||||
Validation func(body []byte, statusCode int) error
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
type SendResetPasswordEmailCommand struct {
|
||||
|
||||
@@ -120,7 +120,6 @@ func (ns *NotificationService) Run(ctx context.Context) error {
|
||||
select {
|
||||
case webhook := <-ns.webhookQueue:
|
||||
err := ns.sendWebRequestSync(context.Background(), webhook)
|
||||
|
||||
if err != nil {
|
||||
ns.log.Error("Failed to send webrequest ", "error", err)
|
||||
}
|
||||
@@ -155,6 +154,7 @@ func (ns *NotificationService) SendWebhookSync(ctx context.Context, cmd *SendWeb
|
||||
HttpMethod: cmd.HttpMethod,
|
||||
HttpHeader: cmd.HttpHeader,
|
||||
ContentType: cmd.ContentType,
|
||||
TLSConfig: cmd.TLSConfig,
|
||||
Validation: cmd.Validation,
|
||||
})
|
||||
}
|
||||
@@ -211,7 +211,6 @@ func (ns *NotificationService) SendEmailCommandHandlerSync(ctx context.Context,
|
||||
Subject: cmd.Subject,
|
||||
ReplyTo: cmd.ReplyTo,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -222,7 +221,6 @@ func (ns *NotificationService) SendEmailCommandHandlerSync(ctx context.Context,
|
||||
|
||||
func (ns *NotificationService) SendEmailCommandHandler(ctx context.Context, cmd *SendEmailCommand) error {
|
||||
message, err := ns.buildEmailMessage(cmd)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -304,7 +302,6 @@ func (ns *NotificationService) signUpStartedHandler(ctx context.Context, evt *ev
|
||||
"SignUpUrl": setting.ToAbsUrl(fmt.Sprintf("signup/?email=%s&code=%s", url.QueryEscape(evt.Email), url.QueryEscape(evt.Code))),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,11 +33,3 @@ func NewFakeDisconnectedMailer() *FakeDisconnectedMailer {
|
||||
func (fdm *FakeDisconnectedMailer) Send(ctx context.Context, messages ...*Message) (int, error) {
|
||||
return 0, fmt.Errorf("connect: connection refused")
|
||||
}
|
||||
|
||||
// NetClient is used to export original in test.
|
||||
var NetClient = &netClient
|
||||
|
||||
// SetWebhookClient is used to mock in test.
|
||||
func SetWebhookClient(client WebhookClient) {
|
||||
netClient = client
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
alertingReceivers "github.com/grafana/alerting/receivers"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@@ -23,6 +23,7 @@ type Webhook struct {
|
||||
HttpMethod string
|
||||
HttpHeader map[string]string
|
||||
ContentType string
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// Validation is a function that will validate the response body and statusCode of the webhook. Any returned error will cause the webhook request to be considered failed.
|
||||
// This can be useful when a webhook service communicates failures in creative ways, such as using the response body instead of the status code.
|
||||
@@ -34,21 +35,6 @@ type WebhookClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
var netTransport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
}
|
||||
var netClient WebhookClient = &http.Client{
|
||||
Timeout: time.Second * 30,
|
||||
Transport: netTransport,
|
||||
}
|
||||
|
||||
func (ns *NotificationService) sendWebRequestSync(ctx context.Context, webhook *Webhook) error {
|
||||
if webhook.HttpMethod == "" {
|
||||
webhook.HttpMethod = http.MethodPost
|
||||
@@ -85,7 +71,7 @@ func (ns *NotificationService) sendWebRequestSync(ctx context.Context, webhook *
|
||||
request.Header.Set(k, v)
|
||||
}
|
||||
|
||||
resp, err := netClient.Do(request)
|
||||
resp, err := alertingReceivers.NewTLSClient(webhook.TLSConfig).Do(request)
|
||||
if err != nil {
|
||||
return redactURL(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user