mirror of
https://github.com/axllent/mailpit.git
synced 2024-12-26 22:56:43 +02:00
Feature: Add option to only allow SMTP recipients matching a regular expression (disable open-relay behaviour #219)
This commit is contained in:
parent
aad15945b3
commit
cdab59b295
@ -103,6 +103,7 @@ func init() {
|
||||
rootCmd.Flags().BoolVar(&config.SMTPAuthAllowInsecure, "smtp-auth-allow-insecure", config.SMTPAuthAllowInsecure, "Enable insecure PLAIN & LOGIN 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")
|
||||
rootCmd.Flags().StringVar(&config.SMTPAllowedRecipients, "smtp-allowed-recipients", config.SMTPAllowedRecipients, "Only allow SMTP recipients matching a regular expression (default allow all)")
|
||||
|
||||
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!)")
|
||||
@ -170,6 +171,9 @@ func initConfigFromEnv() {
|
||||
if len(os.Getenv("MP_SMTP_MAX_RECIPIENTS")) > 0 {
|
||||
config.SMTPMaxRecipients, _ = strconv.Atoi(os.Getenv("MP_SMTP_MAX_RECIPIENTS"))
|
||||
}
|
||||
if len(os.Getenv("MP_SMTP_ALLOWED_RECIPIENTS")) > 0 {
|
||||
config.SMTPAllowedRecipients = os.Getenv("MP_SMTP_ALLOWED_RECIPIENTS")
|
||||
}
|
||||
|
||||
// Relay server config
|
||||
config.SMTPRelayConfigFile = os.Getenv("MP_SMTP_RELAY_CONFIG")
|
||||
|
@ -93,6 +93,12 @@ var (
|
||||
// @see https://github.com/axllent/mailpit/issues/87 & https://github.com/axllent/mailpit/issues/153
|
||||
SMTPStrictRFCHeaders bool
|
||||
|
||||
// SMTPAllowedRecipients if set, will only accept recipients matching this regular expression
|
||||
SMTPAllowedRecipients string
|
||||
|
||||
// SMTPAllowedRecipientsRegexp is the compiled version of SMTPAllowedRecipients
|
||||
SMTPAllowedRecipientsRegexp *regexp.Regexp
|
||||
|
||||
// ReleaseEnabled is whether message releases are enabled, requires a valid SMTPRelayConfigFile
|
||||
ReleaseEnabled = false
|
||||
|
||||
@ -262,6 +268,16 @@ func VerifyConfig() error {
|
||||
}
|
||||
}
|
||||
|
||||
if SMTPAllowedRecipients != "" {
|
||||
restrictRegexp, err := regexp.Compile(SMTPAllowedRecipients)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to compile smtp-allowed-recipients regexp: %s", err.Error())
|
||||
}
|
||||
|
||||
SMTPAllowedRecipientsRegexp = restrictRegexp
|
||||
logger.Log().Infof("[smtp] only allowing recipients matching the following regexp: %s", SMTPAllowedRecipients)
|
||||
}
|
||||
|
||||
if err := parseRelayConfig(SMTPRelayConfigFile); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -335,11 +351,11 @@ func parseRelayConfig(c string) error {
|
||||
|
||||
if SMTPRelayConfig.RecipientAllowlist != "" {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile recipient allowlist regexp: %e", err)
|
||||
return fmt.Errorf("Failed to compile relay recipient allowlist regexp: %s", err.Error())
|
||||
}
|
||||
|
||||
SMTPRelayConfig.RecipientAllowlistRegexp = allowlistRegexp
|
||||
logger.Log().Infof("[smtp] recipient allowlist is active with the following regexp: %s", SMTPRelayConfig.RecipientAllowlist)
|
||||
logger.Log().Infof("[smtp] relay recipient allowlist is active with the following regexp: %s", SMTPRelayConfig.RecipientAllowlist)
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,9 @@ var (
|
||||
|
||||
mu sync.RWMutex
|
||||
|
||||
smtpReceived int
|
||||
smtpReceivedSize int
|
||||
smtpErrors int
|
||||
smtpAccepted int
|
||||
smtpAcceptedSize int
|
||||
smtpRejected int
|
||||
smtpIgnored int
|
||||
)
|
||||
|
||||
@ -52,13 +52,13 @@ type AppInformation struct {
|
||||
Memory uint64
|
||||
// Messages deleted
|
||||
MessagesDeleted int
|
||||
// SMTP messages received via since run
|
||||
SMTPReceived int
|
||||
// Total size in bytes of received messages since run
|
||||
SMTPReceivedSize int
|
||||
// SMTP errors since run
|
||||
SMTPErrors int
|
||||
// SMTP messages ignored since run (duplicate IDs)
|
||||
// SMTP accepted messages since run
|
||||
SMTPAccepted int
|
||||
// Total size in bytes of accepted messages since run
|
||||
SMTPAcceptedSize int
|
||||
// SMTP rejected messages since run
|
||||
SMTPRejected int
|
||||
// SMTP ignored messages since run (duplicate IDs)
|
||||
SMTPIgnored int
|
||||
}
|
||||
}
|
||||
@ -75,9 +75,9 @@ func Load() AppInformation {
|
||||
|
||||
info.RuntimeStats.Uptime = int(time.Since(startedAt).Seconds())
|
||||
info.RuntimeStats.MessagesDeleted = storage.StatsDeleted
|
||||
info.RuntimeStats.SMTPReceived = smtpReceived
|
||||
info.RuntimeStats.SMTPReceivedSize = smtpReceivedSize
|
||||
info.RuntimeStats.SMTPErrors = smtpErrors
|
||||
info.RuntimeStats.SMTPAccepted = smtpAccepted
|
||||
info.RuntimeStats.SMTPAcceptedSize = smtpAcceptedSize
|
||||
info.RuntimeStats.SMTPRejected = smtpRejected
|
||||
info.RuntimeStats.SMTPIgnored = smtpIgnored
|
||||
|
||||
if latestVersionCache != "" {
|
||||
@ -116,18 +116,18 @@ func Track() {
|
||||
startedAt = time.Now()
|
||||
}
|
||||
|
||||
// LogSMTPReceived logs a successfully SMTP transaction
|
||||
func LogSMTPReceived(size int) {
|
||||
// LogSMTPAccepted logs a successful SMTP transaction
|
||||
func LogSMTPAccepted(size int) {
|
||||
mu.Lock()
|
||||
smtpReceived = smtpReceived + 1
|
||||
smtpReceivedSize = smtpReceivedSize + size
|
||||
smtpAccepted = smtpAccepted + 1
|
||||
smtpAcceptedSize = smtpAcceptedSize + size
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
// LogSMTPError logs a failed SMTP transaction
|
||||
func LogSMTPError() {
|
||||
// LogSMTPRejected logs a rejected SMTP transaction
|
||||
func LogSMTPRejected() {
|
||||
mu.Lock()
|
||||
smtpErrors = smtpErrors + 1
|
||||
smtpRejected = smtpRejected + 1
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
|
||||
msg, err := mail.ReadMessage(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
logger.Log().Errorf("[smtpd] error parsing message: %s", err.Error())
|
||||
stats.LogSMTPError()
|
||||
stats.LogSMTPRejected()
|
||||
return err
|
||||
}
|
||||
|
||||
@ -121,11 +121,10 @@ func mailHandler(origin net.Addr, from string, to []string, data []byte) error {
|
||||
_, err = storage.Store(&data)
|
||||
if err != nil {
|
||||
logger.Log().Errorf("[db] error storing message: %s", err.Error())
|
||||
stats.LogSMTPError()
|
||||
return err
|
||||
}
|
||||
|
||||
stats.LogSMTPReceived(len(data))
|
||||
stats.LogSMTPAccepted(len(data))
|
||||
|
||||
data = nil // avoid memory leaks
|
||||
|
||||
@ -153,6 +152,22 @@ func authHandlerAny(remoteAddr net.Addr, mechanism string, username []byte, _ []
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// HandlerRcpt used to optionally restrict recipients based on `--smtp-allowed-recipients`
|
||||
func handlerRcpt(remoteAddr net.Addr, from string, to string) bool {
|
||||
if config.SMTPAllowedRecipientsRegexp == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
result := config.SMTPAllowedRecipientsRegexp.MatchString(to)
|
||||
|
||||
if !result {
|
||||
logger.Log().Warnf("[smtpd] rejected message to %s from %s (%s)", to, from, cleanIP(remoteAddr))
|
||||
stats.LogSMTPRejected()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Listen starts the SMTPD server
|
||||
func Listen() error {
|
||||
if config.SMTPAuthAllowInsecure {
|
||||
@ -178,6 +193,7 @@ func listenAndServe(addr string, handler smtpd.Handler, authHandler smtpd.AuthHa
|
||||
srv := &smtpd.Server{
|
||||
Addr: addr,
|
||||
Handler: handler,
|
||||
HandlerRcpt: handlerRcpt,
|
||||
Appname: "Mailpit",
|
||||
Hostname: "",
|
||||
AuthHandler: nil,
|
||||
|
@ -240,19 +240,23 @@ export default {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
SMTP messages received
|
||||
SMTP messages accepted
|
||||
</td>
|
||||
<td>
|
||||
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPReceived) }}
|
||||
({{ getFileSize(mailbox.appInfo.RuntimeStats.SMTPReceivedSize) }})
|
||||
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPAccepted) }}
|
||||
<small class="text-secondary">
|
||||
({{
|
||||
getFileSize(mailbox.appInfo.RuntimeStats.SMTPAcceptedSize)
|
||||
}})
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
SMTP errors
|
||||
SMTP messages rejected
|
||||
</td>
|
||||
<td>
|
||||
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPErrors) }}
|
||||
{{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPRejected) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -762,23 +762,23 @@
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"SMTPErrors": {
|
||||
"description": "SMTP errors since run",
|
||||
"SMTPAccepted": {
|
||||
"description": "SMTP accepted messages since run",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"SMTPAcceptedSize": {
|
||||
"description": "Total size in bytes of accepted messages since run",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"SMTPIgnored": {
|
||||
"description": "SMTP messages ignored since run (duplicate IDs)",
|
||||
"description": "SMTP ignored messages since run (duplicate IDs)",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"SMTPReceived": {
|
||||
"description": "SMTP messages received via since run",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"SMTPReceivedSize": {
|
||||
"description": "Total size in bytes of received messages since run",
|
||||
"SMTPRejected": {
|
||||
"description": "SMTP rejected messages since run",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user