[FIX] SMTP Notifier Unhandled Error Conditions (#585)
- Only attempt to close the connection once it's established. - Defer the client Quit/Close so that it always executes at the end. - Fixes #585pull/592/head
parent
722cbb63a0
commit
152b33e4fa
|
@ -14,7 +14,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SMTPNotifier a notifier to send emails to SMTP servers
|
// SMTPNotifier a notifier to send emails to SMTP servers.
|
||||||
type SMTPNotifier struct {
|
type SMTPNotifier struct {
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
|
@ -29,7 +29,7 @@ type SMTPNotifier struct {
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSMTPNotifier create an SMTPNotifier targeting a given address
|
// NewSMTPNotifier create an SMTPNotifier targeting a given address.
|
||||||
func NewSMTPNotifier(configuration schema.SMTPNotifierConfiguration) *SMTPNotifier {
|
func NewSMTPNotifier(configuration schema.SMTPNotifierConfiguration) *SMTPNotifier {
|
||||||
notifier := &SMTPNotifier{
|
notifier := &SMTPNotifier{
|
||||||
username: configuration.Username,
|
username: configuration.Username,
|
||||||
|
@ -47,15 +47,15 @@ func NewSMTPNotifier(configuration schema.SMTPNotifierConfiguration) *SMTPNotifi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *SMTPNotifier) initializeTLSConfig() {
|
func (n *SMTPNotifier) initializeTLSConfig() {
|
||||||
// Do not allow users to disable verification of certs if they have also set a trusted cert that was loaded
|
// Do not allow users to disable verification of certs if they have also set a trusted cert that was loaded.
|
||||||
// The second part of this check happens in the Configure Cert Pool code block
|
// The second part of this check happens in the Configure Cert Pool code block.
|
||||||
log.Debug("Notifier SMTP client initializing TLS configuration")
|
log.Debug("Notifier SMTP client initializing TLS configuration")
|
||||||
insecureSkipVerify := false
|
insecureSkipVerify := false
|
||||||
if n.disableVerifyCert {
|
if n.disableVerifyCert {
|
||||||
insecureSkipVerify = true
|
insecureSkipVerify = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configure Cert Pool
|
//Configure Cert Pool.
|
||||||
certPool, err := x509.SystemCertPool()
|
certPool, err := x509.SystemCertPool()
|
||||||
if err != nil || certPool == nil {
|
if err != nil || certPool == nil {
|
||||||
certPool = x509.NewCertPool()
|
certPool = x509.NewCertPool()
|
||||||
|
@ -89,9 +89,9 @@ func (n *SMTPNotifier) initializeTLSConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do startTLS if available (some servers only provide the auth extension after, and encryption is preferred)
|
// Do startTLS if available (some servers only provide the auth extension after, and encryption is preferred).
|
||||||
func (n *SMTPNotifier) startTLS() (bool, error) {
|
func (n *SMTPNotifier) startTLS() (bool, error) {
|
||||||
// Only start if not already encrypted
|
// Only start if not already encrypted.
|
||||||
if _, ok := n.client.TLSConnectionState(); ok {
|
if _, ok := n.client.TLSConnectionState(); ok {
|
||||||
log.Debugf("Notifier SMTP connection is already encrypted, skipping STARTTLS")
|
log.Debugf("Notifier SMTP connection is already encrypted, skipping STARTTLS")
|
||||||
return ok, nil
|
return ok, nil
|
||||||
|
@ -115,16 +115,16 @@ func (n *SMTPNotifier) startTLS() (bool, error) {
|
||||||
return ok, nil
|
return ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt Authentication
|
// Attempt Authentication.
|
||||||
func (n *SMTPNotifier) auth() (bool, error) {
|
func (n *SMTPNotifier) auth() (bool, error) {
|
||||||
// Attempt AUTH if password is specified only
|
// Attempt AUTH if password is specified only.
|
||||||
if n.password != "" {
|
if n.password != "" {
|
||||||
_, ok := n.client.TLSConnectionState()
|
_, ok := n.client.TLSConnectionState()
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, errors.New("Notifier SMTP client does not support authentication over plain text and the connection is currently plain text")
|
return false, errors.New("Notifier SMTP client does not support authentication over plain text and the connection is currently plain text")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the server supports AUTH, and get the mechanisms
|
// Check the server supports AUTH, and get the mechanisms.
|
||||||
ok, m := n.client.Extension("AUTH")
|
ok, m := n.client.Extension("AUTH")
|
||||||
if ok {
|
if ok {
|
||||||
log.Debugf("Notifier SMTP server supports authentication with the following mechanisms: %s", m)
|
log.Debugf("Notifier SMTP server supports authentication with the following mechanisms: %s", m)
|
||||||
|
@ -140,12 +140,12 @@ func (n *SMTPNotifier) auth() (bool, error) {
|
||||||
log.Debug("Notifier SMTP client attempting AUTH LOGIN with server")
|
log.Debug("Notifier SMTP client attempting AUTH LOGIN with server")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw error since AUTH extension is not supported
|
// Throw error since AUTH extension is not supported.
|
||||||
if auth == nil {
|
if auth == nil {
|
||||||
return false, fmt.Errorf("notifier SMTP server does not advertise a AUTH mechanism that are supported by Authelia (PLAIN or LOGIN are supported, but server advertised %s mechanisms)", m)
|
return false, fmt.Errorf("notifier SMTP server does not advertise a AUTH mechanism that are supported by Authelia (PLAIN or LOGIN are supported, but server advertised %s mechanisms)", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate
|
// Authenticate.
|
||||||
err := n.client.Auth(auth)
|
err := n.client.Auth(auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -196,7 +196,7 @@ func (n *SMTPNotifier) compose(recipient, subject, body string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial the SMTP server with the SMTPNotifier config
|
// Dial the SMTP server with the SMTPNotifier config.
|
||||||
func (n *SMTPNotifier) dial() error {
|
func (n *SMTPNotifier) dial() error {
|
||||||
log.Debugf("Notifier SMTP client attempting connection to %s", n.address)
|
log.Debugf("Notifier SMTP client attempting connection to %s", n.address)
|
||||||
client, err := smtp.Dial(n.address)
|
client, err := smtp.Dial(n.address)
|
||||||
|
@ -208,50 +208,46 @@ func (n *SMTPNotifier) dial() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closes the connection properly.
|
||||||
|
func (n *SMTPNotifier) cleanup() {
|
||||||
|
err := n.client.Quit()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Notifier SMTP client encountered error during cleanup: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send an email
|
// Send an email
|
||||||
func (n *SMTPNotifier) Send(recipient, subject, body string) error {
|
func (n *SMTPNotifier) Send(recipient, subject, body string) error {
|
||||||
err := n.dial()
|
if err := n.dial(); err != nil {
|
||||||
if err != nil {
|
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = n.startTLS()
|
// Always execute QUIT at the end once we're connected.
|
||||||
if err != nil {
|
defer n.cleanup()
|
||||||
_ = n.client.Close()
|
|
||||||
|
// Start TLS and then Authenticate.
|
||||||
|
if _, err := n.startTLS(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := n.auth(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = n.auth()
|
// Set the sender and recipient first.
|
||||||
if err != nil {
|
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the sender and recipient first
|
|
||||||
if err := n.client.Mail(n.sender); err != nil {
|
if err := n.client.Mail(n.sender); err != nil {
|
||||||
log.Debugf("Notifier SMTP failed while sending MAIL FROM (using sender) with error: %s", err)
|
log.Debugf("Notifier SMTP failed while sending MAIL FROM (using sender) with error: %s", err)
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.client.Rcpt(recipient); err != nil {
|
if err := n.client.Rcpt(recipient); err != nil {
|
||||||
log.Debugf("Notifier SMTP failed while sending RCPT TO (using recipient) with error: %s", err)
|
log.Debugf("Notifier SMTP failed while sending RCPT TO (using recipient) with error: %s", err)
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// compose and send the email body
|
// Compose and send the email body to the server.
|
||||||
if err := n.compose(recipient, subject, body); err != nil {
|
if err := n.compose(recipient, subject, body); err != nil {
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the QUIT command and close the connection
|
|
||||||
err = n.client.Quit()
|
|
||||||
if err != nil {
|
|
||||||
_ = n.client.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debug("Notifier SMTP client successfully sent email")
|
log.Debug("Notifier SMTP client successfully sent email")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue