1
0
mirror of https://github.com/axllent/mailpit.git synced 2024-12-28 23:06:43 +02:00

Merge branch 'feature/smtp-tls' into develop

This commit is contained in:
Ralph Slooten 2024-03-17 14:59:34 +13:00
commit e70cb75d4a
4 changed files with 80 additions and 35 deletions

View File

@ -32,31 +32,24 @@ Mailpit was originally **inspired** by MailHog which is [no longer maintained](h
## Features
- Runs entirely from a single [static binary](https://mailpit.axllent.org/docs/install/)
- Modern web UI to view emails (formatted HTML, highlighted HTML source, text, headers, raw source, and MIME attachments
including image thumbnails), including optional [HTTPS](https://mailpit.axllent.org/docs/configuration/https/)
- Optional [basic authentication](https://mailpit.axllent.org/docs/configuration/frontend-authentication/) for web UI & API
- Runs entirely from a single [static binary](https://mailpit.axllent.org/docs/install/) or multi-architecture [Docker images](https://mailpit.axllent.org/docs/install/docker/)
- Modern web UI with advanced [mail search](https://mailpit.axllent.org/docs/usage/search-filters/) to view emails (formatted HTML, highlighted HTML source, text, headers, raw source, and MIME attachments
including image thumbnails), including optional [HTTPS](https://mailpit.axllent.org/docs/configuration/http/) & [authentication](https://mailpit.axllent.org/docs/configuration/http/)
- [SMTP server](https://mailpit.axllent.org/docs/configuration/smtp/) with optional STARTTLS or SSL/TLS, authentication (including an "accept any" mode)
- A [REST API](https://mailpit.axllent.org/docs/api-v1/) for integration testing
- Real-time web UI updates using web sockets for new mail & optional [browser notifications](https://mailpit.axllent.org/docs/usage/notifications/) when new mail is received
- Optional [POP3 server](https://mailpit.axllent.org/docs/configuration/pop3/) to download captured message directly into your email client
- [HTML check](https://mailpit.axllent.org/docs/usage/html-check/) to test & score mail client compatibility with HTML emails
- [Link check](https://mailpit.axllent.org/docs/usage/link-check/) to test message links (HTML & text) & linked images
- [Spam check](https://mailpit.axllent.org/docs/usage/spamassassin/) to test message "spamminess" using a running SpamAssassin server
- [Create screenshots](https://mailpit.axllent.org/docs/usage/html-screenshots/) of HTML messages via web UI
- `List-Unsubscribe` syntax validation
- Mobile and tablet HTML preview toggle in desktop mode
- Advanced [mail search](https://mailpit.axllent.org/docs/usage/search-filters/)
- [Message tagging](https://mailpit.axllent.org/docs/usage/tagging/)
- Real-time web UI updates using web sockets for new mail & optional browser notifications for new mail (when accessed
via either HTTPS or `localhost` only)
- SMTP server with optional [STARTTLS & SMTP authentication](https://mailpit.axllent.org/docs/configuration/smtp-authentication/) (including an
"accept any" mode)
- [SMTP relaying](https://mailpit.axllent.org/docs/configuration/smtp-relay/) (message release) - relay messages via a different SMTP server
including an optional allowlist of accepted recipients
- Fast SMTP processing & storing - ingesting 100-200 emails per second depending on CPU, network speed & email size,
easily handling tens of thousands of emails
- Optional [POP3 server](https://mailpit.axllent.org/docs/configuration/pop3/) to download captured message directly into your email client
- Configurable automatic email pruning (default keeps the most recent 500 emails)
- A simple [REST API](https://mailpit.axllent.org/docs/api-v1/) for integration testing
- [Message tagging](https://mailpit.axllent.org/docs/usage/tagging/) including manual tagging or automated tagging using filtering and "plus addressing"
- [SMTP relaying](https://mailpit.axllent.org/docs/configuration/smtp-relay/) (message release) - relay messages via a different SMTP server including an optional allowlist of accepted recipients
- Fast message [storing & processing](https://mailpit.axllent.org/docs/configuration/email-storage/) - ingesting 100-200 emails per second over SMTP depending on CPU, network speed & email size,
easily handling tens of thousands of emails, with automatic email pruning (by default keeping the most recent 500 emails)
- `List-Unsubscribe` syntax validation
- Optional [webhook](https://mailpit.axllent.org/docs/integration/webhook/) for received messages
- Multi-architecture [Docker images](https://mailpit.axllent.org/docs/install/docker/)
## Installation

View File

@ -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 <CR><CR><LF>")
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

View File

@ -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 != "" {
@ -265,6 +273,18 @@ func VerifyConfig() error {
if err := auth.SetSMTPAuth(string(b)); err != nil {
return err
}
if !SMTPAuthAllowInsecure {
// https://www.rfc-editor.org/rfc/rfc4954
// A server implementation MUST implement a configuration in which
// it does NOT permit any plaintext password mechanisms, unless either
// the STARTTLS [SMTP-TLS] command has been negotiated or some other
// mechanism that protects the session from password snooping has been
// provided. Server sites SHOULD NOT use any configuration which
// permits a plaintext password mechanism without such a protection
// mechanism against password snooping.
SMTPRequireSTARTTLS = true
}
}
if auth.SMTPCredentials != nil && SMTPAuthAcceptAny {
@ -272,7 +292,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

View File

@ -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
}