Re-do #163: smtp: STARTTLS before querying auth mechanisms

This was not ported over with the rewrite.
This commit is contained in:
Michael Stapelberg 2016-03-04 19:42:06 +01:00
parent 43fcbe0695
commit 5158926bc3
2 changed files with 30 additions and 21 deletions

View File

@ -32,7 +32,8 @@ var (
NotifierConfig: NotifierConfig{
VSendResolved: false,
},
HTML: `{{ template "email.default.html" . }}`,
HTML: `{{ template "email.default.html" . }}`,
RequireTLS: true,
}
// DefaultEmailSubject defines the default Subject header of an Email.
@ -119,11 +120,12 @@ type EmailConfig struct {
NotifierConfig `yaml:",inline"`
// Email address to notify.
To string `yaml:"to"`
From string `yaml:"from"`
Smarthost string `yaml:"smarthost,omitempty"`
Headers map[string]string `yaml:"headers"`
HTML string `yaml:"html"`
To string `yaml:"to"`
From string `yaml:"from"`
Smarthost string `yaml:"smarthost,omitempty"`
Headers map[string]string `yaml:"headers"`
HTML string `yaml:"html"`
RequireTLS bool `yaml:"require_tls"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`

View File

@ -227,7 +227,7 @@ func NewEmail(c *config.EmailConfig, t *template.Template) *Email {
func (*Email) name() string { return "email" }
// auth resolves a string of authentication mechanisms.
func (n *Email) auth(mechs string) (smtp.Auth, *tls.Config, error) {
func (n *Email) auth(mechs string) (smtp.Auth, error) {
username := os.Getenv("SMTP_AUTH_USERNAME")
for _, mech := range strings.Split(mechs, " ") {
@ -237,7 +237,7 @@ func (n *Email) auth(mechs string) (smtp.Auth, *tls.Config, error) {
if secret == "" {
continue
}
return smtp.CRAMMD5Auth(username, secret), nil, nil
return smtp.CRAMMD5Auth(username, secret), nil
case "PLAIN":
password := os.Getenv("SMTP_AUTH_PASSWORD")
@ -249,16 +249,12 @@ func (n *Email) auth(mechs string) (smtp.Auth, *tls.Config, error) {
// We need to know the hostname for both auth and TLS.
host, _, err := net.SplitHostPort(n.conf.Smarthost)
if err != nil {
return nil, nil, fmt.Errorf("invalid address: %s", err)
return nil, fmt.Errorf("invalid address: %s", err)
}
var (
auth = smtp.PlainAuth(identity, username, password, host)
cfg = &tls.Config{ServerName: host}
)
return auth, cfg, nil
return smtp.PlainAuth(identity, username, password, host), nil
}
}
return nil, nil, nil
return nil, nil
}
// Notify implements the Notifier interface.
@ -270,16 +266,27 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) error {
}
defer c.Quit()
// We need to know the hostname for both auth and TLS.
host, _, err := net.SplitHostPort(n.conf.Smarthost)
if err != nil {
return fmt.Errorf("invalid address: %s", err)
}
if n.conf.RequireTLS {
if ok, _ := c.Extension("STARTTLS"); !ok {
return fmt.Errorf("require_tls: true (default), but %q does not advertise the STARTTLS extension", n.conf.Smarthost)
}
tlsConf := &tls.Config{ServerName: host}
if err := c.StartTLS(tlsConf); err != nil {
return fmt.Errorf("starttls failed: %s", err)
}
}
if ok, mech := c.Extension("AUTH"); ok {
auth, tlsConf, err := n.auth(mech)
auth, err := n.auth(mech)
if err != nil {
return err
}
if tlsConf != nil {
if err := c.StartTLS(tlsConf); err != nil {
return fmt.Errorf("starttls failed: %s", err)
}
}
if auth != nil {
if err := c.Auth(auth); err != nil {
return fmt.Errorf("%T failed: %s", auth, err)