switch to another email lib to support more AUTH types

This commit is contained in:
Christoph Haas 2021-04-29 16:45:28 +02:00
parent 7b1f59d86a
commit 19e6fa2a1a
5 changed files with 80 additions and 53 deletions

View File

@ -130,6 +130,7 @@ The following configuration options are available:
| EMAIL_CERT_VALIDATION | certcheck | email | false | Validate the email server certificate. | | EMAIL_CERT_VALIDATION | certcheck | email | false | Validate the email server certificate. |
| EMAIL_USERNAME | user | email | | An optional username for SMTP authentication. | | EMAIL_USERNAME | user | email | | An optional username for SMTP authentication. |
| EMAIL_PASSWORD | pass | email | | An optional password for SMTP authentication. | | EMAIL_PASSWORD | pass | email | | An optional password for SMTP authentication. |
| EMAIL_AUTHTYPE | auth | email | plain | Either plain, login or crammd5. If username and password are empty, this value is ignored. |
| WG_DEVICES | devices | wg | wg0 | A comma separated list of WireGuard devices. | | WG_DEVICES | devices | wg | wg0 | A comma separated list of WireGuard devices. |
| WG_DEFAULT_DEVICE | defaultDevice | wg | wg0 | This device is used for auto-created peers (if CREATE_DEFAULT_PEER is enabled). | | WG_DEFAULT_DEVICE | defaultDevice | wg | wg0 | This device is used for auto-created peers (if CREATE_DEFAULT_PEER is enabled). |
| WG_CONFIG_PATH | configDirectory | wg | /etc/wireguard | If set, interface configuration updates will be written to this path, filename: <devicename>.conf. | | WG_CONFIG_PATH | configDirectory | wg | /etc/wireguard | If set, interface configuration updates will be written to this path, filename: <devicename>.conf. |

2
go.mod
View File

