mirror of
https://github.com/axllent/mailpit.git
synced 2025-02-07 13:31:56 +02:00
Merge branch 'release/v1.6.8'
This commit is contained in:
commit
4800922f91
10
CHANGELOG.md
10
CHANGELOG.md
@ -2,6 +2,16 @@
|
||||
|
||||
Notable changes to Mailpit will be documented in this file.
|
||||
|
||||
## [v1.6.8]
|
||||
|
||||
### Bugfix
|
||||
- Fix Date display when message doesn't contain a Date header
|
||||
|
||||
### Feature
|
||||
- Add allowlist to filter recipients before relaying messages ([#109](https://github.com/axllent/mailpit/issues/109))
|
||||
- Add `-S` short flag for sendmail `--smtp-addr`
|
||||
|
||||
|
||||
## [v1.6.7]
|
||||
|
||||
### Bugfix
|
||||
|
@ -28,7 +28,7 @@ Mailpit is inspired by [MailHog](#why-rewrite-mailhog), but much, much faster.
|
||||
- Configurable automatic email pruning (default keeps the most recent 500 emails)
|
||||
- Email storage either in a temporary or persistent database ([see wiki](https://github.com/axllent/mailpit/wiki/Email-storage))
|
||||
- Fast SMTP processing & storing - approximately 70-100 emails per second depending on CPU, network speed & email size, easily handling tens of thousands of emails
|
||||
- SMTP relaying / message release - relay messages via a different SMTP server ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-relay))
|
||||
- SMTP relaying / message release - relay messages via a different SMTP server including an optional allowlist of accepted recipients ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-relay))
|
||||
- Optional SMTP with STARTTLS & SMTP authentication, including an "accept anything" mode ([see wiki](https://github.com/axllent/mailpit/wiki/SMTP-with-STARTTLS-and-authentication))
|
||||
- Optional HTTPS for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/HTTPS))
|
||||
- Optional basic authentication for web UI ([see wiki](https://github.com/axllent/mailpit/wiki/Basic-authentication))
|
||||
|
@ -27,7 +27,7 @@ func init() {
|
||||
|
||||
// these are simply repeated for cli consistency
|
||||
sendmailCmd.Flags().StringVarP(&fromAddr, "from", "f", fromAddr, "SMTP sender")
|
||||
sendmailCmd.Flags().StringVar(&smtpAddr, "smtp-addr", smtpAddr, "SMTP server address")
|
||||
sendmailCmd.Flags().StringVarP(&smtpAddr, "smtp-addr", "S", smtpAddr, "SMTP server address")
|
||||
sendmailCmd.Flags().BoolVarP(&sendmail.Verbose, "verbose", "v", false, "Verbose mode (sends debug output to stderr)")
|
||||
sendmailCmd.Flags().BoolP("long-b", "b", false, "Ignored. This flag exists for sendmail compatibility.")
|
||||
sendmailCmd.Flags().BoolP("long-i", "i", false, "Ignored. This flag exists for sendmail compatibility.")
|
||||
|
@ -111,15 +111,17 @@ 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, 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"` // allows overriding the boune address
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
STARTTLS bool `yaml:"starttls"`
|
||||
AllowInsecure bool `yaml:"allow-insecure"`
|
||||
Auth string `yaml:"auth"` // none, plain, 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"` // allows overriding the boune address
|
||||
RecipientAllowlist string `yaml:"recipient-allowlist"` // regex, if set needs to match for mails to be relayed
|
||||
RecipientAllowlistRegexp *regexp.Regexp
|
||||
}
|
||||
|
||||
// VerifyConfig wil do some basic checking
|
||||
@ -296,6 +298,18 @@ 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)
|
||||
|
||||
if SMTPRelayConfig.RecipientAllowlist != "" {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile recipient allowlist regexp: %e", err)
|
||||
}
|
||||
|
||||
SMTPRelayConfig.RecipientAllowlistRegexp = allowlistRegexp
|
||||
logger.Log().Infof("[smtp] recipient allowlist is active with the following regexp: %s", SMTPRelayConfig.RecipientAllowlist)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ func Run() {
|
||||
|
||||
// override defaults from cli flags
|
||||
flag.StringVarP(&fromAddr, "from", "f", fromAddr, "SMTP sender address")
|
||||
flag.StringVar(&smtpAddr, "smtp-addr", smtpAddr, "SMTP server address")
|
||||
flag.StringVarP(&smtpAddr, "smtp-addr", "S", smtpAddr, "SMTP server address")
|
||||
flag.BoolVarP(&Verbose, "verbose", "v", false, "Verbose mode (sends debug output to stderr)")
|
||||
flag.BoolP("long-b", "b", false, "Ignored. This flag exists for sendmail compatibility.")
|
||||
flag.BoolP("long-i", "i", false, "Ignored. This flag exists for sendmail compatibility.")
|
||||
|
@ -554,10 +554,17 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
for _, to := range tos {
|
||||
if _, err := mail.ParseAddress(to); err != nil {
|
||||
address, err := mail.ParseAddress(to)
|
||||
|
||||
if err != nil {
|
||||
httpError(w, "Invalid email address: "+to)
|
||||
return
|
||||
}
|
||||
|
||||
if config.SMTPRelayConfig.RecipientAllowlistRegexp != nil && !config.SMTPRelayConfig.RecipientAllowlistRegexp.MatchString(address.Address) {
|
||||
httpError(w, "Mail address does not match allowlist: "+to)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(msg)
|
||||
|
@ -20,6 +20,8 @@ type webUIConfiguration struct {
|
||||
SMTPServer string
|
||||
// Enforced Return-Path (if set) for relay bounces
|
||||
ReturnPath string
|
||||
// Allowlist of accepted recipients
|
||||
RecipientAllowlist string
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +47,7 @@ func WebUIConfig(w http.ResponseWriter, r *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
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(conf)
|
||||
|
@ -2,14 +2,48 @@ package smtpd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
|
||||
"github.com/axllent/mailpit/config"
|
||||
"github.com/axllent/mailpit/utils/logger"
|
||||
)
|
||||
|
||||
func allowedRecipients(to []string) []string {
|
||||
if config.SMTPRelayConfig.RecipientAllowlistRegexp == nil {
|
||||
return to
|
||||
}
|
||||
|
||||
var ar []string
|
||||
|
||||
for _, recipient := range to {
|
||||
address, err := mail.ParseAddress(recipient)
|
||||
|
||||
if err != nil {
|
||||
logger.Log().Warnf("ignoring invalid email address: %s", recipient)
|
||||
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)
|
||||
} else {
|
||||
ar = append(ar, recipient)
|
||||
}
|
||||
}
|
||||
|
||||
return ar
|
||||
}
|
||||
|
||||
// Send will connect to a pre-configured SMTP server and send a message to one or more recipients.
|
||||
func Send(from string, to []string, msg []byte) error {
|
||||
recipients := allowedRecipients(to)
|
||||
|
||||
if len(recipients) == 0 {
|
||||
return errors.New("no valid recipients")
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port)
|
||||
|
||||
c, err := smtp.Dial(addr)
|
||||
@ -48,7 +82,7 @@ func Send(from string, to []string, msg []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addr := range to {
|
||||
for _, addr := range recipients {
|
||||
if err = c.Rcpt(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
|
||||
// if enabled, this will route the email 1:1 through to the preconfigured smtp server
|
||||
if config.SMTPRelayAllIncoming {
|
||||
if err := Send(from, to, data); err != nil {
|
||||
logger.Log().Errorf("[smtp] error relaying message: %s", err.Error())
|
||||
logger.Log().Warnf("[smtp] error relaying message: %s", err.Error())
|
||||
} else {
|
||||
logger.Log().Debugf("[smtp] relayed message from %s via %s:%d", from, config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port)
|
||||
}
|
||||
|
@ -83,6 +83,11 @@ export default {
|
||||
<div class="invalid-feedback">Invalid email address</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text text-center" v-if="relayConfig.MessageRelay.RecipientAllowlist != ''">
|
||||
Note: A recipient allowlist has been configured. Any mail address not matching it will be rejected.
|
||||
<br class="d-none d-md-inline">
|
||||
Configured allowlist: <b>{{ relayConfig.MessageRelay.RecipientAllowlist }}</b>
|
||||
</div>
|
||||
<div class="form-text text-center">
|
||||
Note: For testing purposes, a unique Message-Id will be generated on send.
|
||||
<br class="d-none d-md-inline">
|
||||
|
@ -470,7 +470,7 @@ func GetMessage(id string) (*Message, error) {
|
||||
messageID := strings.Trim(env.GetHeader("Message-ID"), "<>")
|
||||
|
||||
returnPath := strings.Trim(env.GetHeader("Return-Path"), "<>")
|
||||
if returnPath == "" {
|
||||
if returnPath == "" && from != nil {
|
||||
returnPath = from.Address
|
||||
}
|
||||
|
||||
@ -491,7 +491,7 @@ func GetMessage(id string) (*Message, error) {
|
||||
|
||||
logger.Log().Debugf("[db] %s does not contain a date header, using received datetime", id)
|
||||
|
||||
date = time.UnixMicro(created)
|
||||
date = time.UnixMilli(created)
|
||||
}); err != nil {
|
||||
logger.Log().Error(err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user