From 5463256aa2203f8594ad7343a41269823aeead35 Mon Sep 17 00:00:00 2001 From: Fabrizio Steiner Date: Mon, 22 Jan 2018 20:36:32 +0100 Subject: [PATCH] Possibility to disable the TLS verify for sending mails. --- LICENSE.md | 5 +++ README.md | 1 + main.go | 41 ++++++++++++++--------- notifications/email.go | 16 +++++---- notifications/smtp.go | 76 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 23 deletions(-) create mode 100644 notifications/smtp.go diff --git a/LICENSE.md b/LICENSE.md index 3a3284a..474d999 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -199,3 +199,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +Watchtower contains code that is licensed under a BSD-license: + - Copyright (c) 2009 The Go Authors. All rights reserved. + + For details see https://golang.org/LICENSE diff --git a/README.md b/README.md index bb1d1cc..8b4a3f3 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,7 @@ To receive notifications by email, the following command-line options, or their * `--notification-email-from` (env. `WATCHTOWER_NOTIFICATION_EMAIL_FROM`): The e-mail address from which notifications will be sent. * `--notification-email-to` (env. `WATCHTOWER_NOTIFICATION_EMAIL_TO`): The e-mail address to which notifications will be sent. * `--notification-email-server` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER`): The SMTP server to send e-mails through. +* `--notification-email-server-tls-skip-verify` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY`): Do not verify the TLS certificate of the mail server. This should be used only for testing. * `--notification-email-server-port` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT`): The port used to connect to the SMTP server to send e-mails through. Defaults to `25`. * `--notification-email-server-user` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER`): The username to authenticate with the SMTP server with. * `--notification-email-server-password` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD`): The password to authenticate with the SMTP server with. diff --git a/main.go b/main.go index 1fd015b..fd74fcc 100644 --- a/main.go +++ b/main.go @@ -91,40 +91,49 @@ func main() { Usage: "enable debug mode with verbose logging", }, cli.StringSliceFlag{ - Name: "notifications", - Value: &cli.StringSlice{}, - Usage: "notification types to send (valid: email)", + Name: "notifications", + Value: &cli.StringSlice{}, + Usage: "notification types to send (valid: email)", EnvVar: "WATCHTOWER_NOTIFICATIONS", }, cli.StringFlag{ - Name: "notification-email-from", - Usage: "Address to send notification e-mails from", + Name: "notification-email-from", + Usage: "Address to send notification e-mails from", EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_FROM", }, cli.StringFlag{ - Name: "notification-email-to", - Usage: "Address to send notification e-mails to", + Name: "notification-email-to", + Usage: "Address to send notification e-mails to", EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_TO", }, cli.StringFlag{ - Name: "notification-email-server", - Usage: "SMTP server to send notification e-mails through", + Name: "notification-email-server", + Usage: "SMTP server to send notification e-mails through", EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_SERVER", }, cli.IntFlag{ - Name: "notification-email-server-port", - Usage: "SMTP server port to send notification e-mails through", + Name: "notification-email-server-port", + Usage: "SMTP server port to send notification e-mails through", Value: 25, EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT", }, + cli.BoolFlag{ + Name: "notification-email-server-tls-skip-verify", + Usage: "Controls whether watchtower verifies the SMTP server's certificate chain and host name. " + + "If set, TLS accepts any certificate " + + "presented by the server and any host name in that certificate. " + + "In this mode, TLS is susceptible to man-in-the-middle attacks. " + + "This should be used only for testing.", + EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_TLS_SKIP_VERIFY", + }, cli.StringFlag{ - Name: "notification-email-server-user", - Usage: "SMTP server user for sending notifications", + Name: "notification-email-server-user", + Usage: "SMTP server user for sending notifications", EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER", }, cli.StringFlag{ - Name: "notification-email-server-password", - Usage: "SMTP server password for sending notifications", + Name: "notification-email-server-password", + Usage: "SMTP server password for sending notifications", EnvVar: "WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD", }, } @@ -180,7 +189,7 @@ func start(c *cli.Context) error { scheduleSpec, func() { select { - case v := <- tryLockSem: + case v := <-tryLockSem: defer func() { tryLockSem <- v }() notifier.StartNotification() if err := actions.Update(client, names, cleanup, noRestart); err != nil { diff --git a/notifications/email.go b/notifications/email.go index f6d252c..bb63329 100644 --- a/notifications/email.go +++ b/notifications/email.go @@ -26,17 +26,19 @@ type emailTypeNotifier struct { From, To string Server, User, Password string Port int + tlsSkipVerify bool entries []*log.Entry } func newEmailNotifier(c *cli.Context) typeNotifier { n := &emailTypeNotifier{ - From: c.GlobalString("notification-email-from"), - To: c.GlobalString("notification-email-to"), - Server: c.GlobalString("notification-email-server"), - User: c.GlobalString("notification-email-server-user"), - Password: c.GlobalString("notification-email-server-password"), - Port: c.GlobalInt("notification-email-server-port"), + From: c.GlobalString("notification-email-from"), + To: c.GlobalString("notification-email-to"), + Server: c.GlobalString("notification-email-server"), + User: c.GlobalString("notification-email-server-user"), + Password: c.GlobalString("notification-email-server-password"), + Port: c.GlobalInt("notification-email-server-port"), + tlsSkipVerify: c.GlobalBool("notification-email-server-tls-skip-verify"), } log.AddHook(n) @@ -80,7 +82,7 @@ func (e *emailTypeNotifier) sendEntries(entries []*log.Entry) { msg := e.buildMessage(entries) go func() { auth := smtp.PlainAuth("", e.User, e.Password, e.Server) - err := smtp.SendMail(e.Server+":"+strconv.Itoa(e.Port), auth, e.From, []string{e.To}, msg) + err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, []string{e.To}, msg) if err != nil { // Use fmt so it doesn't trigger another email. fmt.Println("Failed to send notification email: ", err) diff --git a/notifications/smtp.go b/notifications/smtp.go new file mode 100644 index 0000000..d0f07f8 --- /dev/null +++ b/notifications/smtp.go @@ -0,0 +1,76 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license. +package notifications + +import ( + "crypto/tls" + "net" + "net/smtp" +) + +// SendMail connects to the server at addr, switches to TLS if +// possible, authenticates with the optional mechanism a if possible, +// and then sends an email from address from, to addresses to, with +// message msg. +// The addr must include a port, as in "mail.example.com:smtp". +// +// The addresses in the to parameter are the SMTP RCPT addresses. +// +// The msg parameter should be an RFC 822-style email with headers +// first, a blank line, and then the message body. The lines of msg +// should be CRLF terminated. The msg headers should usually include +// fields such as "From", "To", "Subject", and "Cc". Sending "Bcc" +// messages is accomplished by including an email address in the to +// parameter but not including it in the msg headers. +// +// The SendMail function and the net/smtp package are low-level +// mechanisms and provide no support for DKIM signing, MIME +// attachments (see the mime/multipart package), or other mail +// functionality. Higher-level packages exist outside of the standard +// library. +func SendMail(addr string, insecureSkipVerify bool, a smtp.Auth, from string, to []string, msg []byte) error { + c, err := smtp.Dial(addr) + if err != nil { + return err + } + defer c.Close() + if err = c.Hello("localHost"); err != nil { + return err + } + if ok, _ := c.Extension("STARTTLS"); ok { + serverName, _, _ := net.SplitHostPort(addr) + config := &tls.Config{ServerName: serverName, InsecureSkipVerify: insecureSkipVerify} + if err = c.StartTLS(config); err != nil { + return err + } + } + if a != nil { + if ok, _ := c.Extension("AUTH"); ok { + if err = c.Auth(a); err != nil { + return err + } + } + } + if err = c.Mail(from); err != nil { + return err + } + for _, addr := range to { + if err = c.Rcpt(addr); err != nil { + return err + } + } + w, err := c.Data() + if err != nil { + return err + } + _, err = w.Write(msg) + if err != nil { + return err + } + err = w.Close() + if err != nil { + return err + } + return c.Quit() +}