@ -13,7 +13,6 @@ require (
github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/validator/v10 v10.4.1 github.com/go-playground/validator/v10 v10.4.1
github.com/gorilla/sessions v1.2.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/milosgajdos/tenus v0.0.3 github.com/milosgajdos/tenus v0.0.3
@ -25,6 +24,7 @@ require (
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f
github.com/utrack/gin-csrf v0.0.0-20190424104817-40fb8d2c8fca github.com/utrack/gin-csrf v0.0.0-20190424104817-40fb8d2c8fca
github.com/xhit/go-simple-mail/v2 v2.8.1
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 // indirect golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 // indirect
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 // indirect golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 // indirect

View File

@ -3,13 +3,10 @@ package common
import ( import (
"crypto/tls" "crypto/tls"
"io" "io"
"net/smtp" "io/ioutil"
"strconv"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
mail "github.com/xhit/go-simple-mail/v2"
"github.com/jordan-wright/email"
) )
type MailEncryption string type MailEncryption string
@ -20,6 +17,14 @@ const (
MailEncryptionStartTLS MailEncryption = "starttls" MailEncryptionStartTLS MailEncryption = "starttls"
) )
type MailAuthType string
const (
MailAuthPlain MailAuthType = "plain"
MailAuthLogin MailAuthType = "login"
MailAuthCramMD5 MailAuthType = "crammd5"
)
type MailConfig struct { type MailConfig struct {
Host string `yaml:"host" envconfig:"EMAIL_HOST"` Host string `yaml:"host" envconfig:"EMAIL_HOST"`
Port int `yaml:"port" envconfig:"EMAIL_PORT"` Port int `yaml:"port" envconfig:"EMAIL_PORT"`
@ -28,6 +33,7 @@ type MailConfig struct {
CertValidation bool `yaml:"certcheck" envconfig:"EMAIL_CERT_VALIDATION"` CertValidation bool `yaml:"certcheck" envconfig:"EMAIL_CERT_VALIDATION"`
Username string `yaml:"user" envconfig:"EMAIL_USERNAME"` Username string `yaml:"user" envconfig:"EMAIL_USERNAME"`
Password string `yaml:"pass" envconfig:"EMAIL_PASSWORD"` Password string `yaml:"pass" envconfig:"EMAIL_PASSWORD"`
AuthType MailAuthType `yaml:"auth" envconfig:"EMAIL_AUTHTYPE"`
} }
type MailAttachment struct { type MailAttachment struct {
@ -39,60 +45,72 @@ type MailAttachment struct {
// SendEmailWithAttachments sends a mail with optional attachments. // SendEmailWithAttachments sends a mail with optional attachments.
func SendEmailWithAttachments(cfg MailConfig, sender, replyTo, subject, body string, htmlBody string, receivers []string, attachments []MailAttachment) error { func SendEmailWithAttachments(cfg MailConfig, sender, replyTo, subject, body string, htmlBody string, receivers []string, attachments []MailAttachment) error {
e := email.NewEmail() srv := mail.NewSMTPClient()
hostname := cfg.Host + ":" + strconv.Itoa(cfg.Port) srv.Host = cfg.Host
subject = strings.Trim(subject, "\n\r\t") srv.Port = cfg.Port
sender = strings.Trim(sender, "\n\r\t") srv.Username = cfg.Username
replyTo = strings.Trim(replyTo, "\n\r\t") srv.Password = cfg.Password
if replyTo == "" {
replyTo = sender
}
var auth smtp.Auth
if cfg.Username == "" {
auth = nil
} else {
// Set up authentication information.
auth = smtp.PlainAuth(
"",
cfg.Username,
cfg.Password,
cfg.Host,
)
}
// Set email data.
e.From = sender
e.To = receivers
e.ReplyTo = []string{replyTo}
e.Subject = subject
e.Text = []byte(body)
if htmlBody != "" {
e.HTML = []byte(htmlBody)
}
for _, attachment := range attachments {
a, err := e.Attach(attachment.Data, attachment.Name, attachment.ContentType)
if err != nil {
return errors.Wrapf(err, "failed to attach %s to mailbody", attachment.Name)
}
if attachment.Embedded {
a.HTMLRelated = true
}
}
// TODO: remove this once the deprecated MailConfig.TLS config option has been removed // TODO: remove this once the deprecated MailConfig.TLS config option has been removed
if cfg.TLS { if cfg.TLS {
cfg.Encryption = MailEncryptionStartTLS cfg.Encryption = MailEncryptionStartTLS
} }
switch cfg.Encryption { switch cfg.Encryption {
case MailEncryptionTLS: case MailEncryptionTLS:
return e.SendWithTLS(hostname, auth, &tls.Config{ServerName: cfg.Host, InsecureSkipVerify: !cfg.CertValidation}) srv.Encryption = mail.EncryptionTLS
case MailEncryptionStartTLS: case MailEncryptionStartTLS:
return e.SendWithStartTLS(hostname, auth, &tls.Config{ServerName: cfg.Host, InsecureSkipVerify: !cfg.CertValidation}) srv.Encryption = mail.EncryptionSTARTTLS
default: // MailEncryptionNone default: // MailEncryptionNone
return e.Send(hostname, auth) srv.Encryption = mail.EncryptionNone
} }
srv.TLSConfig = &tls.Config{InsecureSkipVerify: !cfg.CertValidation}
switch cfg.AuthType {
case MailAuthPlain:
srv.Authentication = mail.AuthPlain
case MailAuthLogin:
srv.Authentication = mail.AuthLogin
case MailAuthCramMD5:
srv.Authentication = mail.AuthCRAMMD5
}
client, err := srv.Connect()
if err != nil {
return errors.Wrap(err, "failed to connect via SMTP")
}
if replyTo == "" {
replyTo = sender
}
email := mail.NewMSG()
email.SetFrom(sender).
AddTo(receivers...).
SetReplyTo(replyTo).
SetSubject(subject)
email.SetBody(mail.TextPlain, body)
if htmlBody != "" {
email.SetBody(mail.TextHTML, htmlBody)
}
for _, attachment := range attachments {
attachmentData, err := ioutil.ReadAll(attachment.Data)
if err != nil {
return errors.Wrapf(err, "failed to read attachment data for %s", attachment.Name)
}
if attachment.Embedded {
email.AddInlineData(attachmentData, attachment.Name, attachment.ContentType)
} else {
email.AddAttachmentData(attachmentData, attachment.Name, attachment.ContentType)
}
}
// Call Send and pass the client
err = email.Send(client)
if err != nil {
return errors.Wrapf(err, "failed to send email")
}
return nil
} }

View File

@ -114,6 +114,7 @@ func NewConfig() *Config {
cfg.Email.Host = "127.0.0.1" cfg.Email.Host = "127.0.0.1"
cfg.Email.Port = 25 cfg.Email.Port = 25
cfg.Email.Encryption = common.MailEncryptionNone cfg.Email.Encryption = common.MailEncryptionNone
cfg.Email.AuthType = common.MailAuthPlain
// Load config from file and environment // Load config from file and environment
cfgFile, ok := os.LookupEnv("CONFIG_FILE") cfgFile, ok := os.LookupEnv("CONFIG_FILE")

View File

@ -265,6 +265,7 @@ func (s *Server) GetPeerConfigMail(c *gin.Context) {
return return
} }
// Apply mail template // Apply mail template
qrcodeFileName := "wireguard-qrcode.png"
var tplBuff bytes.Buffer var tplBuff bytes.Buffer
if err := s.mailTpl.Execute(&tplBuff, struct { if err := s.mailTpl.Execute(&tplBuff, struct {
Peer wireguard.Peer Peer wireguard.Peer
@ -274,7 +275,7 @@ func (s *Server) GetPeerConfigMail(c *gin.Context) {
}{ }{
Peer: peer, Peer: peer,
User: user, User: user,
QrcodePngName: "wireguard-config.png", QrcodePngName: qrcodeFileName,
PortalUrl: s.config.Core.ExternalUrl, PortalUrl: s.config.Core.ExternalUrl,
}); err != nil { }); err != nil {
s.GetHandleError(c, http.StatusInternalServerError, "Template error", err.Error()) s.GetHandleError(c, http.StatusInternalServerError, "Template error", err.Error())
@ -289,7 +290,13 @@ func (s *Server) GetPeerConfigMail(c *gin.Context) {
Data: bytes.NewReader(cfg), Data: bytes.NewReader(cfg),
}, },
{ {
Name: "wireguard-config.png", Name: qrcodeFileName,
ContentType: "image/png",
Data: bytes.NewReader(png),
Embedded: true,
},
{
Name: qrcodeFileName,
ContentType: "image/png", ContentType: "image/png",
Data: bytes.NewReader(png), Data: bytes.NewReader(png),
}, },