From ef5e903418d4c07c021626d29e035bc9a47cbee3 Mon Sep 17 00:00:00 2001 From: Mathieu Parent Date: Thu, 22 Feb 2024 15:29:05 +0100 Subject: [PATCH] Support default SMTP TLS config Fixes: #3709 Signed-off-by: Mathieu Parent --- config/config.go | 58 +++++++++++++++++++++----------------- config/config_test.go | 12 ++++++-- config/notifiers.go | 28 +++++++++--------- docs/configuration.md | 4 ++- notify/email/email.go | 4 +-- notify/email/email_test.go | 2 +- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/config/config.go b/config/config.go index 35cd06dd..3588a305 100644 --- a/config/config.go +++ b/config/config.go @@ -365,6 +365,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } } for _, ec := range rcv.EmailConfigs { + if ec.TLSConfig == nil { + ec.TLSConfig = c.Global.SMTPTLSConfig + } if ec.Smarthost.String() == "" { if c.Global.SMTPSmarthost.String() == "" { return fmt.Errorf("no global SMTP smarthost set") @@ -629,12 +632,14 @@ func checkTimeInterval(r *Route, timeIntervals map[string]struct{}) error { // DefaultGlobalConfig returns GlobalConfig with default values. func DefaultGlobalConfig() GlobalConfig { defaultHTTPConfig := commoncfg.DefaultHTTPClientConfig - return GlobalConfig{ - ResolveTimeout: model.Duration(5 * time.Minute), - HTTPConfig: &defaultHTTPConfig, + defaultSMTPTLSConfig := commoncfg.TLSConfig{} + return GlobalConfig{ + ResolveTimeout: model.Duration(5 * time.Minute), + HTTPConfig: &defaultHTTPConfig, SMTPHello: "localhost", SMTPRequireTLS: true, + SMTPTLSConfig: &defaultSMTPTLSConfig, PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), OpsGenieAPIURL: mustParseURL("https://api.opsgenie.com/"), WeChatAPIURL: mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"), @@ -742,29 +747,30 @@ type GlobalConfig struct { HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"` - SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"` - SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"` - SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"` - SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"` - SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"` - SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"` - SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"` - SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"` - SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"` - SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"` - PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"` - OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"` - OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"` - OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"` - WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` - WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` - WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` - VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` - VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` - VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` - TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` - WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` + SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"` + SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"` + SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"` + SMTPAuthUsername string `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"` + SMTPAuthPassword Secret `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"` + SMTPAuthPasswordFile string `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"` + SMTPAuthSecret Secret `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"` + SMTPAuthIdentity string `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"` + SMTPRequireTLS bool `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"` + SMTPTLSConfig *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"` + SlackAPIURL *SecretURL `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"` + SlackAPIURLFile string `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"` + PagerdutyURL *URL `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"` + OpsGenieAPIURL *URL `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"` + OpsGenieAPIKey Secret `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"` + OpsGenieAPIKeyFile string `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"` + WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` + WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` + WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` + VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` + VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` + VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` + TelegramAPIUrl *URL `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"` + WebexAPIURL *URL `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig. diff --git a/config/config_test.go b/config/config_test.go index 7631d37c..25511327 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -857,9 +857,12 @@ func TestEmptyFieldsAndRegex(t *testing.T) { FollowRedirects: true, EnableHTTP2: true, }, - ResolveTimeout: model.Duration(5 * time.Minute), - SMTPSmarthost: HostPort{Host: "localhost", Port: "25"}, - SMTPFrom: "alertmanager@example.org", + ResolveTimeout: model.Duration(5 * time.Minute), + SMTPSmarthost: HostPort{Host: "localhost", Port: "25"}, + SMTPFrom: "alertmanager@example.org", + SMTPTLSConfig: &commoncfg.TLSConfig{ + InsecureSkipVerify: false, + }, SlackAPIURL: (*SecretURL)(mustParseURL("http://slack.example.com/")), SMTPRequireTLS: true, PagerdutyURL: mustParseURL("https://events.pagerduty.com/v2/enqueue"), @@ -905,6 +908,9 @@ func TestEmptyFieldsAndRegex(t *testing.T) { Smarthost: HostPort{Host: "localhost", Port: "25"}, HTML: "{{ template \"email.default.html\" . }}", RequireTLS: &boolFoo, + TLSConfig: &commoncfg.TLSConfig{ + InsecureSkipVerify: false, + }, }, }, }, diff --git a/config/notifiers.go b/config/notifiers.go index d79c8b50..7a601bb9 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -248,20 +248,20 @@ type EmailConfig struct { NotifierConfig `yaml:",inline" json:",inline"` // Email address to notify. - To string `yaml:"to,omitempty" json:"to,omitempty"` - From string `yaml:"from,omitempty" json:"from,omitempty"` - Hello string `yaml:"hello,omitempty" json:"hello,omitempty"` - Smarthost HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"` - AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"` - AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"` - AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"` - AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"` - AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"` - Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"` - HTML string `yaml:"html,omitempty" json:"html,omitempty"` - Text string `yaml:"text,omitempty" json:"text,omitempty"` - RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"` - TLSConfig commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` + To string `yaml:"to,omitempty" json:"to,omitempty"` + From string `yaml:"from,omitempty" json:"from,omitempty"` + Hello string `yaml:"hello,omitempty" json:"hello,omitempty"` + Smarthost HostPort `yaml:"smarthost,omitempty" json:"smarthost,omitempty"` + AuthUsername string `yaml:"auth_username,omitempty" json:"auth_username,omitempty"` + AuthPassword Secret `yaml:"auth_password,omitempty" json:"auth_password,omitempty"` + AuthPasswordFile string `yaml:"auth_password_file,omitempty" json:"auth_password_file,omitempty"` + AuthSecret Secret `yaml:"auth_secret,omitempty" json:"auth_secret,omitempty"` + AuthIdentity string `yaml:"auth_identity,omitempty" json:"auth_identity,omitempty"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"` + HTML string `yaml:"html,omitempty" json:"html,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` + RequireTLS *bool `yaml:"require_tls,omitempty" json:"require_tls,omitempty"` + TLSConfig *commoncfg.TLSConfig `yaml:"tls_config,omitempty" json:"tls_config,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. diff --git a/docs/configuration.md b/docs/configuration.md index b4b421d0..bd94aec5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -83,6 +83,8 @@ global: # The default SMTP TLS requirement. # Note that Go does not support unencrypted connections to remote SMTP endpoints. [ smtp_require_tls: | default = true ] + # The default TLS configuration for SMTP receivers + [ smtp_tls_config: ] # The API URL to use for Slack notifications. [ slack_api_url: ] @@ -884,7 +886,7 @@ to: # TLS configuration. tls_config: - [ ] + [ | default = global.smtp_tls_config ] # The HTML body of the email notification. [ html: | default = '{{ template "email.default.html" . }}' ] diff --git a/notify/email/email.go b/notify/email/email.go index d7e5be7b..457b80ab 100644 --- a/notify/email/email.go +++ b/notify/email/email.go @@ -131,7 +131,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { success = false ) if n.conf.Smarthost.Port == "465" { - tlsConfig, err := commoncfg.NewTLSConfig(&n.conf.TLSConfig) + tlsConfig, err := commoncfg.NewTLSConfig(n.conf.TLSConfig) if err != nil { return false, fmt.Errorf("parse TLS configuration: %w", err) } @@ -178,7 +178,7 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { return true, fmt.Errorf("'require_tls' is true (default) but %q does not advertise the STARTTLS extension", n.conf.Smarthost) } - tlsConf, err := commoncfg.NewTLSConfig(&n.conf.TLSConfig) + tlsConf, err := commoncfg.NewTLSConfig(n.conf.TLSConfig) if err != nil { return false, fmt.Errorf("parse TLS configuration: %w", err) } diff --git a/notify/email/email_test.go b/notify/email/email_test.go index 6303b85d..970c1177 100644 --- a/notify/email/email_test.go +++ b/notify/email/email_test.go @@ -407,7 +407,7 @@ func TestEmailNotifyWithSTARTTLS(t *testing.T) { Text: "Text body", RequireTLS: &trueVar, // MailDev embeds a self-signed certificate which can't be retrieved. - TLSConfig: commoncfg.TLSConfig{InsecureSkipVerify: true}, + TLSConfig: &commoncfg.TLSConfig{InsecureSkipVerify: true}, }, c.Server, )