Skip to content

Commit

Permalink
smtp: STARTTLS before querying auth mechanisms
Browse files Browse the repository at this point in the history
Some servers (notably smtp.gmail.com) require you to use STARTTLS before
they will even list PLAIN authentication. This commit makes using gmail
for smtp notifications work.

Aside from STARTTLS being required for authentication, it’s of course
also a good idea to just use STARTTLS whenever possible, regardless of
the authentication requirements :).
  • Loading branch information
stapelberg committed Nov 28, 2015
1 parent ec60790 commit bb47752
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 29 deletions.
32 changes: 18 additions & 14 deletions manager/notifier.go
Expand Up @@ -516,9 +516,9 @@ func writeEmailBodyWithTime(w io.Writer, from, to, status string, a *Alert, mome
return nil
}

func getSMTPAuth(hasAuth bool, mechs string) (smtp.Auth, *tls.Config, error) {
func getSMTPAuth(hasAuth bool, mechs string) (smtp.Auth, error) {
if !hasAuth {
return nil, nil, nil
return nil, nil
}

username := os.Getenv("SMTP_AUTH_USERNAME")
Expand All @@ -530,7 +530,7 @@ func getSMTPAuth(hasAuth bool, 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")
if password == "" {
Expand All @@ -541,15 +541,14 @@ func getSMTPAuth(hasAuth bool, mechs string) (smtp.Auth, *tls.Config, error) {
// We need to know the hostname for both auth and TLS.
host, _, err := net.SplitHostPort(*smtpSmartHost)
if err != nil {
return nil, nil, fmt.Errorf("invalid address: %s", err)
return nil, fmt.Errorf("invalid address: %s", err)
}

auth := smtp.PlainAuth(identity, username, password, host)
cfg := &tls.Config{ServerName: host}
return auth, cfg, nil
return auth, nil
}
}
return nil, nil, nil
return nil, nil
}

func (n *notifier) sendEmailNotification(to string, op notificationOp, a *Alert) error {
Expand All @@ -567,16 +566,21 @@ func (n *notifier) sendEmailNotification(to string, op notificationOp, a *Alert)
}
defer c.Quit()

// Authenticate if we and the server are both configured for it.
auth, tlsConfig, err := getSMTPAuth(c.Extension("AUTH"))
// We need to know the hostname for both auth and TLS.
host, _, err := net.SplitHostPort(*smtpSmartHost)
if err != nil {
return err
return fmt.Errorf("invalid address: %s", err)
}

if tlsConfig != nil {
if err := c.StartTLS(tlsConfig); err != nil {
return fmt.Errorf("starttls failed: %s", err)
}
tlsConfig := &tls.Config{ServerName: host}
if err := c.StartTLS(tlsConfig); err != nil {
return fmt.Errorf("starttls failed: %s", err)
}

// Authenticate if we and the server are both configured for it.
auth, err := getSMTPAuth(c.Extension("AUTH"))
if err != nil {
return err
}

if auth != nil {
Expand Down
18 changes: 3 additions & 15 deletions manager/notifier_test.go
Expand Up @@ -15,7 +15,6 @@ package manager

import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"html"
Expand Down Expand Up @@ -85,7 +84,7 @@ type authTestCase struct {
}

func (tc *authTestCase) test(t *testing.T) {
auth, cfg, err := getSMTPAuth(tc.hasAuth, tc.mechs)
auth, err := getSMTPAuth(tc.hasAuth, tc.mechs)
if err != nil {
tc.fail(t, "unexpected error: %s", err)
return
Expand All @@ -94,21 +93,10 @@ func (tc *authTestCase) test(t *testing.T) {
if auth != nil {
tc.fail(t, "expected auth to be nil, got %T", auth)
}
if cfg != nil {
tc.fail(t, "expected tls config to be nil, got %v", cfg)
}
} else {
if fmt.Sprintf("%T", auth) != tc.expAuthType {
tc.fail(t, "expected auth to be %s, got %T", tc.expAuthType, auth)
}
if tc.expAuthType == "*smtp.plainAuth" {
if cfg == nil {
tc.fail(t, "expected tls config")
} else if cfg.ServerName != "testSMTPHost" {
tc.fail(t, "expected tls config to be %v, got %v",
&tls.Config{ServerName: "testSMTPHost"}, cfg)
}
}
}
}

Expand Down Expand Up @@ -179,8 +167,8 @@ func TestGetSMTPAuth(t *testing.T) {
{true, "PLAIN", ""},
})
os.Setenv("SMTP_AUTH_PASSWORD", "p")
if auth, cfg, err := getSMTPAuth(true, "PLAIN"); err == nil {
t.Errorf("PLAIN auth with bad host-port: expected error but got %T, %v", auth, cfg)
if auth, err := getSMTPAuth(true, "PLAIN"); err == nil {
t.Errorf("PLAIN auth with bad host-port: expected error but got %T", auth)
}
}

Expand Down

0 comments on commit bb47752

Please sign in to comment.