From 43e5bbbe21d1c57fb4087f5380b9821bdc398782 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 15 Mar 2021 21:57:42 +0000 Subject: [PATCH] add option to trust specific cert for SMTP --- config/config-base.json | 8 ++++++++ email.go | 44 ++++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/config/config-base.json b/config/config-base.json index 6e0c435..d81d600 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -673,6 +673,14 @@ "requires_restart": false, "type": "password", "value": "smtp password" + }, + "ssl_cert": { + "name": "Path to custom SSL certificate", + "required": false, + "requires_restart": false, + "type": "text", + "value": "", + "description": "Use if your SMTP server's SSL Certificate is not trusted by the system." } } }, diff --git a/email.go b/email.go index 730e5e5..ed9a672 100644 --- a/email.go +++ b/email.go @@ -4,10 +4,13 @@ import ( "bytes" "context" "crypto/tls" + "crypto/x509" + "errors" "fmt" "html/template" "io" "net/smtp" + "os" "strings" "sync" textTemplate "text/template" @@ -49,18 +52,15 @@ func (mg *Mailgun) send(fromName, fromAddr string, email *Email, address ...stri // SMTP supports SSL/TLS and STARTTLS; implements emailClient. type SMTP struct { - sslTLS bool - server string - port int - auth smtp.Auth + sslTLS bool + server string + port int + auth smtp.Auth + tlsConfig *tls.Config } func (sm *SMTP) send(fromName, fromAddr string, email *Email, address ...string) error { server := fmt.Sprintf("%s:%d", sm.server, sm.port) - tlsConfig := &tls.Config{ - InsecureSkipVerify: false, - ServerName: sm.server, - } from := fmt.Sprintf("%s <%s>", fromName, fromAddr) var wg sync.WaitGroup var err error @@ -75,9 +75,9 @@ func (sm *SMTP) send(fromName, fromAddr string, email *Email, address ...string) e.HTML = []byte(email.HTML) e.To = []string{addr} if sm.sslTLS { - err = e.SendWithTLS(server, sm.auth, tlsConfig) + err = e.SendWithTLS(server, sm.auth, sm.tlsConfig) } else { - err = e.SendWithStartTLS(server, sm.auth, tlsConfig) + err = e.SendWithStartTLS(server, sm.auth, sm.tlsConfig) } }(addr) } @@ -139,7 +139,10 @@ func NewEmailer(app *appContext) *Emailer { } else { username = emailer.fromAddr } - emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, app.config.Section("smtp").Key("password").String(), sslTls) + err := emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, app.config.Section("smtp").Key("password").String(), sslTls, app.config.Section("smtp").Key("ssl_cert").MustString("")) + if err != nil { + app.err.Printf("Error while initiating SMTP mailer: %v", err) + } } else if method == "mailgun" { emailer.NewMailgun(app.config.Section("mailgun").Key("api_url").String(), app.config.Section("mailgun").Key("api_key").String()) } @@ -161,13 +164,30 @@ func (emailer *Emailer) NewMailgun(url, key string) { } // NewSMTP returns an SMTP emailClient. -func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool) { +func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool, certPath string) (err error) { + rootCAs, err := x509.SystemCertPool() + if rootCAs == nil || err != nil { + rootCAs = x509.NewCertPool() + } + if certPath != "" { + var cert []byte + cert, err = os.ReadFile(certPath) + if rootCAs.AppendCertsFromPEM(cert) == false { + err = errors.New("Failed to append cert to pool") + } + } emailer.sender = &SMTP{ auth: smtp.PlainAuth("", username, password, server), server: server, port: port, sslTLS: sslTLS, + tlsConfig: &tls.Config{ + InsecureSkipVerify: false, + ServerName: server, + RootCAs: rootCAs, + }, } + return } type templ interface {