From 8508c772f89f1fcd6435e706745d2eabdd72f462 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Mon, 13 Jul 2020 16:57:52 +0200 Subject: [PATCH 1/6] Add headers to SMTP client to prevent being tagged as spam --- changelog/unreleased/smtp-headers.md | 6 ++++ pkg/smtpclient/smtpclient.go | 48 ++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 changelog/unreleased/smtp-headers.md diff --git a/changelog/unreleased/smtp-headers.md b/changelog/unreleased/smtp-headers.md new file mode 100644 index 0000000000..af4d568cf4 --- /dev/null +++ b/changelog/unreleased/smtp-headers.md @@ -0,0 +1,6 @@ +Enhancement: Add required headers to SMTP client to prevent being tagged as spam + +Mails being sent through the client, specially through unauthenticated SMTP were +being tagged as spam due to missing headers. + +https://github.com/cs3org/reva/pull/970 diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index 4a3997a516..5f87975ce0 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -20,9 +20,13 @@ package smtpclient import ( "bytes" + "encoding/base64" "fmt" "net/smtp" + "os" + "time" + "github.com/google/uuid" "github.com/pkg/errors" ) @@ -37,21 +41,34 @@ type SMTPCredentials struct { // SendMail allows sending mails using a set of client credentials. func (creds *SMTPCredentials) SendMail(recipient, subject, body string) error { + + header := map[string]string{ + "From": creds.SenderMail, + "To": recipient, + "Subject": subject, + "Date": time.Now().Format(time.RFC1123Z), + "Message-ID": uuid.New().String(), + "MIME-Version": "1.0", + "Content-Type": "text/plain; charset=\"utf-8\"", + "Content-Transfer-Encoding": "base64", + } + + message := "" + for k, v := range header { + message += fmt.Sprintf("%s: %s\r\n", k, v) + } + message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body)) + if creds.DisableAuth { - return creds.sendMailSMTP(recipient, subject, body) + return creds.sendMailSMTP(recipient, subject, message) } - return creds.sendMailAuthSMTP(recipient, subject, body) + return creds.sendMailAuthSMTP(recipient, subject, message) } -func (creds *SMTPCredentials) sendMailAuthSMTP(recipient, subject, body string) error { +func (creds *SMTPCredentials) sendMailAuthSMTP(recipient, subject, message string) error { auth := smtp.PlainAuth("", creds.SenderMail, creds.SenderPassword, creds.SMTPServer) - message := "From: " + creds.SenderMail + "\n" + - "To: " + recipient + "\n" + - "Subject: " + subject + "\n\n" + - body - err := smtp.SendMail( fmt.Sprintf("%s:%d", creds.SMTPServer, creds.SMTPPort), auth, @@ -67,7 +84,7 @@ func (creds *SMTPCredentials) sendMailAuthSMTP(recipient, subject, body string) return nil } -func (creds *SMTPCredentials) sendMailSMTP(recipient, subject, body string) error { +func (creds *SMTPCredentials) sendMailSMTP(recipient, subject, message string) error { c, err := smtp.Dial(fmt.Sprintf("%s:%d", creds.SMTPServer, creds.SMTPPort)) if err != nil { @@ -75,6 +92,14 @@ func (creds *SMTPCredentials) sendMailSMTP(recipient, subject, body string) erro } defer c.Close() + host, err := os.Hostname() + if err != nil { + return err + } + if err = c.Hello(host); err != nil { + return err + } + if err = c.Mail(creds.SenderMail); err != nil { return err } @@ -88,12 +113,7 @@ func (creds *SMTPCredentials) sendMailSMTP(recipient, subject, body string) erro } defer wc.Close() - message := "From: " + creds.SenderMail + "\n" + - "To: " + recipient + "\n" + - "Subject: " + subject + "\n\n" + - body buf := bytes.NewBufferString(message) - if _, err = buf.WriteTo(wc); err != nil { return err } From 8441bd2a073e364ff9b4ad67d84aa563d4846889 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Tue, 14 Jul 2020 09:51:48 +0200 Subject: [PATCH 2/6] Make local name configurable --- .../docs/config/packages/smtpclient/_index.md | 58 +++++++++++++++++++ internal/http/services/ocmd/invites.go | 4 -- pkg/smtpclient/smtpclient.go | 31 +++++++--- 3 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 docs/content/en/docs/config/packages/smtpclient/_index.md diff --git a/docs/content/en/docs/config/packages/smtpclient/_index.md b/docs/content/en/docs/config/packages/smtpclient/_index.md new file mode 100644 index 0000000000..31839cfdbc --- /dev/null +++ b/docs/content/en/docs/config/packages/smtpclient/_index.md @@ -0,0 +1,58 @@ +--- +title: "smtpclient" +linkTitle: "smtpclient" +weight: 10 +description: > + Configuration for the smtpclient service +--- + +# _struct: SMTPCredentials_ + +{{% dir name="sender_mail" type="string" default="" %}} +The email to be used to send mails. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L36) +{{< highlight toml >}} +[smtpclient] +sender_mail = "" +{{< /highlight >}} +{{% /dir %}} + +{{% dir name="sender_password" type="string" default="" %}} +The sender's password. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L37) +{{< highlight toml >}} +[smtpclient] +sender_password = "" +{{< /highlight >}} +{{% /dir %}} + +{{% dir name="smtp_server" type="string" default="" %}} +The hostname of the SMTP server. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L38) +{{< highlight toml >}} +[smtpclient] +smtp_server = "" +{{< /highlight >}} +{{% /dir %}} + +{{% dir name="smtp_port" type="int" default=587 %}} +The port on which the SMTP daemon is running. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L39) +{{< highlight toml >}} +[smtpclient] +smtp_port = 587 +{{< /highlight >}} +{{% /dir %}} + +{{% dir name="disable_auth" type="bool" default=false %}} +Whether to disable SMTP auth. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L40) +{{< highlight toml >}} +[smtpclient] +disable_auth = false +{{< /highlight >}} +{{% /dir %}} + +{{% dir name="local_name" type="string" default="" %}} +The host name to be used for unauthenticated SMTP. [[Ref]](https://github.com/cs3org/reva/tree/master/pkg/smtpclient/smtpclient.go#L41) +{{< highlight toml >}} +[smtpclient] +local_name = "" +{{< /highlight >}} +{{% /dir %}} + diff --git a/internal/http/services/ocmd/invites.go b/internal/http/services/ocmd/invites.go index 60a6244560..c18d847456 100644 --- a/internal/http/services/ocmd/invites.go +++ b/internal/http/services/ocmd/invites.go @@ -23,7 +23,6 @@ import ( "errors" "fmt" "net/http" - "os" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1" @@ -45,9 +44,6 @@ type invitesHandler struct { func (h *invitesHandler) init(c *Config) { h.gatewayAddr = c.GatewaySvc h.smtpCredentials = c.SMTPCredentials - if h.smtpCredentials != nil && !h.smtpCredentials.DisableAuth && h.smtpCredentials.SenderPassword == "" { - h.smtpCredentials.SenderPassword = os.Getenv("REVA_OCMD_SMTP_SENDER_PASSWORD") - } } func (h *invitesHandler) Handler() http.Handler { diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index 5f87975ce0..ea22a4c272 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -24,6 +24,7 @@ import ( "fmt" "net/smtp" "os" + "strings" "time" "github.com/google/uuid" @@ -32,17 +33,33 @@ import ( // SMTPCredentials stores the credentials required to connect to an SMTP server. type SMTPCredentials struct { - SenderMail string `mapstructure:"sender_mail"` - SenderPassword string `mapstructure:"sender_password"` - SMTPServer string `mapstructure:"smtp_server"` - SMTPPort int `mapstructure:"smtp_port"` - DisableAuth bool `mapstructure:"disable_auth"` + SenderMail string `mapstructure:"sender_mail" docs:";The email to be used to send mails."` + SenderPassword string `mapstructure:"sender_password" docs:";The sender's password."` + SMTPServer string `mapstructure:"smtp_server" docs:";The hostname of the SMTP server."` + SMTPPort int `mapstructure:"smtp_port" docs:"587;The port on which the SMTP daemon is running."` + DisableAuth bool `mapstructure:"disable_auth" docs:"false;Whether to disable SMTP auth."` + LocalName string `mapstructure:"local_name" docs:";The host name to be used for unauthenticated SMTP."` +} + +func (creds *SMTPCredentials) init() { + if creds.SMTPPort == 0 { + creds.SMTPPort = 587 + } + if !creds.DisableAuth && creds.SenderPassword == "" { + creds.SenderPassword = os.Getenv("REVA_SMTP_SENDER_PASSWORD") + } + if creds.LocalName == "" { + tokens := strings.Split(creds.SenderMail, "@") + creds.LocalName = tokens[len(tokens)-1] + } } // SendMail allows sending mails using a set of client credentials. func (creds *SMTPCredentials) SendMail(recipient, subject, body string) error { - header := map[string]string{ + creds.init() + + headers := map[string]string{ "From": creds.SenderMail, "To": recipient, "Subject": subject, @@ -54,7 +71,7 @@ func (creds *SMTPCredentials) SendMail(recipient, subject, body string) error { } message := "" - for k, v := range header { + for k, v := range headers { message += fmt.Sprintf("%s: %s\r\n", k, v) } message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body)) From 404ba3045da648014928e7fb5a444c4c9e0115bf Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Tue, 14 Jul 2020 10:03:53 +0200 Subject: [PATCH 3/6] Add New function to SMTPClient --- internal/http/services/ocmd/invites.go | 2 +- pkg/smtpclient/smtpclient.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/http/services/ocmd/invites.go b/internal/http/services/ocmd/invites.go index c18d847456..9b890f5785 100644 --- a/internal/http/services/ocmd/invites.go +++ b/internal/http/services/ocmd/invites.go @@ -43,7 +43,7 @@ type invitesHandler struct { func (h *invitesHandler) init(c *Config) { h.gatewayAddr = c.GatewaySvc - h.smtpCredentials = c.SMTPCredentials + h.smtpCredentials = smtpclient.NewSMTPCredentials(c.SMTPCredentials) } func (h *invitesHandler) Handler() http.Handler { diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index ea22a4c272..580389ca7b 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -41,7 +41,9 @@ type SMTPCredentials struct { LocalName string `mapstructure:"local_name" docs:";The host name to be used for unauthenticated SMTP."` } -func (creds *SMTPCredentials) init() { +func NewSMTPCredentials(c *SMTPCredentials) *SMTPCredentials { + creds := c + if creds.SMTPPort == 0 { creds.SMTPPort = 587 } @@ -52,13 +54,12 @@ func (creds *SMTPCredentials) init() { tokens := strings.Split(creds.SenderMail, "@") creds.LocalName = tokens[len(tokens)-1] } + return creds } // SendMail allows sending mails using a set of client credentials. func (creds *SMTPCredentials) SendMail(recipient, subject, body string) error { - creds.init() - headers := map[string]string{ "From": creds.SenderMail, "To": recipient, From a122b5c345652d41d49ba99231884500b16d9cec Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Tue, 14 Jul 2020 10:07:25 +0200 Subject: [PATCH 4/6] Add comment for NewSMTPCredentials --- pkg/smtpclient/smtpclient.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index 580389ca7b..2cc75788a5 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -41,6 +41,8 @@ type SMTPCredentials struct { LocalName string `mapstructure:"local_name" docs:";The host name to be used for unauthenticated SMTP."` } +// NewSMTPCredentials creates a new SMTPCredentials object with the details of +// the passed object with sane defaults. func NewSMTPCredentials(c *SMTPCredentials) *SMTPCredentials { creds := c From b250afc503d86b2c63399a8b66653bd2c9315619 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Tue, 14 Jul 2020 10:09:23 +0200 Subject: [PATCH 5/6] Have comment on one line --- pkg/smtpclient/smtpclient.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index 2cc75788a5..ceda7fa9b1 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -41,8 +41,7 @@ type SMTPCredentials struct { LocalName string `mapstructure:"local_name" docs:";The host name to be used for unauthenticated SMTP."` } -// NewSMTPCredentials creates a new SMTPCredentials object with the details of -// the passed object with sane defaults. +// NewSMTPCredentials creates a new SMTPCredentials object with the details of the passed object with sane defaults. func NewSMTPCredentials(c *SMTPCredentials) *SMTPCredentials { creds := c From f45b02641eed0f648a01e78f6e5ee3785c0454b9 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Thu, 23 Jul 2020 12:33:13 +0200 Subject: [PATCH 6/6] Send HELO signal to the passed localname --- pkg/smtpclient/smtpclient.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/smtpclient/smtpclient.go b/pkg/smtpclient/smtpclient.go index ceda7fa9b1..6ca85170b4 100644 --- a/pkg/smtpclient/smtpclient.go +++ b/pkg/smtpclient/smtpclient.go @@ -111,14 +111,9 @@ func (creds *SMTPCredentials) sendMailSMTP(recipient, subject, message string) e } defer c.Close() - host, err := os.Hostname() - if err != nil { - return err - } - if err = c.Hello(host); err != nil { + if err = c.Hello(creds.LocalName); err != nil { return err } - if err = c.Mail(creds.SenderMail); err != nil { return err }