Force TLS and valid x509 certs in SMTP Notifier by default
- Adjust AUTH LOGIN functionality to be closer to AUTH PLAIN - Removed: secure (notifier smtp conf) boolean string - Added: disable_verify_cert (notifier smtp conf) boolean - disables X509 validation of certificates - Added: disable_require_tls (notifier smtp conf) boolean - allows emails to be sent over plain text (for non-authenticated only) - Added: trusted_cert (notifier smtp conf) string (path) - allows specifying the path of a PEM format cert to add to trusted cert pool - Make SMTP notifier return errors on connection over plain text - Make SMTP notifier return errors on TLS connection with invalid certs - Implemented various debug logging for the SMTP notifier - Implemented explicit SMTP closes on errors (previously left con open) - Split SMTPNotifier Send func to seperate funcs for: - writing future test suites and startup checks more easily - organization and readability - Add details of changes to docs/security.yml - Adjust config.yml's (template and test) for the changespull/546/head
parent
1ef3485418
commit
242386e279
|
@ -277,14 +277,26 @@ notifier:
|
|||
## filesystem:
|
||||
## filename: /tmp/authelia/notification.txt
|
||||
|
||||
# Use a SMTP server for sending notifications. Authelia uses PLAIN method to authenticate.
|
||||
# [Security] Make sure the connection is made over TLS otherwise your password will transit in plain text.
|
||||
# Use a SMTP server for sending notifications. Authelia uses PLAIN or LOGIN method to authenticate.
|
||||
# [Security] By default Authelia will:
|
||||
# - force all SMTP connections over TLS including unauthenticated connections
|
||||
# - use the disable_require_tls boolean value to disable this requirement (only works for unauthenticated connections)
|
||||
# - validate the SMTP server x509 certificate during the TLS handshake against the hosts trusted certificates
|
||||
# - trusted_cert option:
|
||||
# - this is a string value, that may specify the path of a PEM format cert, it is completely optional
|
||||
# - if it is not set, a blank string, or an invalid path; will still trust the host machine/containers cert store
|
||||
# - defaults to the host machine (or docker container's) trusted certificate chain for validation
|
||||
# - use the trusted_cert string value to specify the path of a PEM format public cert to trust in addition to the hosts trusted certificates
|
||||
# - use the disable_verify_cert boolean value to disable the validation (prefer the trusted_cert option as it's more secure)
|
||||
smtp:
|
||||
username: test
|
||||
password: password
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
## disable_require_tls: false
|
||||
## disable_verify_cert: false
|
||||
## trusted_cert: ""
|
||||
# Sending an email using a Gmail account is as simple as the next section.
|
||||
# You need to create an app password by following: https://support.google.com/accounts/answer/185833?hl=en
|
||||
## smtp:
|
||||
|
|
|
@ -21,6 +21,49 @@ that the attacker must also require the certificate to retrieve the cookies.
|
|||
Note that using [HSTS] has consequences. That's why you should read the blog
|
||||
post nginx has written on [HSTS].
|
||||
|
||||
## Notifier security measures (SMTP)
|
||||
|
||||
By default the SMTP Notifier implementation does not allow connections that are not secure.
|
||||
As such all connections require the following:
|
||||
|
||||
1. STARTTLS before authentication or sending emails (unauthenticated connections
|
||||
require it as well)
|
||||
2. Valid X509 Certificate presented to the client during the STARTTLS handshake
|
||||
|
||||
There is an option to disable both of these security measures however they are
|
||||
not recommended. You should only do this in a situation where you control all
|
||||
networks between Authelia and the SMTP server. The following configuration options
|
||||
exist to configure the security level:
|
||||
|
||||
### Configuration Option: disable_verify_cert
|
||||
|
||||
This is a YAML boolean type (true/false, y/n, 1/0, etc). This disables the X509 PKI
|
||||
verification mechanism. We recommend using the trusted_cert option over this, as
|
||||
disabling this security feature makes you vulnerable to MITM attacks.
|
||||
|
||||
### Configuration Option: disable_require_tls
|
||||
|
||||
This is a YAML boolean type (true/false, y/n, 1/0, etc). This disables the
|
||||
requirement that all connections must be over TLS. This is only usable currently
|
||||
with authentication disabled (comment the password) and as such is only an
|
||||
option for SMTP servers that allow unauthenticated relay (bad practice).
|
||||
|
||||
### Configuration Option: trusted_cert
|
||||
This is a YAML string type. This specifies the file location of a pub certificate
|
||||
that can be used to validate the authenticity of a server with a self signed
|
||||
certificate. This can either be the public cert of the certificate authority
|
||||
used to sign the certificate or the public key itself. They must be in the PEM
|
||||
format. The certificate is added in addition to the certificates trusted by the
|
||||
;host machine. If the certificate is invalid, inaccessible, or is otherwise not
|
||||
configured; Authelia just uses the hosts certificates.
|
||||
|
||||
### Explanation
|
||||
There are a few reasons for the security measures implemented:
|
||||
1. Transmitting usernames and passwords over plain-text is an obvious vulnerability
|
||||
2. The emails generated by Authelia, if transmitted in plain-text could allow
|
||||
an attacker to intercept a link used to setup 2FA; which reduces security
|
||||
3. Not validating the identity of the server allows man-in-the-middle attacks
|
||||
|
||||
## More protections measures with Nginx
|
||||
|
||||
You can also apply the following headers to your nginx configuration for
|
||||
|
|
|
@ -100,3 +100,4 @@ notifier:
|
|||
host: "mailcatcher-service"
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -15,12 +15,14 @@ type EmailNotifierConfiguration struct {
|
|||
|
||||
// SMTPNotifierConfiguration represents the configuration of the SMTP server to send emails with.
|
||||
type SMTPNotifierConfiguration struct {
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Sender string `yaml:"sender"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Secure bool `yaml:"secure"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Sender string `yaml:"sender"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
TrustedCert string `yaml:"trusted_cert"`
|
||||
DisableVerifyCert bool `yaml:"disable_verify_cert"`
|
||||
DisableRequireTLS bool `yaml:"disable_require_tls"`
|
||||
}
|
||||
|
||||
// NotifierConfiguration represents the configuration of the notifier to use when sending notifications to users.
|
||||
|
|
|
@ -115,7 +115,7 @@ notifier:
|
|||
smtp:
|
||||
username: test
|
||||
password: password
|
||||
secure: false
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -21,7 +21,7 @@ func NewFileNotifier(configuration schema.FileSystemNotifierConfiguration) *File
|
|||
}
|
||||
|
||||
// Send send a identity verification link to a user.
|
||||
func (n *FileNotifier) Send(recipient string, subject string, body string) error {
|
||||
func (n *FileNotifier) Send(recipient, subject, body string) error {
|
||||
content := fmt.Sprintf("Date: %s\nRecipient: %s\nSubject: %s\nBody: %s", time.Now(), recipient, subject, body)
|
||||
|
||||
err := ioutil.WriteFile(n.path, []byte(content), 0755)
|
||||
|
|
|
@ -2,5 +2,5 @@ package notification
|
|||
|
||||
// Notifier interface for sending the identity verification link.
|
||||
type Notifier interface {
|
||||
Send(to string, subject string, link string) error
|
||||
Send(recipient, subject, body string) error
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package notification
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
)
|
||||
|
@ -9,13 +10,20 @@ import (
|
|||
type loginAuth struct {
|
||||
username string
|
||||
password string
|
||||
host string
|
||||
}
|
||||
|
||||
func LoginAuth(username, password string) smtp.Auth {
|
||||
return &loginAuth{username, password}
|
||||
func newLoginAuth(username, password, host string) smtp.Auth {
|
||||
return &loginAuth{username, password, host}
|
||||
}
|
||||
|
||||
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
if !server.TLS && !(server.Name == "localhost" || server.Name == "127.0.0.1" || server.Name == "::1") {
|
||||
return "", nil, errors.New("connection over plain-text")
|
||||
}
|
||||
if server.Name != a.host {
|
||||
return "", nil, errors.New("unexpected hostname from server")
|
||||
}
|
||||
return "LOGIN", []byte{}, nil
|
||||
}
|
||||
|
||||
|
@ -29,6 +37,6 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
|||
case bytes.Equal(fromServer, []byte("Password:")):
|
||||
return []byte(a.password), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unexpected challenge/data from server: %s.", fromServer)
|
||||
return nil, fmt.Errorf("unexpected server challenge: %s", fromServer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFullLoginAuth(t *testing.T) {
|
||||
username := "john"
|
||||
password := "strongpw123"
|
||||
serverInfo := &smtp.ServerInfo{
|
||||
Name: "mail.authelia.com",
|
||||
TLS: true,
|
||||
Auth: nil,
|
||||
}
|
||||
auth := newLoginAuth(username, password, "mail.authelia.com")
|
||||
|
||||
proto, _, err := auth.Start(serverInfo)
|
||||
assert.Equal(t,"LOGIN", proto)
|
||||
require.NoError(t, err)
|
||||
|
||||
toServer, err := auth.Next([]byte("Username:"), true)
|
||||
assert.Equal(t,[]byte(username), toServer)
|
||||
require.NoError(t, err)
|
||||
|
||||
toServer, err = auth.Next([]byte("Password:"), true)
|
||||
assert.Equal(t, []byte(password), toServer)
|
||||
require.NoError(t, err)
|
||||
|
||||
toServer, err = auth.Next([]byte(nil), false)
|
||||
assert.Equal(t,[]byte(nil), toServer)
|
||||
require.NoError(t, err)
|
||||
|
||||
toServer, err = auth.Next([]byte("test"), true)
|
||||
assert.Equal(t, []byte(nil), toServer)
|
||||
assert.EqualError(t, err, fmt.Sprintf("unexpected server challenge: %s", []byte("test")))
|
||||
}
|
||||
|
||||
func TestShouldHaveUnexpectedHostname(t *testing.T) {
|
||||
serverInfo := &smtp.ServerInfo{
|
||||
Name: "localhost",
|
||||
TLS: true,
|
||||
Auth: nil,
|
||||
}
|
||||
auth := newLoginAuth("john", "strongpw123", "mail.authelia.com")
|
||||
_, _, err := auth.Start(serverInfo)
|
||||
assert.EqualError(t, err, "unexpected hostname from server")
|
||||
}
|
||||
|
||||
func TestTLSNotNeededForLocalhost(t *testing.T) {
|
||||
serverInfo := &smtp.ServerInfo{
|
||||
Name: "localhost",
|
||||
TLS: false,
|
||||
Auth: nil,
|
||||
}
|
||||
auth := newLoginAuth("john", "strongpw123", "localhost")
|
||||
|
||||
proto, _, err := auth.Start(serverInfo)
|
||||
assert.Equal(t,"LOGIN", proto)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestTLSNeededForNonLocalhost(t *testing.T) {
|
||||
serverInfo := &smtp.ServerInfo{
|
||||
Name: "mail.authelia.com",
|
||||
TLS: false,
|
||||
Auth: nil,
|
||||
}
|
||||
auth := newLoginAuth("john", "strongpw123", "mail.authelia.com")
|
||||
_, _, err := auth.Start(serverInfo)
|
||||
assert.EqualError(t, err, "connection over plain-text")
|
||||
}
|
|
@ -2,8 +2,10 @@ package notification
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
|
@ -12,130 +14,244 @@ import (
|
|||
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 {
|
||||
username string
|
||||
password string
|
||||
sender string
|
||||
host string
|
||||
port int
|
||||
secure bool
|
||||
address string
|
||||
username string
|
||||
password string
|
||||
sender string
|
||||
host string
|
||||
port int
|
||||
trustedCert string
|
||||
disableVerifyCert bool
|
||||
disableRequireTLS bool
|
||||
address string
|
||||
client *smtp.Client
|
||||
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 {
|
||||
return &SMTPNotifier{
|
||||
username: configuration.Username,
|
||||
password: configuration.Password,
|
||||
sender: configuration.Sender,
|
||||
host: configuration.Host,
|
||||
port: configuration.Port,
|
||||
secure: configuration.Secure,
|
||||
address: fmt.Sprintf("%s:%d", configuration.Host, configuration.Port),
|
||||
notifier := &SMTPNotifier{
|
||||
username: configuration.Username,
|
||||
password: configuration.Password,
|
||||
sender: configuration.Sender,
|
||||
host: configuration.Host,
|
||||
port: configuration.Port,
|
||||
trustedCert: configuration.TrustedCert,
|
||||
disableVerifyCert: configuration.DisableVerifyCert,
|
||||
disableRequireTLS: configuration.DisableRequireTLS,
|
||||
address: fmt.Sprintf("%s:%d", configuration.Host, configuration.Port),
|
||||
}
|
||||
notifier.initializeTLSConfig()
|
||||
return notifier
|
||||
}
|
||||
|
||||
func (n *SMTPNotifier) initializeTLSConfig() {
|
||||
// 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
|
||||
log.Debug("Notifier SMTP client initializing TLS configuration")
|
||||
insecureSkipVerify := false
|
||||
if n.disableVerifyCert {
|
||||
insecureSkipVerify = true
|
||||
}
|
||||
|
||||
//Configure Cert Pool
|
||||
certPool, err := x509.SystemCertPool()
|
||||
if err != nil || certPool == nil {
|
||||
certPool = x509.NewCertPool()
|
||||
}
|
||||
|
||||
if n.trustedCert != "" {
|
||||
log.Debugf("Notifier SMTP client attempting to load certificate from %s", n.trustedCert)
|
||||
if exists, err := utils.FileExists(n.trustedCert); exists {
|
||||
pem, err := ioutil.ReadFile(n.trustedCert)
|
||||
if err != nil {
|
||||
log.Warnf("Notifier SMTP failed to load cert from file with error: %s", err)
|
||||
} else {
|
||||
if ok := certPool.AppendCertsFromPEM(pem); !ok {
|
||||
log.Warn("Notifier SMTP failed to import cert loaded from file")
|
||||
} else {
|
||||
log.Debug("Notifier SMTP successfully loaded certificate")
|
||||
if n.disableVerifyCert {
|
||||
log.Warn("Notifier SMTP when trusted_cert is specified we force disable_verify_cert to false, if you want to disable certificate validation please comment/delete trusted_cert from your config")
|
||||
insecureSkipVerify = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Warnf("Notifier SMTP failed to load cert from file (file does not exist) with error: %s", err)
|
||||
}
|
||||
}
|
||||
n.tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
ServerName: n.host,
|
||||
RootCAs: certPool,
|
||||
}
|
||||
}
|
||||
|
||||
// Send send a identity verification link to a user.
|
||||
func (n *SMTPNotifier) Send(recipient string, subject string, body string) error {
|
||||
msg := "From: " + n.sender + "\n" +
|
||||
"To: " + recipient + "\n" +
|
||||
"Subject: " + subject + "\n" +
|
||||
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" +
|
||||
body
|
||||
|
||||
c, err := smtp.Dial(n.address)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
// Do startTLS if available (some servers only provide the auth extension after, and encryption is preferred)
|
||||
func (n *SMTPNotifier) startTLS() (bool, error) {
|
||||
// Only start if not already encrypted
|
||||
if _, ok := n.client.TLSConnectionState(); ok {
|
||||
log.Debugf("Notifier SMTP connection is already encrypted, skipping STARTTLS")
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// Do StartTLS if available (some servers only provide the auth extnesion after, and encryption is preferred)
|
||||
starttls, _ := c.Extension("STARTTLS")
|
||||
if starttls {
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: !n.secure,
|
||||
ServerName: n.host,
|
||||
}
|
||||
log.Debugf("SMTP server supports STARTTLS (InsecureSkipVerify: %t, ServerName: %s), attempting", tlsconfig.InsecureSkipVerify, tlsconfig.ServerName)
|
||||
err := c.StartTLS(tlsconfig)
|
||||
ok, _ := n.client.Extension("STARTTLS")
|
||||
if ok {
|
||||
log.Debugf("Notifier SMTP server supports STARTTLS (disableVerifyCert: %t, ServerName: %s), attempting", n.tlsConfig.InsecureSkipVerify, n.tlsConfig.ServerName)
|
||||
|
||||
err := n.client.StartTLS(n.tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
return ok, err
|
||||
} else {
|
||||
log.Debug("SMTP STARTTLS completed without error")
|
||||
log.Debug("Notifier SMTP STARTTLS completed without error")
|
||||
}
|
||||
} else if n.disableRequireTLS {
|
||||
log.Warn("Notifier SMTP server does not support STARTTLS and SMTP configuration is set to disable the TLS requirement (only useful for unauthenticated emails over plain text)")
|
||||
} else {
|
||||
log.Debug("SMTP server does not support STARTTLS, skipping")
|
||||
return ok, errors.New("Notifier SMTP server does not support TLS and it is required by default (see documentation if you want to disable this highly recommended requirement)")
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// Attempt Authentication
|
||||
func (n *SMTPNotifier) auth() (bool, error) {
|
||||
// Attempt AUTH if password is specified only
|
||||
if n.password != "" {
|
||||
if !starttls {
|
||||
log.Warn("Authentication is being attempted over an insecure connection. Using a SMTP server that supports STARTTLS is recommended, especially if the server is not on your local network (username and pasword are being transmitted in plain-text).")
|
||||
_, ok := n.client.TLSConnectionState()
|
||||
if !ok {
|
||||
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
|
||||
authExtension, m := c.Extension("AUTH")
|
||||
if authExtension {
|
||||
log.Debugf("Config has SMTP password and server supports AUTH with the following mechanisms: %s.", m)
|
||||
ok, m := n.client.Extension("AUTH")
|
||||
if ok {
|
||||
log.Debugf("Notifier SMTP server supports authentication with the following mechanisms: %s", m)
|
||||
mechanisms := strings.Split(m, " ")
|
||||
var auth smtp.Auth
|
||||
|
||||
// Adaptively select the AUTH mechanism to use based on what the server advertised
|
||||
if utils.IsStringInSlice("PLAIN", mechanisms) {
|
||||
auth = smtp.PlainAuth("", n.username, n.password, n.host)
|
||||
log.Debug("SMTP server supports AUTH PLAIN, attempting...")
|
||||
log.Debug("Notifier SMTP client attempting AUTH PLAIN with server")
|
||||
} else if utils.IsStringInSlice("LOGIN", mechanisms) {
|
||||
auth = LoginAuth(n.username, n.password)
|
||||
log.Debug("SMTP server supports AUTH LOGIN, attempting...")
|
||||
auth = newLoginAuth(n.username, n.password, n.host)
|
||||
log.Debug("Notifier SMTP client attempting AUTH LOGIN with server")
|
||||
}
|
||||
|
||||
// Throw error since AUTH extension is not supported
|
||||
if auth == nil {
|
||||
return fmt.Errorf("SMTP server does not advertise a AUTH mechanism that Authelia supports (PLAIN or LOGIN). Advertised mechanisms: %s.", 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
|
||||
err := c.Auth(auth)
|
||||
err := n.client.Auth(auth)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
} else {
|
||||
log.Debug("SMTP AUTH completed successfully.")
|
||||
log.Debug("Notifier SMTP client authenticated successfully with the server")
|
||||
return true, nil
|
||||
}
|
||||
} else {
|
||||
return errors.New("SMTP server does not advertise the AUTH extension but a password was specified. Either disable auth (don't specify a password/comment the password), or specify an SMTP host and port that supports AUTH PLAIN or AUTH LOGIN.")
|
||||
return false, errors.New("Notifier SMTP server does not advertise the AUTH extension but config requires AUTH (password specified), either disable AUTH, or use an SMTP host that supports AUTH PLAIN or AUTH LOGIN")
|
||||
}
|
||||
} else {
|
||||
log.Debug("SMTP config has no password specified for use with AUTH, skipping.")
|
||||
log.Debug("Notifier SMTP config has no password specified so authentication is being skipped")
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Set the sender and recipient first
|
||||
if err := c.Mail(n.sender); err != nil {
|
||||
return err
|
||||
func (n *SMTPNotifier) compose(recipient, subject, body string) error {
|
||||
log.Debugf("Notifier SMTP client attempting to send email body to %s", recipient)
|
||||
if !n.disableRequireTLS {
|
||||
_, ok := n.client.TLSConnectionState()
|
||||
if !ok {
|
||||
return errors.New("Notifier SMTP client can't send an email over plain text connection")
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Rcpt(recipient); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send the email body.
|
||||
wc, err := c.Data()
|
||||
wc, err := n.client.Data()
|
||||
if err != nil {
|
||||
log.Debugf("Notifier SMTP client error while obtaining WriteCloser: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
msg := "From: " + n.sender + "\n" +
|
||||
"To: " + recipient + "\n" +
|
||||
"Subject: " + subject + "\n" +
|
||||
"MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" +
|
||||
body
|
||||
|
||||
_, err = fmt.Fprintf(wc, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wc.Close()
|
||||
if err != nil {
|
||||
log.Debugf("Notifier SMTP client error while sending email body over WriteCloser: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Send the QUIT command and close the connection.
|
||||
err = c.Quit()
|
||||
err = wc.Close()
|
||||
if err != nil {
|
||||
log.Debugf("Notifier SMTP client error while closing the WriteCloser: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dial the SMTP server with the SMTPNotifier config
|
||||
func (n *SMTPNotifier) dial() error {
|
||||
log.Debugf("Notifier SMTP client attempting connection to %s", n.address)
|
||||
client, err := smtp.Dial(n.address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Notifier SMTP client connected successfully")
|
||||
n.client = client
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send an email
|
||||
func (n *SMTPNotifier) Send(recipient, subject, body string) error {
|
||||
err := n.dial()
|
||||
if err != nil {
|
||||
_ = n.client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = n.startTLS()
|
||||
if err != nil {
|
||||
_ = n.client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = n.auth()
|
||||
if err != nil {
|
||||
_ = n.client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the sender and recipient first
|
||||
if err := n.client.Mail(n.sender); err != nil {
|
||||
log.Debugf("Notifier SMTP failed while sending MAIL FROM (using sender) with error: %s", err)
|
||||
_ = n.client.Close()
|
||||
return err
|
||||
}
|
||||
if err := n.client.Rcpt(recipient); err != nil {
|
||||
log.Debugf("Notifier SMTP failed while sending RCPT TO (using recipient) with error: %s", err)
|
||||
_ = n.client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// compose and send the email body
|
||||
if err := n.compose(recipient, subject, body); err != nil {
|
||||
_ = n.client.Close()
|
||||
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")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -41,3 +41,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -79,3 +79,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -97,3 +97,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -39,3 +39,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
||||
|
|
|
@ -241,3 +241,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -95,3 +95,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -65,3 +65,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -59,3 +59,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -65,3 +65,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -70,3 +70,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -79,3 +79,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -39,3 +39,4 @@ notifier:
|
|||
host: smtp
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -26,7 +26,7 @@ const emailContent = `
|
|||
<title>Authelia</title>
|
||||
|
||||
<style type="text/css">
|
||||
/* Client-specific Styles */
|
||||
/* client-specific Styles */
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue