From 528c35eec623c72dbfdd50a509c9657db9f7b3b3 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sun, 17 Mar 2024 14:57:41 +1300 Subject: [PATCH] Feature: Add SMTP TLS option (#265) --- cmd/root.go | 21 ++++++++++++++++++--- config/config.go | 22 +++++++++++++++------- server/smtpd/smtpd.go | 29 +++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 0866e04..98658af 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -108,7 +108,8 @@ func init() { rootCmd.Flags().BoolVar(&config.SMTPAuthAcceptAny, "smtp-auth-accept-any", config.SMTPAuthAcceptAny, "Accept any SMTP username and password, including none") rootCmd.Flags().StringVar(&config.SMTPTLSCert, "smtp-tls-cert", config.SMTPTLSCert, "TLS certificate for SMTP (STARTTLS) - requires smtp-tls-key") rootCmd.Flags().StringVar(&config.SMTPTLSKey, "smtp-tls-key", config.SMTPTLSKey, "TLS key for SMTP (STARTTLS) - requires smtp-tls-cert") - rootCmd.Flags().BoolVar(&config.SMTPTLSRequired, "smtp-tls-required", config.SMTPTLSRequired, "Require TLS SMTP encryption") + rootCmd.Flags().BoolVar(&config.SMTPRequireSTARTTLS, "smtp-require-starttls", config.SMTPRequireSTARTTLS, "Require SMTP client use STARTTLS") + rootCmd.Flags().BoolVar(&config.SMTPRequireTLS, "smtp-require-tls", config.SMTPRequireTLS, "Require client use SSL/TLS") rootCmd.Flags().BoolVar(&config.SMTPAuthAllowInsecure, "smtp-auth-allow-insecure", config.SMTPAuthAllowInsecure, "Allow insecure PLAIN & LOGIN SMTP authentication") rootCmd.Flags().BoolVar(&config.SMTPStrictRFCHeaders, "smtp-strict-rfc-headers", config.SMTPStrictRFCHeaders, "Return SMTP error if message headers contain ") rootCmd.Flags().IntVar(&config.SMTPMaxRecipients, "smtp-max-recipients", config.SMTPMaxRecipients, "Maximum SMTP recipients allowed") @@ -146,6 +147,11 @@ func init() { rootCmd.Flags().Lookup("smtp-ssl-cert").Deprecated = "use --smtp-tls-cert" rootCmd.Flags().Lookup("smtp-ssl-key").Hidden = true rootCmd.Flags().Lookup("smtp-ssl-key").Deprecated = "use --smtp-tls-key" + + // DEPRECATED FLAGS 2024/03/16 + rootCmd.Flags().BoolVar(&config.SMTPRequireSTARTTLS, "smtp-tls-required", config.SMTPRequireSTARTTLS, "smtp-require-starttls") + rootCmd.Flags().Lookup("smtp-tls-required").Hidden = true + rootCmd.Flags().Lookup("smtp-tls-required").Deprecated = "use --smtp-require-starttls" } // Load settings from environment @@ -213,9 +219,13 @@ func initConfigFromEnv() { } config.SMTPTLSCert = os.Getenv("MP_SMTP_TLS_CERT") config.SMTPTLSKey = os.Getenv("MP_SMTP_TLS_KEY") - if getEnabledFromEnv("MP_SMTP_TLS_REQUIRED") { - config.SMTPTLSRequired = true + if getEnabledFromEnv("MP_SMTP_REQUIRE_STARTTLS") { + config.SMTPRequireSTARTTLS = true } + if getEnabledFromEnv("MP_SMTP_REQUIRE_TLS") { + config.SMTPRequireTLS = true + } + if getEnabledFromEnv("MP_SMTP_AUTH_ALLOW_INSECURE") { config.SMTPAuthAllowInsecure = true } @@ -306,6 +316,11 @@ func initDeprecatedConfigFromEnv() { logger.Log().Warn("ENV MP_STRICT_RFC_HEADERS has been deprecated, use MP_SMTP_STRICT_RFC_HEADERS") config.SMTPStrictRFCHeaders = true } + // deprecated 2024/03.16 + if getEnabledFromEnv("MP_SMTP_TLS_REQUIRED") { + logger.Log().Warn("ENV MP_SMTP_TLS_REQUIRED has been deprecated, use MP_SMTP_REQUIRE_STARTTLS") + config.SMTPRequireSTARTTLS = true + } } // Wrapper to get a boolean from an environment variable diff --git a/config/config.go b/config/config.go index 94d6a70..dfa50f0 100644 --- a/config/config.go +++ b/config/config.go @@ -53,10 +53,14 @@ var ( // SMTPTLSKey file SMTPTLSKey string - // SMTPTLSRequired to enforce TLS + // SMTPRequireSTARTTLS to enforce the use of STARTTLS // The only allowed commands are NOOP, EHLO, STARTTLS and QUIT (as specified in RFC 3207) until // the connection is upgraded to TLS i.e. until STARTTLS is issued. - SMTPTLSRequired bool + SMTPRequireSTARTTLS bool + + // SMTPRequireTLS to allow only SSL/TLS connections for all connections + // + SMTPRequireTLS bool // SMTPAuthFile for SMTP authentication SMTPAuthFile string @@ -242,12 +246,16 @@ func VerifyConfig() error { if !isFile(SMTPTLSKey) { return fmt.Errorf("[smtp] TLS key not found: %s", SMTPTLSKey) } - } else if SMTPTLSRequired { + } else if SMTPRequireTLS { return errors.New("[smtp] TLS cannot be required without an SMTP TLS certificate and key") + } else if SMTPRequireSTARTTLS { + return errors.New("[smtp] STARTTLS cannot be required without an SMTP TLS certificate and key") } - - if SMTPTLSRequired && SMTPAuthAllowInsecure { - return errors.New("[smtp] TLS cannot be required while also allowing insecure authentication") + if SMTPRequireSTARTTLS && SMTPAuthAllowInsecure || SMTPRequireTLS && SMTPAuthAllowInsecure { + return errors.New("[smtp] TLS cannot be required with --smtp-auth-allow-insecure") + } + if SMTPRequireSTARTTLS && SMTPRequireTLS { + return errors.New("[smtp] TLS & STARTTLS cannot be required together") } if SMTPAuthFile != "" { @@ -272,7 +280,7 @@ func VerifyConfig() error { } if SMTPTLSCert == "" && (auth.SMTPCredentials != nil || SMTPAuthAcceptAny) && !SMTPAuthAllowInsecure { - return errors.New("[smtp] authentication requires TLS encryption, run with `--smtp-auth-allow-insecure` to allow insecure authentication") + return errors.New("[smtp] authentication requires STARTTLS or TLS encryption, run with `--smtp-auth-allow-insecure` to allow insecure authentication") } // POP3 server diff --git a/server/smtpd/smtpd.go b/server/smtpd/smtpd.go index e9fb0f4..63786e5 100644 --- a/server/smtpd/smtpd.go +++ b/server/smtpd/smtpd.go @@ -177,19 +177,35 @@ func handlerRcpt(remoteAddr net.Addr, from string, to string) bool { func Listen() error { if config.SMTPAuthAllowInsecure { if auth.SMTPCredentials != nil { - logger.Log().Info("[smtpd] enabling login auth (insecure)") + logger.Log().Info("[smtpd] enabling login authentication (insecure)") } else if config.SMTPAuthAcceptAny { - logger.Log().Info("[smtpd] enabling all auth (insecure)") + logger.Log().Info("[smtpd] enabling any authentication (insecure)") } } else { if auth.SMTPCredentials != nil { - logger.Log().Info("[smtpd] enabling login auth (TLS)") + logger.Log().Info("[smtpd] enabling login authentication") } else if config.SMTPAuthAcceptAny { - logger.Log().Info("[smtpd] enabling any auth (TLS)") + logger.Log().Info("[smtpd] enabling any authentication") } } - logger.Log().Infof("[smtpd] starting on %s", config.SMTPListen) + smtpType := "no encryption" + + if config.SMTPTLSCert != "" { + if config.SMTPRequireSTARTTLS { + smtpType = "STARTTLS required" + } else if config.SMTPRequireTLS { + smtpType = "SSL/TLS required" + } else { + smtpType = "STARTTLS optional" + if !config.SMTPAuthAllowInsecure && auth.SMTPCredentials != nil { + smtpType = "STARTTLS required" + } + } + + } + + logger.Log().Infof("[smtpd] starting on %s (%s)", config.SMTPListen, smtpType) return listenAndServe(config.SMTPListen, mailHandler, authHandler) } @@ -221,7 +237,8 @@ func listenAndServe(addr string, handler smtpd.Handler, authHandler smtpd.AuthHa } if config.SMTPTLSCert != "" { - srv.TLSRequired = config.SMTPTLSRequired + srv.TLSRequired = config.SMTPRequireSTARTTLS + srv.TLSListener = config.SMTPRequireTLS // if true overrules srv.TLSRequired if err := srv.ConfigureTLS(config.SMTPTLSCert, config.SMTPTLSKey); err != nil { return err }