From 053779c656afbafac58ccea9f815dc65cb62f23d Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Mon, 11 Mar 2024 21:53:30 +1300 Subject: [PATCH 1/4] Chore: Reorder CLI flags to group by related functionality --- cmd/root.go | 145 +++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 9411243..b5b0443 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -83,21 +83,27 @@ func init() { initConfigFromEnv() rootCmd.Flags().StringVarP(&config.DataFile, "db-file", "d", config.DataFile, "Database file to store persistent data") - rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port") - rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI") rootCmd.Flags().IntVarP(&config.MaxMessages, "max", "m", config.MaxMessages, "Max number of messages to store") - rootCmd.Flags().StringVar(&config.Webroot, "webroot", config.Webroot, "Set the webroot for web UI & API") - rootCmd.Flags().StringVar(&server.AccessControlAllowOrigin, "api-cors", server.AccessControlAllowOrigin, "Set API CORS Access-Control-Allow-Origin header") rootCmd.Flags().BoolVar(&config.UseMessageDates, "use-message-dates", config.UseMessageDates, "Use message dates as the received dates") rootCmd.Flags().BoolVar(&config.IgnoreDuplicateIDs, "ignore-duplicate-ids", config.IgnoreDuplicateIDs, "Ignore duplicate messages (by Message-Id)") - rootCmd.Flags().BoolVar(&config.DisableHTMLCheck, "disable-html-check", config.DisableHTMLCheck, "Disable the HTML check functionality (web UI & API)") - rootCmd.Flags().BoolVar(&config.BlockRemoteCSSAndFonts, "block-remote-css-and-fonts", config.BlockRemoteCSSAndFonts, "Block access to remote CSS & fonts") - rootCmd.Flags().StringVar(&config.EnableSpamAssassin, "enable-spamassassin", config.EnableSpamAssassin, "Enable integration with SpamAssassin") + rootCmd.Flags().StringVar(&logger.LogFile, "log-file", logger.LogFile, "Log output to file instead of stdout") + rootCmd.Flags().BoolVarP(&logger.QuietLogging, "quiet", "q", logger.QuietLogging, "Quiet logging (errors only)") + rootCmd.Flags().BoolVarP(&logger.VerboseLogging, "verbose", "v", logger.VerboseLogging, "Verbose logging") + // Web UI / API + rootCmd.Flags().StringVarP(&config.HTTPListen, "listen", "l", config.HTTPListen, "HTTP bind interface and port for UI") + rootCmd.Flags().StringVar(&config.Webroot, "webroot", config.Webroot, "Set the webroot for web UI & API") rootCmd.Flags().StringVar(&config.UIAuthFile, "ui-auth-file", config.UIAuthFile, "A password file for web UI & API authentication") rootCmd.Flags().StringVar(&config.UITLSCert, "ui-tls-cert", config.UITLSCert, "TLS certificate for web UI (HTTPS) - requires ui-tls-key") rootCmd.Flags().StringVar(&config.UITLSKey, "ui-tls-key", config.UITLSKey, "TLS key for web UI (HTTPS) - requires ui-tls-cert") + rootCmd.Flags().StringVar(&server.AccessControlAllowOrigin, "api-cors", server.AccessControlAllowOrigin, "Set API CORS Access-Control-Allow-Origin header") + rootCmd.Flags().BoolVar(&config.DisableHTMLCheck, "disable-html-check", config.DisableHTMLCheck, "Disable the HTML check functionality (web UI & API)") + rootCmd.Flags().BoolVar(&config.BlockRemoteCSSAndFonts, "block-remote-css-and-fonts", config.BlockRemoteCSSAndFonts, "Block access to remote CSS & fonts") + rootCmd.Flags().StringVar(&config.EnableSpamAssassin, "enable-spamassassin", config.EnableSpamAssassin, "Enable integration with SpamAssassin") + rootCmd.Flags().BoolVar(&config.AllowUntrustedTLS, "allow-untrusted-tls", config.AllowUntrustedTLS, "Do not verify HTTPS certificates (link checker & screenshots)") + // SMTP server + rootCmd.Flags().StringVarP(&config.SMTPListen, "smtp", "s", config.SMTPListen, "SMTP bind interface and port") rootCmd.Flags().StringVar(&config.SMTPAuthFile, "smtp-auth-file", config.SMTPAuthFile, "A password file for SMTP authentication") 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") @@ -109,26 +115,25 @@ func init() { rootCmd.Flags().StringVar(&config.SMTPAllowedRecipients, "smtp-allowed-recipients", config.SMTPAllowedRecipients, "Only allow SMTP recipients matching a regular expression (default allow all)") rootCmd.Flags().BoolVar(&smtpd.DisableReverseDNS, "smtp-disable-rdns", smtpd.DisableReverseDNS, "Disable SMTP reverse DNS lookups") + // SMTP relay rootCmd.Flags().StringVar(&config.SMTPRelayConfigFile, "smtp-relay-config", config.SMTPRelayConfigFile, "SMTP configuration file to allow releasing messages") rootCmd.Flags().BoolVar(&config.SMTPRelayAllIncoming, "smtp-relay-all", config.SMTPRelayAllIncoming, "Relay all incoming messages via external SMTP server (caution!)") + // POP3 server rootCmd.Flags().StringVar(&config.POP3Listen, "pop3", config.POP3Listen, "POP3 server bind interface and port") rootCmd.Flags().StringVar(&config.POP3AuthFile, "pop3-auth-file", config.POP3AuthFile, "A password file for POP3 server authentication (enables POP3 server)") rootCmd.Flags().StringVar(&config.POP3TLSCert, "pop3-tls-cert", config.POP3TLSCert, "Optional TLS certificate for POP3 server - requires pop3-tls-key") rootCmd.Flags().StringVar(&config.POP3TLSKey, "pop3-tls-key", config.POP3TLSKey, "Optional TLS key for POP3 server - requires pop3-tls-cert") + // Tagging + rootCmd.Flags().StringVarP(&config.SMTPCLITags, "tag", "t", config.SMTPCLITags, "Tag new messages matching filters") + rootCmd.Flags().BoolVar(&tools.TagsTitleCase, "tags-title-case", tools.TagsTitleCase, "Convert new tags automatically to TitleCase") + + // Webhook rootCmd.Flags().StringVar(&config.WebhookURL, "webhook-url", config.WebhookURL, "Send a webhook request for new messages") rootCmd.Flags().IntVar(&webhook.RateLimit, "webhook-limit", webhook.RateLimit, "Limit webhook requests per second") - rootCmd.Flags().BoolVar(&config.AllowUntrustedTLS, "allow-untrusted-tls", config.AllowUntrustedTLS, "Do not verify HTTPS certificates (link checker & screenshots)") - - rootCmd.Flags().StringVarP(&config.SMTPCLITags, "tag", "t", config.SMTPCLITags, "Tag new messages matching filters") - rootCmd.Flags().BoolVar(&tools.TagsTitleCase, "tags-title-case", tools.TagsTitleCase, "Convert new tags automatically to TitleCase") - rootCmd.Flags().StringVar(&logger.LogFile, "log-file", logger.LogFile, "Log output to file instead of stdout") - rootCmd.Flags().BoolVarP(&logger.QuietLogging, "quiet", "q", logger.QuietLogging, "Quiet logging (errors only)") - rootCmd.Flags().BoolVarP(&logger.VerboseLogging, "verbose", "v", logger.VerboseLogging, "Verbose logging") - - // deprecated flags 2023/03/12 + // DEPRECATED FLAGS 2023/03/12 rootCmd.Flags().StringVar(&config.UITLSCert, "ui-ssl-cert", config.UITLSCert, "SSL certificate for web UI - requires ui-ssl-key") rootCmd.Flags().StringVar(&config.UITLSKey, "ui-ssl-key", config.UITLSKey, "SSL key for web UI - requires ui-ssl-cert") rootCmd.Flags().StringVar(&config.SMTPTLSCert, "smtp-ssl-cert", config.SMTPTLSCert, "SSL certificate for SMTP - requires smtp-ssl-key") @@ -145,39 +150,72 @@ func init() { // Load settings from environment func initConfigFromEnv() { - // inherit from environment if provided + // General config.DataFile = os.Getenv("MP_DATA_FILE") - if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 { - config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR") - } - if len(os.Getenv("MP_UI_BIND_ADDR")) > 0 { - config.HTTPListen = os.Getenv("MP_UI_BIND_ADDR") - } if len(os.Getenv("MP_MAX_MESSAGES")) > 0 { config.MaxMessages, _ = strconv.Atoi(os.Getenv("MP_MAX_MESSAGES")) } + if getEnabledFromEnv("MP_USE_MESSAGE_DATES") { + config.UseMessageDates = true + } + if getEnabledFromEnv("MP_IGNORE_DUPLICATE_IDS") { + config.IgnoreDuplicateIDs = true + } + if len(os.Getenv("MP_LOG_FILE")) > 0 { + logger.LogFile = os.Getenv("MP_LOG_FILE") + } + if getEnabledFromEnv("MP_QUIET") { + logger.QuietLogging = true + } + if getEnabledFromEnv("MP_VERBOSE") { + logger.VerboseLogging = true + } - // UI + // Web UI & API + if len(os.Getenv("MP_UI_BIND_ADDR")) > 0 { + config.HTTPListen = os.Getenv("MP_UI_BIND_ADDR") + } + if len(os.Getenv("MP_WEBROOT")) > 0 { + config.Webroot = os.Getenv("MP_WEBROOT") + } config.UIAuthFile = os.Getenv("MP_UI_AUTH_FILE") if err := auth.SetUIAuth(os.Getenv("MP_UI_AUTH")); err != nil { logger.Log().Errorf(err.Error()) } config.UITLSCert = os.Getenv("MP_UI_TLS_CERT") config.UITLSKey = os.Getenv("MP_UI_TLS_KEY") + if len(os.Getenv("MP_API_CORS")) > 0 { + server.AccessControlAllowOrigin = os.Getenv("MP_API_CORS") + } + if getEnabledFromEnv("MP_DISABLE_HTML_CHECK") { + config.DisableHTMLCheck = true + } + if getEnabledFromEnv("MP_BLOCK_REMOTE_CSS_AND_FONTS") { + config.BlockRemoteCSSAndFonts = true + } + if len(os.Getenv("MP_ENABLE_SPAMASSASSIN")) > 0 { + config.EnableSpamAssassin = os.Getenv("MP_ENABLE_SPAMASSASSIN") + } + if getEnabledFromEnv("MP_ALLOW_UNTRUSTED_TLS") { + config.AllowUntrustedTLS = true + } - // SMTP + // SMTP server + if len(os.Getenv("MP_SMTP_BIND_ADDR")) > 0 { + config.SMTPListen = os.Getenv("MP_SMTP_BIND_ADDR") + } config.SMTPAuthFile = os.Getenv("MP_SMTP_AUTH_FILE") if err := auth.SetSMTPAuth(os.Getenv("MP_SMTP_AUTH")); err != nil { logger.Log().Errorf(err.Error()) } + if getEnabledFromEnv("MP_SMTP_AUTH_ACCEPT_ANY") { + config.SMTPAuthAcceptAny = true + } 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_AUTH_ACCEPT_ANY") { - config.SMTPAuthAcceptAny = true - } if getEnabledFromEnv("MP_SMTP_AUTH_ALLOW_INSECURE") { config.SMTPAuthAllowInsecure = true } @@ -194,13 +232,13 @@ func initConfigFromEnv() { smtpd.DisableReverseDNS = true } - // Relay server config + // SMTP relay config.SMTPRelayConfigFile = os.Getenv("MP_SMTP_RELAY_CONFIG") if getEnabledFromEnv("MP_SMTP_RELAY_ALL") { config.SMTPRelayAllIncoming = true } - // POP3 + // POP3 server if len(os.Getenv("MP_POP3_BIND_ADDR")) > 0 { config.POP3Listen = os.Getenv("MP_POP3_BIND_ADDR") } @@ -211,53 +249,20 @@ func initConfigFromEnv() { config.POP3TLSCert = os.Getenv("MP_POP3_TLS_CERT") config.POP3TLSKey = os.Getenv("MP_POP3_TLS_KEY") - // Webhook - if len(os.Getenv("MP_WEBHOOK_URL")) > 0 { - config.WebhookURL = os.Getenv("MP_WEBHOOK_URL") - } - if len(os.Getenv("MP_WEBHOOK_LIMIT")) > 0 { - webhook.RateLimit, _ = strconv.Atoi(os.Getenv("MP_WEBHOOK_LIMIT")) - } - - // Misc options - if len(os.Getenv("MP_WEBROOT")) > 0 { - config.Webroot = os.Getenv("MP_WEBROOT") - } - if len(os.Getenv("MP_API_CORS")) > 0 { - server.AccessControlAllowOrigin = os.Getenv("MP_API_CORS") - } - if getEnabledFromEnv("MP_USE_MESSAGE_DATES") { - config.UseMessageDates = true - } - if getEnabledFromEnv("MP_IGNORE_DUPLICATE_IDS") { - config.IgnoreDuplicateIDs = true - } - if getEnabledFromEnv("MP_DISABLE_HTML_CHECK") { - config.DisableHTMLCheck = true - } - if getEnabledFromEnv("MP_BLOCK_REMOTE_CSS_AND_FONTS") { - config.BlockRemoteCSSAndFonts = true - } - if len(os.Getenv("MP_ENABLE_SPAMASSASSIN")) > 0 { - config.EnableSpamAssassin = os.Getenv("MP_ENABLE_SPAMASSASSIN") - } - if getEnabledFromEnv("MP_ALLOW_UNTRUSTED_TLS") { - config.AllowUntrustedTLS = true - } + // Tagging if len(os.Getenv("MP_TAG")) > 0 { config.SMTPCLITags = os.Getenv("MP_TAG") } if getEnabledFromEnv("MP_TAGS_TITLE_CASE") { tools.TagsTitleCase = getEnabledFromEnv("MP_TAGS_TITLE_CASE") } - if len(os.Getenv("MP_LOG_FILE")) > 0 { - logger.LogFile = os.Getenv("MP_LOG_FILE") + + // Webhook + if len(os.Getenv("MP_WEBHOOK_URL")) > 0 { + config.WebhookURL = os.Getenv("MP_WEBHOOK_URL") } - if getEnabledFromEnv("MP_QUIET") { - logger.QuietLogging = true - } - if getEnabledFromEnv("MP_VERBOSE") { - logger.VerboseLogging = true + if len(os.Getenv("MP_WEBHOOK_LIMIT")) > 0 { + webhook.RateLimit, _ = strconv.Atoi(os.Getenv("MP_WEBHOOK_LIMIT")) } } From a324d817b3f28ca718ad9615a5bf66d26db14d94 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Tue, 12 Mar 2024 17:07:25 +1300 Subject: [PATCH 2/4] Feature: Allow setting SMTP relay configuration values via environment variables (#262) --- cmd/root.go | 15 ++++- config/config.go | 66 +++++++++++++------- server/apiv1/api.go | 2 +- server/apiv1/webui.go | 9 ++- server/smtpd/smtp.go | 6 +- server/ui-src/components/message/Release.vue | 4 +- server/ui/api/v1/swagger.json | 8 +-- 7 files changed, 76 insertions(+), 34 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index b5b0443..0866e04 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -79,7 +79,7 @@ func init() { // load and warn deprecated ENV vars initDeprecatedConfigFromEnv() - // load ENV vars + // load environment variables initConfigFromEnv() rootCmd.Flags().StringVarP(&config.DataFile, "db-file", "d", config.DataFile, "Database file to store persistent data") @@ -237,6 +237,19 @@ func initConfigFromEnv() { if getEnabledFromEnv("MP_SMTP_RELAY_ALL") { config.SMTPRelayAllIncoming = true } + config.SMTPRelayConfig = config.SMTPRelayConfigStruct{} + config.SMTPRelayConfig.Host = os.Getenv("MP_SMTP_RELAY_HOST") + if len(os.Getenv("MP_SMTP_RELAY_PORT")) > 0 { + config.SMTPRelayConfig.Port, _ = strconv.Atoi(os.Getenv("MP_SMTP_RELAY_PORT")) + } + config.SMTPRelayConfig.STARTTLS = getEnabledFromEnv("MP_SMTP_RELAY_STARTTLS") + config.SMTPRelayConfig.AllowInsecure = getEnabledFromEnv("MP_SMTP_RELAY_ALLOW_INSECURE") + config.SMTPRelayConfig.Auth = os.Getenv("MP_SMTP_RELAY_AUTH") + config.SMTPRelayConfig.Username = os.Getenv("MP_SMTP_RELAY_USERNAME") + config.SMTPRelayConfig.Password = os.Getenv("MP_SMTP_RELAY_PASSWORD") + config.SMTPRelayConfig.Secret = os.Getenv("MP_SMTP_RELAY_SECRET") + config.SMTPRelayConfig.ReturnPath = os.Getenv("MP_SMTP_RELAY_RETURN_PATH") + config.SMTPRelayConfig.AllowedRecipients = os.Getenv("MP_SMTP_RELAY_ALLOWED_RECIPIENTS") // POP3 server if len(os.Getenv("MP_POP3_BIND_ADDR")) > 0 { diff --git a/config/config.go b/config/config.go index 0efc32c..94d6a70 100644 --- a/config/config.go +++ b/config/config.go @@ -94,7 +94,7 @@ var ( SMTPRelayConfigFile string // SMTPRelayConfig to parse a yaml file and store config of relay SMTP server - SMTPRelayConfig smtpRelayConfigStruct + SMTPRelayConfig SMTPRelayConfigStruct // SMTPStrictRFCHeaders will return an error if the email headers contain (\r\r\n) // @see https://github.com/axllent/mailpit/issues/87 & https://github.com/axllent/mailpit/issues/153 @@ -154,18 +154,20 @@ type AutoTag struct { } // SMTPRelayConfigStruct struct for parsing yaml & storing variables -type smtpRelayConfigStruct struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - STARTTLS bool `yaml:"starttls"` - AllowInsecure bool `yaml:"allow-insecure"` - Auth string `yaml:"auth"` // none, plain, login, cram-md5 - Username string `yaml:"username"` // plain & cram-md5 - Password string `yaml:"password"` // plain - Secret string `yaml:"secret"` // cram-md5 - ReturnPath string `yaml:"return-path"` // allow overriding the bounce address - RecipientAllowlist string `yaml:"recipient-allowlist"` // regex, if set needs to match for mails to be relayed - RecipientAllowlistRegexp *regexp.Regexp +type SMTPRelayConfigStruct struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + STARTTLS bool `yaml:"starttls"` + AllowInsecure bool `yaml:"allow-insecure"` + Auth string `yaml:"auth"` // none, plain, login, cram-md5 + Username string `yaml:"username"` // plain & cram-md5 + Password string `yaml:"password"` // plain + Secret string `yaml:"secret"` // cram-md5 + ReturnPath string `yaml:"return-path"` // allow overriding the bounce address + AllowedRecipients string `yaml:"allowed-recipients"` // regex, if set needs to match for mails to be relayed + AllowedRecipientsRegexp *regexp.Regexp // compiled regexp using AllowedRecipients + // DEPRECATED 2024/03/12 + RecipientAllowlist string `yaml:"recipient-allowlist"` } // VerifyConfig wil do some basic checking @@ -371,6 +373,11 @@ func VerifyConfig() error { return err } + // separate relay config validation to account for environment variables + if err := validateRelayConfig(); err != nil { + return err + } + if !ReleaseEnabled && SMTPRelayAllIncoming { return errors.New("[smtp] relay config must be set to relay all messages") } @@ -383,7 +390,7 @@ func VerifyConfig() error { return nil } -// Parse & validate the SMTPRelayConfigFile (if set) +// Parse the SMTPRelayConfigFile (if set) func parseRelayConfig(c string) error { if c == "" { return nil @@ -408,6 +415,23 @@ func parseRelayConfig(c string) error { return errors.New("[smtp] relay host not set") } + // DEPRECATED 2024/03/12 + if SMTPRelayConfig.RecipientAllowlist != "" { + logger.Log().Warn("[smtp] relay 'recipient-allowlist' is deprecated, use 'allowed_recipients' instead") + if SMTPRelayConfig.AllowedRecipients == "" { + SMTPRelayConfig.AllowedRecipients = SMTPRelayConfig.RecipientAllowlist + } + } + + return nil +} + +// Validate the SMTPRelayConfig (if Host is set) +func validateRelayConfig() error { + if SMTPRelayConfig.Host == "" { + return nil + } + if SMTPRelayConfig.Port == 0 { SMTPRelayConfig.Port = 25 // default } @@ -418,17 +442,17 @@ func parseRelayConfig(c string) error { SMTPRelayConfig.Auth = "none" } else if SMTPRelayConfig.Auth == "plain" { if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Password == "" { - return fmt.Errorf("[smtp] relay host username or password not set for PLAIN authentication (%s)", c) + return fmt.Errorf("[smtp] relay host username or password not set for PLAIN authentication") } } else if SMTPRelayConfig.Auth == "login" { SMTPRelayConfig.Auth = "login" if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Password == "" { - return fmt.Errorf("[smtp] relay host username or password not set for LOGIN authentication (%s)", c) + return fmt.Errorf("[smtp] relay host username or password not set for LOGIN authentication") } } else if strings.HasPrefix(SMTPRelayConfig.Auth, "cram") { SMTPRelayConfig.Auth = "cram-md5" if SMTPRelayConfig.Username == "" || SMTPRelayConfig.Secret == "" { - return fmt.Errorf("[smtp] relay host username or secret not set for CRAM-MD5 authentication (%s)", c) + return fmt.Errorf("[smtp] relay host username or secret not set for CRAM-MD5 authentication") } } else { return fmt.Errorf("[smtp] relay authentication method not supported: %s", SMTPRelayConfig.Auth) @@ -438,15 +462,15 @@ func parseRelayConfig(c string) error { logger.Log().Infof("[smtp] enabling message relaying via %s:%d", SMTPRelayConfig.Host, SMTPRelayConfig.Port) - allowlistRegexp, err := regexp.Compile(SMTPRelayConfig.RecipientAllowlist) + allowlistRegexp, err := regexp.Compile(SMTPRelayConfig.AllowedRecipients) - if SMTPRelayConfig.RecipientAllowlist != "" { + if SMTPRelayConfig.AllowedRecipients != "" { if err != nil { return fmt.Errorf("[smtp] failed to compile relay recipient allowlist regexp: %s", err.Error()) } - SMTPRelayConfig.RecipientAllowlistRegexp = allowlistRegexp - logger.Log().Infof("[smtp] relay recipient allowlist is active with the following regexp: %s", SMTPRelayConfig.RecipientAllowlist) + SMTPRelayConfig.AllowedRecipientsRegexp = allowlistRegexp + logger.Log().Infof("[smtp] relay recipient allowlist is active with the following regexp: %s", SMTPRelayConfig.AllowedRecipients) } diff --git a/server/apiv1/api.go b/server/apiv1/api.go index cf185e6..9c30abe 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -642,7 +642,7 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { return } - if config.SMTPRelayConfig.RecipientAllowlistRegexp != nil && !config.SMTPRelayConfig.RecipientAllowlistRegexp.MatchString(address.Address) { + if config.SMTPRelayConfig.AllowedRecipientsRegexp != nil && !config.SMTPRelayConfig.AllowedRecipientsRegexp.MatchString(address.Address) { httpError(w, "Mail address does not match allowlist: "+to) return } diff --git a/server/apiv1/webui.go b/server/apiv1/webui.go index c8f4107..0b134c3 100644 --- a/server/apiv1/webui.go +++ b/server/apiv1/webui.go @@ -20,7 +20,10 @@ type webUIConfiguration struct { SMTPServer string // Enforced Return-Path (if set) for relay bounces ReturnPath string - // Allowlist of accepted recipients + // Only allow relaying to these recipients (regex) + AllowedRecipients string + // DEPRECATED 2024/03/12 + // swagger:ignore RecipientAllowlist string } @@ -57,7 +60,9 @@ func WebUIConfig(w http.ResponseWriter, _ *http.Request) { if config.ReleaseEnabled { conf.MessageRelay.SMTPServer = fmt.Sprintf("%s:%d", config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port) conf.MessageRelay.ReturnPath = config.SMTPRelayConfig.ReturnPath - conf.MessageRelay.RecipientAllowlist = config.SMTPRelayConfig.RecipientAllowlist + conf.MessageRelay.AllowedRecipients = config.SMTPRelayConfig.AllowedRecipients + // DEPRECATED 2024/03/12 + conf.MessageRelay.RecipientAllowlist = config.SMTPRelayConfig.AllowedRecipients } conf.DisableHTMLCheck = config.DisableHTMLCheck diff --git a/server/smtpd/smtp.go b/server/smtpd/smtp.go index 25f20bb..863c256 100644 --- a/server/smtpd/smtp.go +++ b/server/smtpd/smtp.go @@ -12,7 +12,7 @@ import ( ) func allowedRecipients(to []string) []string { - if config.SMTPRelayConfig.RecipientAllowlistRegexp == nil { + if config.SMTPRelayConfig.AllowedRecipientsRegexp == nil { return to } @@ -26,8 +26,8 @@ func allowedRecipients(to []string) []string { continue } - if !config.SMTPRelayConfig.RecipientAllowlistRegexp.MatchString(address.Address) { - logger.Log().Debugf("[smtp] not allowed to relay to %s: does not match the allowlist %s", recipient, config.SMTPRelayConfig.RecipientAllowlist) + if !config.SMTPRelayConfig.AllowedRecipientsRegexp.MatchString(address.Address) { + logger.Log().Debugf("[smtp] not allowed to relay to %s: does not match the allowlist %s", recipient, config.SMTPRelayConfig.AllowedRecipients) } else { ar = append(ar, recipient) } diff --git a/server/ui-src/components/message/Release.vue b/server/ui-src/components/message/Release.vue index 12a9043..799bb35 100644 --- a/server/ui-src/components/message/Release.vue +++ b/server/ui-src/components/message/Release.vue @@ -126,10 +126,10 @@ export default { -
+
Note: A recipient allowlist has been configured. Any mail address not matching it will be rejected.
- Configured allowlist: {{ mailbox.uiConfig.MessageRelay.RecipientAllowlist }} + Allowed recipients: {{ mailbox.uiConfig.MessageRelay.AllowedRecipients }}
Note: For testing purposes, a unique Message-Id will be generated on send. diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json index 8b99aab..54dd822 100644 --- a/server/ui/api/v1/swagger.json +++ b/server/ui/api/v1/swagger.json @@ -1407,14 +1407,14 @@ "description": "Message Relay information", "type": "object", "properties": { + "AllowedRecipients": { + "description": "Only allow relaying to these recipients (regex)", + "type": "string" + }, "Enabled": { "description": "Whether message relaying (release) is enabled", "type": "boolean" }, - "RecipientAllowlist": { - "description": "Allowlist of accepted recipients", - "type": "string" - }, "ReturnPath": { "description": "Enforced Return-Path (if set) for relay bounces", "type": "string" From edc529fbdeba554f470b2e76367b4db1b9375027 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Tue, 12 Mar 2024 17:11:43 +1300 Subject: [PATCH 3/4] Chore: Update caniemail test data --- internal/htmlcheck/caniemail-data.json | 222 +++++++++++++++++-------- 1 file changed, 151 insertions(+), 71 deletions(-) diff --git a/internal/htmlcheck/caniemail-data.json b/internal/htmlcheck/caniemail-data.json index 003053d..5e125ad 100644 --- a/internal/htmlcheck/caniemail-data.json +++ b/internal/htmlcheck/caniemail-data.json @@ -1,6 +1,6 @@ { "api_version":"1.0.4", - "last_update_date":"2024-01-31 20:28:42 +0000", + "last_update_date":"2024-03-09 07:21:32 +0000", "nicenames":{"family":{"gmail":"Gmail","outlook":"Outlook","yahoo":"Yahoo! Mail","apple-mail":"Apple Mail","aol":"AOL","thunderbird":"Mozilla Thunderbird","microsoft":"Microsoft","samsung-email":"Samsung Email","sfr":"SFR","orange":"Orange","protonmail":"ProtonMail","hey":"HEY","mail-ru":"Mail.ru","fastmail":"Fastmail","laposte":"LaPoste.net","t-online-de":"T-online.de","free-fr":"Free.fr","gmx":"GMX","web-de":"WEB.DE","ionos-1and1":"1&1","rainloop":"RainLoop","wp-pl":"WP.pl"},"platform":{"desktop-app":"Desktop","desktop-webmail":"Desktop Webmail","mobile-webmail":"Mobile Webmail","webmail":"Webmail","ios":"iOS","android":"Android","windows":"Windows","macos":"macOS","windows-mail":"Windows Mail","outlook-com":"Outlook.com"},"support":{"supported":"Supported","mitigated":"Partially supported","unsupported":"Not supported","unknown":"Support unknown","mixed":"Mixed support"},"category":{"html":"HTML","css":"CSS","image":"Image formats","others":"Others"}}, "data":[ { @@ -240,7 +240,7 @@ "test_results_url":"https://app.emailonacid.com/app/acidtest/hMLCNCSKZYHkLgLOpIWltlnYjtagbNsrwzMxalc2VbghN/list", "stats":{"apple-mail":{"macos":{"10.3":"y"},"ios":{"10.3":"y","12.2":"y"}},"gmail":{"desktop-webmail":{"2019-08":"a #1 #7","2020-01":"a #7"},"ios":{"2019-08":"a #1 #6 #7","2020-01":"a #6 #7"},"android":{"2019-08":"a #1 #6 #7","2022-07":"a #6 #7"},"mobile-webmail":{"2020-02":"n"}},"orange":{"desktop-webmail":{"2019-08":"y #5","2021-03":"y"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"outlook":{"windows":{"2003":"a #1","2007":"n","2010":"n","2013":"n","2016":"n","2019":"n"},"windows-mail":{"2020-01":"n"},"macos":{"2011":"y","2016":"y","16.80":"a #1 #10"},"outlook-com":{"2019-08":"a #1","2023-12":"a #1 #10"},"ios":{"2019-08":"a #1"},"android":{"2019-08":"a #1"}},"samsung-email":{"android":{"5.0.10.2":"y","6.0":"y","6.1.90.16":"a #9"}},"sfr":{"desktop-webmail":{"2019-08":"y"},"ios":{"2019-08":"n"},"android":{"2019-08":"n"}},"thunderbird":{"macos":{"60.3":"y","78.5":"n","102.11":"n"}},"yahoo":{"desktop-webmail":{"2019-08":"a #1 #2","2020-01":"a #2"},"ios":{"2019-08":"a #1 #2","2020-01":"a #2"},"android":{"2019-08":"a #1 #2 #3","2020-01":"a #2 #3"}},"aol":{"desktop-webmail":{"2019-02":"a #1 #2","2020-01":"a #2"},"ios":{"2019-02":"a #1 #2","2020-01":"a #2"},"android":{"2019-02":"a #1 #2","2020-01":"a #2"}},"protonmail":{"desktop-webmail":{"2020-03":"n","2023-05":"a #8"},"ios":{"2020-03":"n","2023-05":"a #8"},"android":{"2020-03":"y"}},"hey":{"desktop-webmail":{"2020-06":"y"}},"mail-ru":{"desktop-webmail":{"2020-10":"a #1 #7"}},"fastmail":{"desktop-webmail":{"2021-07":"y"}},"laposte":{"desktop-webmail":{"2021-08":"y"}},"gmx":{"desktop-webmail":{"2022-06":"y #1"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"web-de":{"desktop-webmail":{"2022-06":"y #1"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"ionos-1and1":{"desktop-webmail":{"2022-06":"y"},"android":{"2022-06":"y"}}}, "notes":null, - "notes_by_num":{"1":"Partial. Does not support nested media queries.","2":"Partial. Only supports `screen`, `min-width`, `max-width`, `min-height` and `max-height` based media queries.","3":"Buggy. Requires a double `` hack to work.","4":"Partial. Does not support simple `@media {}` declarations.","5":"Buggy. The first rule inside a media query is not prefixed.","6":"Partial. Not supported with non Gmail accounts.","7":"Partial. Does not support height based media queries.","8":"Partial. Does not support landscape media query.","9":"Partial. Not supported with Hotmail/Outlook accounts.","10":"Partial. Nested media queries are removed."} + "notes_by_num":{"1":"Partial. Does not support nested media queries.","2":"Partial. Only supports `screen`, `min-width`, `max-width`, `min-height` and `max-height` based media queries.","3":"Buggy. Requires a double `` hack to work.","4":"Partial. Does not support simple `@media {}` declarations.","5":"Buggy. The first rule inside a media query is not prefixed.","6":"Partial. Not supported with non Google accounts.","7":"Partial. Does not support height based media queries.","8":"Partial. Does not support landscape media query.","9":"Partial. Not supported with Hotmail/Outlook accounts.","10":"Partial. Nested media queries are removed."} }, { @@ -272,7 +272,7 @@ "test_results_url":"https://app.emailonacid.com/app/acidtest/oxaaoE6R3ur4T9fAPzVsQ3G2R7p1c9axDm7LLgC3cKw0F/list", "stats":{"apple-mail":{"macos":{"12.4":"y"},"ios":{"12.1":"y"}},"gmail":{"desktop-webmail":{"2019-02":"y"},"ios":{"2019-02":"a #1"},"android":{"2019-02":"a #1"},"mobile-webmail":{"2020-02":"n"}},"orange":{"desktop-webmail":{"2019-08":"y","2021-03":"n"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"outlook":{"windows":{"2007":"n","2010":"n","2013":"n","2016":"n","2019":"n"},"windows-mail":{"2019-02":"n"},"macos":{"2019-02":"y","16.80":"y"},"outlook-com":{"2019-02":"y"},"ios":{"2019-02":"y"},"android":{"2019-02":"y"}},"yahoo":{"desktop-webmail":{"2019-02":"n"},"ios":{"2019-02":"n"},"android":{"2019-02":"n"}},"aol":{"desktop-webmail":{"2019-02":"n"},"ios":{"2019-02":"n"},"android":{"2019-02":"n"}},"samsung-email":{"android":{"5.0.10.2":"y","6.0.04.6":"y"}},"sfr":{"desktop-webmail":{"2019-08":"y"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"thunderbird":{"macos":{"60.5.0":"y"}},"protonmail":{"desktop-webmail":{"2020-03":"y"},"ios":{"2020-03":"y"},"android":{"2020-03":"y"}},"hey":{"desktop-webmail":{"2020-06":"y"}},"mail-ru":{"desktop-webmail":{"2020-10":"y"}},"fastmail":{"desktop-webmail":{"2021-07":"y"}},"laposte":{"desktop-webmail":{"2021-08":"y"}},"gmx":{"desktop-webmail":{"2022-06":"n"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"web-de":{"desktop-webmail":{"2022-06":"n"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"ionos-1and1":{"desktop-webmail":{"2022-06":"y"},"android":{"2022-06":"y"}}}, "notes":null, - "notes_by_num":{"1":"Partial. Not supported with non Gmail accounts."} + "notes_by_num":{"1":"Partial. Not supported with non Google accounts."} }, { @@ -288,7 +288,7 @@ "test_results_url":"https://app.emailonacid.com/app/acidtest/oxaaoE6R3ur4T9fAPzVsQ3G2R7p1c9axDm7LLgC3cKw0F/list", "stats":{"apple-mail":{"macos":{"12.4":"y"},"ios":{"12.1":"y"}},"gmail":{"desktop-webmail":{"2019-02":"y"},"ios":{"2019-02":"a #1"},"android":{"2019-02":"a #1"},"mobile-webmail":{"2020-02":"n"}},"orange":{"desktop-webmail":{"2019-08":"y","2021-03":"n"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"outlook":{"windows":{"2007":"n","2010":"n","2013":"n","2016":"n","2019":"n"},"windows-mail":{"2019-02":"n"},"macos":{"2019-02":"y","16.80":"y"},"outlook-com":{"2019-02":"y"},"ios":{"2019-02":"y"},"android":{"2019-02":"y"}},"yahoo":{"desktop-webmail":{"2019-02":"n"},"ios":{"2019-02":"n"},"android":{"2019-02":"n"}},"aol":{"desktop-webmail":{"2019-02":"n"},"ios":{"2019-02":"n"},"android":{"2019-02":"n"}},"samsung-email":{"android":{"5.0.10.2":"y","6.0.04.6":"y"}},"sfr":{"desktop-webmail":{"2019-08":"y"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"thunderbird":{"macos":{"60.5.0":"y"}},"protonmail":{"desktop-webmail":{"2020-03":"y"},"ios":{"2020-03":"y"},"android":{"2020-03":"y"}},"hey":{"desktop-webmail":{"2020-06":"y"}},"mail-ru":{"desktop-webmail":{"2020-10":"y"}},"fastmail":{"desktop-webmail":{"2021-07":"y"}},"laposte":{"desktop-webmail":{"2021-08":"y"}},"gmx":{"desktop-webmail":{"2022-06":"n"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"web-de":{"desktop-webmail":{"2022-06":"n"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"ionos-1and1":{"desktop-webmail":{"2022-06":"y"},"android":{"2022-06":"y"}}}, "notes":null, - "notes_by_num":{"1":"Partial. Not supported with non Gmail accounts. But it can be used in the `background` shorthand property instead."} + "notes_by_num":{"1":"Partial. Not supported with non Google accounts. But it can be used in the `background` shorthand property instead."} }, { @@ -320,7 +320,7 @@ "test_results_url":"https://app.emailonacid.com/app/acidtest/04SuPXr8tEGhWRlJ2Us6dA8BzgREpyxHYEmSBeyNuWyWo/list", "stats":{"apple-mail":{"macos":{"12.4":"y"},"ios":{"12.1":"y"}},"gmail":{"desktop-webmail":{"2019-02":"y","2023-07":"a #6","2023-08":"y"},"ios":{"2018-09":"a #1","2018-10":"y","2019-02":"y"},"android":{"2018-09":"a #1","2018-10":"y","2019-02":"y"},"mobile-webmail":{"2020-02":"y"}},"orange":{"desktop-webmail":{"2019-08":"y","2021-03":"y"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"outlook":{"windows":{"2007":"n #5","2010":"n #5","2013":"n #5","2016":"n #5","2019":"n #5"},"windows-mail":{"2019-02":"n"},"macos":{"2019-02":"y","16.80":"y"},"outlook-com":{"2019-02":"y"},"ios":{"2019-02":"y"},"android":{"2019-02":"y"}},"yahoo":{"desktop-webmail":{"2019-02":"a #3 #4","2021-10":"a #3"},"ios":{"2019-02":"a #3 #4","2021-10":"a #3"},"android":{"2019-02":"a #3 #4","2021-10":"a #3"}},"aol":{"desktop-webmail":{"2019-02":"a #3 #4","2021-10":"a #3"},"ios":{"2019-02":"a #3 #4","2021-10":"a #3"},"android":{"2019-02":"a #3 #4","2021-10":"a #3"}},"samsung-email":{"android":{"5.0.10.2":"a #2","6.0.04.6":"y"}},"sfr":{"desktop-webmail":{"2019-08":"y"},"ios":{"2019-08":"y"},"android":{"2019-08":"y"}},"thunderbird":{"macos":{"60.5.0":"y"}},"protonmail":{"desktop-webmail":{"2020-03":"y"},"ios":{"2020-03":"y"},"android":{"2020-03":"y"}},"hey":{"desktop-webmail":{"2020-06":"y"}},"mail-ru":{"desktop-webmail":{"2020-10":"y"}},"fastmail":{"desktop-webmail":{"2021-07":"y"}},"laposte":{"desktop-webmail":{"2021-08":"y"}},"t-online-de":{"desktop-webmail":{"2021-11":"n"}},"free-fr":{"desktop-webmail":{"2021-11":"n"}},"gmx":{"desktop-webmail":{"2022-06":"a #3"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"web-de":{"desktop-webmail":{"2022-06":"a #3"},"ios":{"2022-06":"y"},"android":{"2022-06":"y"}},"ionos-1and1":{"desktop-webmail":{"2022-06":"y"},"android":{"2022-06":"y"}}}, "notes":null, - "notes_by_num":{"1":"Partial. Not supported with non Gmail accounts.","2":"Buggy. Requires at least one `` element in the email to download all images.","3":"Partial. Does not support multiple values. The comma between two values is removed.","4":"Partial. Images URL must be between quotes.","5":"Background images can be used in VML. See [backgrounds.cm](https://backgrounds.cm/) and [VML documentation](https://docs.microsoft.com/en-us/windows/win32/vml/web-workshop---how-to-use-vml-on-web-pages-----fill--element).","6":"Partial and buggy. Removes the entire `style` attribute or `