diff --git a/CHANGELOG.md b/CHANGELOG.md index 127330b..e793ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - RedHat `.rpm` package generation to release process. - Message seen flag in REST and Web UI so you can see which messages have already been read. +- Recipient domain accept policy; Inbucket can now reject mail to specific + domains. ### Changed - Massive refactor of back-end code. Inbucket should now be both easier and @@ -34,9 +36,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). flag; which is enabled by default for the Docker container. - SMTP and POP3 network tracing is no longer logged regardless of level, but can be sent to stdout via `-netdebug` flag. +- Replaced store/nostore config variables with a storage policy that mirrors the + domain accept policy. ### Removed -- Support for SIGHUP and log file rotation. +- No longer support SIGHUP or log file rotation. ## [v1.3.1] - 2018-03-10 diff --git a/doc/config.md b/doc/config.md index fcb9d5e..cab55fc 100644 --- a/doc/config.md +++ b/doc/config.md @@ -11,13 +11,14 @@ variables it supports: INBUCKET_LOGLEVEL INFO DEBUG, INFO, WARN, or ERROR INBUCKET_SMTP_ADDR 0.0.0.0:2500 SMTP server IP4 host:port INBUCKET_SMTP_DOMAIN inbucket HELO domain - INBUCKET_SMTP_DOMAINNOSTORE Load testing domain INBUCKET_SMTP_MAXRECIPIENTS 200 Maximum RCPT TO per message INBUCKET_SMTP_MAXMESSAGEBYTES 10240000 Maximum message size - INBUCKET_SMTP_STOREMESSAGES true Store incoming mail? INBUCKET_SMTP_DEFAULTACCEPT true Accept all mail by default? INBUCKET_SMTP_ACCEPTDOMAINS Domains to accept mail for INBUCKET_SMTP_REJECTDOMAINS Domains to reject mail for + INBUCKET_SMTP_DEFAULTSTORE true Store all mail by default? + INBUCKET_SMTP_STOREDOMAINS Domains to store mail for + INBUCKET_SMTP_DISCARDDOMAINS Domains to discard mail for INBUCKET_SMTP_TIMEOUT 300s Idle network timeout INBUCKET_POP3_ADDR 0.0.0.0:1100 POP3 server IP4 host:port INBUCKET_POP3_DOMAIN inbucket HELLO domain @@ -78,17 +79,6 @@ Most SMTP clients appear to ignore this value. - Default: `inbucket` -### Load Testing/No Store Domain - -`INBUCKET_SMTP_DOMAINNOSTORE` - -Mail sent to this domain will not be stored by Inbucket. This is helpful if you -are load or soak testing a service, and do not plan to inspect the resulting -emails. Messages sent to a domain other than this will be stored normally. - -- Default: None -- Example: `bitbucket.local` - ### Maximum Recipients `INBUCKET_SMTP_MAXRECIPIENTS` @@ -108,17 +98,6 @@ exceeding this size will be rejected during the SMTP `DATA` phase. - Default: `10240000` (10MB) -### Store Messages - -`INBUCKET_SMTP_STOREMESSAGES` - -This option can be used to disable mail storage entirely. Useful for load -testing, or turning Inbucket into a black hole that will consume our entire -solar system. - -- Default: `true` -- Values: `true` or `false` - ### Default Recipient Accept Policy `INBUCKET_SMTP_DEFAULTACCEPT` @@ -152,6 +131,41 @@ has no effect when false. - Values: Comma separated list of domains - Example: `reject.com,gmail.com` +### Default Recipient Store Policy + +`INBUCKET_SMTP_DEFAULTSTORE` + +If true, Inbucket will store mail sent to any domain unless present in the +discard domains list. If false, messages will be discarded unless their domain +is present in the store domains list. + +- Default: `true` +- Values: `true` or `false` + +### Stored Recipient Domain List + +`INBUCKET_SMTP_STOREDOMAINS` + +List of domains to store mail for when `INBUCKET_SMTP_DEFAULTSTORE` is false; +has no effect when true. + +- Default: None +- Values: Comma separated list of domains +- Example: `localhost,mysite.org` + +### Discarded Recipient Domain List + +`INBUCKET_SMTP_DISCARDDOMAINS` + +Mail sent to these domains will not be stored by Inbucket. This is helpful if +you are load or soak testing a service, and do not plan to inspect the resulting +emails. Messages sent to a domain other than this will be stored normally. +Only has an effect when `INBUCKET_SMTP_DEFAULTSTORE` is true. + +- Default: None +- Values: Comma separated list of domains +- Example: `recycle.com,loadtest.org` + ### Network Idle Timeout `INBUCKET_SMTP_TIMEOUT` diff --git a/pkg/config/config.go b/pkg/config/config.go index 0fc23b8..1bb6057 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,7 +3,6 @@ package config import ( "log" "os" - "strings" "text/tabwriter" "time" @@ -42,13 +41,14 @@ type Root struct { type SMTP struct { Addr string `required:"true" default:"0.0.0.0:2500" desc:"SMTP server IP4 host:port"` Domain string `required:"true" default:"inbucket" desc:"HELO domain"` - DomainNoStore string `desc:"Load testing domain"` MaxRecipients int `required:"true" default:"200" desc:"Maximum RCPT TO per message"` MaxMessageBytes int `required:"true" default:"10240000" desc:"Maximum message size"` - StoreMessages bool `required:"true" default:"true" desc:"Store incoming mail?"` DefaultAccept bool `required:"true" default:"true" desc:"Accept all mail by default?"` AcceptDomains []string `desc:"Domains to accept mail for"` RejectDomains []string `desc:"Domains to reject mail for"` + DefaultStore bool `required:"true" default:"true" desc:"Store all mail by default?"` + StoreDomains []string `desc:"Domains to store mail for"` + DiscardDomains []string `desc:"Domains to discard mail for"` Timeout time.Duration `required:"true" default:"300s" desc:"Idle network timeout"` Debug bool `ignored:"true"` } @@ -86,9 +86,10 @@ type Storage struct { func Process() (*Root, error) { c := &Root{} err := envconfig.Process(prefix, c) - c.SMTP.DomainNoStore = strings.ToLower(c.SMTP.DomainNoStore) stringutil.SliceToLower(c.SMTP.AcceptDomains) stringutil.SliceToLower(c.SMTP.RejectDomains) + stringutil.SliceToLower(c.SMTP.StoreDomains) + stringutil.SliceToLower(c.SMTP.DiscardDomains) return c, err } diff --git a/pkg/policy/address.go b/pkg/policy/address.go index 6941eb3..f8cded9 100644 --- a/pkg/policy/address.go +++ b/pkg/policy/address.go @@ -50,10 +50,14 @@ func (a *Addressing) ShouldAcceptDomain(domain string) bool { return false } -// ShouldStoreDomain indicates if Inbucket stores email destined for the specified domain. +// ShouldStoreDomain indicates if Inbucket stores mail destined for the specified domain. func (a *Addressing) ShouldStoreDomain(domain string) bool { - if a.Config.StoreMessages { - return strings.ToLower(domain) != strings.ToLower(a.Config.DomainNoStore) + domain = strings.ToLower(domain) + if a.Config.DefaultStore && !stringutil.SliceContains(a.Config.DiscardDomains, domain) { + return true + } + if !a.Config.DefaultStore && stringutil.SliceContains(a.Config.StoreDomains, domain) { + return true } return false } diff --git a/pkg/policy/address_test.go b/pkg/policy/address_test.go index 19bfaf7..4410f11 100644 --- a/pkg/policy/address_test.go +++ b/pkg/policy/address_test.go @@ -65,24 +65,24 @@ func TestShouldStoreDomain(t *testing.T) { // Test with storage enabled. ap := &policy.Addressing{ Config: config.SMTP{ - DomainNoStore: "Foo.Com", - StoreMessages: true, + DefaultStore: false, + StoreDomains: []string{"store.com", "a.store.com"}, }, } testCases := []struct { domain string want bool }{ - {domain: "bar.com", want: true}, {domain: "foo.com", want: false}, - {domain: "FOO.com", want: false}, - {domain: "bar.foo.com", want: true}, + {domain: "STORE.com", want: true}, + {domain: "a.store.com", want: true}, + {domain: "b.store.com", want: false}, } for _, tc := range testCases { t.Run(tc.domain, func(t *testing.T) { got := ap.ShouldStoreDomain(tc.domain) if got != tc.want { - t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want) + t.Errorf("Got store %v for %q, want: %v", got, tc.domain, tc.want) } }) @@ -90,23 +90,24 @@ func TestShouldStoreDomain(t *testing.T) { // Test with storage disabled. ap = &policy.Addressing{ Config: config.SMTP{ - StoreMessages: false, + DefaultStore: true, + DiscardDomains: []string{"discard.com", "a.discard.com"}, }, } testCases = []struct { domain string want bool }{ - {domain: "bar.com", want: false}, - {domain: "foo.com", want: false}, - {domain: "FOO.com", want: false}, - {domain: "bar.foo.com", want: false}, + {domain: "foo.com", want: true}, + {domain: "DISCARD.com", want: false}, + {domain: "a.discard.com", want: false}, + {domain: "b.discard.com", want: true}, } for _, tc := range testCases { t.Run(tc.domain, func(t *testing.T) { got := ap.ShouldStoreDomain(tc.domain) if got != tc.want { - t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want) + t.Errorf("Got store %v for %q, want: %v", got, tc.domain, tc.want) } }) diff --git a/pkg/server/smtp/handler_test.go b/pkg/server/smtp/handler_test.go index e648868..e4d441a 100644 --- a/pkg/server/smtp/handler_test.go +++ b/pkg/server/smtp/handler_test.go @@ -365,10 +365,8 @@ func setupSMTPServer(ds storage.Store) (s *Server, buf *bytes.Buffer, teardown f cfg := config.SMTP{ Addr: "127.0.0.1:2500", Domain: "inbucket.local", - DomainNoStore: "bitbucket.local", MaxRecipients: 5, MaxMessageBytes: 5000, - StoreMessages: true, DefaultAccept: true, RejectDomains: []string{"deny.com"}, Timeout: 5, diff --git a/pkg/server/smtp/listener.go b/pkg/server/smtp/listener.go index 3741eef..9161d75 100644 --- a/pkg/server/smtp/listener.go +++ b/pkg/server/smtp/listener.go @@ -97,11 +97,6 @@ func (s *Server) Start(ctx context.Context) { s.emergencyShutdown() return } - if !s.config.StoreMessages { - slog.Info().Msg("Load test mode active, messages will not be stored") - } else if s.config.DomainNoStore != "" { - slog.Info().Msgf("Messages sent to domain '%v' will be discarded", s.config.DomainNoStore) - } // Listener go routine. go s.serve(ctx) // Wait for shutdown. diff --git a/pkg/server/web/helpers.go b/pkg/server/web/helpers.go index 60d2edc..ea32cb2 100644 --- a/pkg/server/web/helpers.go +++ b/pkg/server/web/helpers.go @@ -15,6 +15,7 @@ import ( var TemplateFuncs = template.FuncMap{ "friendlyTime": FriendlyTime, "reverse": Reverse, + "stringsJoin": strings.Join, "textToHtml": TextToHTML, } diff --git a/ui/templates/root/status.html b/ui/templates/root/status.html index f666ee7..3f1d976 100644 --- a/ui/templates/root/status.html +++ b/ui/templates/root/status.html @@ -52,10 +52,30 @@ $(document).ready(
{{.webListener}}
-
No-Store Domain:
+
Accept Policy:
{{with .smtpConfig}} - {{or .DomainNoStore .DomainNoStore "Not Configured"}} + + {{if .DefaultAccept}} + All domains{{with .RejectDomains}}, except: {{stringsJoin . ", "}}{{end}} + {{else}} + No domains{{with .AcceptDomains}}, except: {{stringsJoin . ", "}}{{end}} + {{end}} + + {{end}} +
+
+
+
Store Policy:
+
+ {{with .smtpConfig}} + + {{if .DefaultStore}} + All domains{{with .DiscardDomains}}, except: {{stringsJoin . ", "}}{{end}} + {{else}} + No domains{{with .StoreDomains}}, except: {{stringsJoin . ", "}}{{end}} + {{end}} + {{end}}