diff --git a/Dockerfile b/Dockerfile index b341f460..3bf630e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,14 +2,14 @@ # MailHog Dockerfile # -FROM golang:1.18-alpine as builder +FROM golang:1.13-alpine as builder # Install MailHog: RUN apk --no-cache add --virtual build-dependencies \ git \ && mkdir -p /root/gocode \ && export GOPATH=/root/gocode \ - && go install github.com/mailhog/MailHog@latest + && go get github.com/secforge/MailHog FROM alpine:3 # Add mailhog user/group with uid/gid 1000. diff --git a/MailHog b/MailHog new file mode 100755 index 00000000..8004cbaf Binary files /dev/null and b/MailHog differ diff --git a/README.md b/README.md index 33f43a0b..d94af55c 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ See [MailHog libraries](docs/LIBRARIES.md) for a list of MailHog client librarie * See [Introduction to Jim](/docs/JIM.md) for more information * HTTP API to list, retrieve and delete messages * See [APIv1](/docs/APIv1.md) and [APIv2](/docs/APIv2.md) documentation for more information +* Email blacklist functionality to reject messages from/to specific addresses via SMTP * [HTTP basic authentication](docs/Auth.md) for MailHog UI and API * Multipart MIME support * Download individual MIME parts diff --git a/docs/APIv2/swagger-2.0.yaml b/docs/APIv2/swagger-2.0.yaml index e70f5f82..161458c6 100644 --- a/docs/APIv2/swagger-2.0.yaml +++ b/docs/APIv2/swagger-2.0.yaml @@ -91,6 +91,60 @@ paths: created: type: string format: date-time + /api/v2/blacklist: + get: + description: | + Retrieve a list of blacklisted email addresses + responses: + 200: + description: Successful response + schema: + title: Blacklisted Emails + type: array + items: + type: string + description: Email address + /api/v2/blacklist/{email}: + post: + description: | + Add an email address to the blacklist + parameters: + - + name: email + in: path + description: Email address to blacklist + required: true + type: string + responses: + 200: + description: Email successfully added to blacklist + schema: + type: object + properties: + success: + type: boolean + 400: + description: Email address is required + delete: + description: | + Remove an email address from the blacklist + parameters: + - + name: email + in: path + description: Email address to remove from blacklist + required: true + type: string + responses: + 200: + description: Email successfully removed from blacklist + schema: + type: object + properties: + success: + type: boolean + 400: + description: Email address is required /api/v2/search: get: description: | @@ -190,3 +244,57 @@ paths: created: type: string format: date-time + /api/v2/blacklist: + get: + description: | + Retrieve a list of blacklisted email addresses + responses: + 200: + description: Successful response + schema: + title: Blacklisted Emails + type: array + items: + type: string + description: Email address + /api/v2/blacklist/{email}: + post: + description: | + Add an email address to the blacklist + parameters: + - + name: email + in: path + description: Email address to blacklist + required: true + type: string + responses: + 200: + description: Email successfully added to blacklist + schema: + type: object + properties: + success: + type: boolean + 400: + description: Email address is required + delete: + description: | + Remove an email address from the blacklist + parameters: + - + name: email + in: path + description: Email address to remove from blacklist + required: true + type: string + responses: + 200: + description: Email successfully removed from blacklist + schema: + type: object + properties: + success: + type: boolean + 400: + description: Email address is required diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 3b001b7d..77f412f5 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -1,6 +1,16 @@ MailHog Releases ================ +### [v1.0.1](https://github.com/secforge/MailHog/releases/v1.0.1) + +- Add email blacklist functionality +- Add APIv2 endpoints for blacklist management: + - `GET /api/v2/blacklist` - List blacklisted email addresses + - `POST /api/v2/blacklist/{email}` - Add email to blacklist + - `DELETE /api/v2/blacklist/{email}` - Remove email from blacklist +- Add SMTP rejection for blacklisted sender and recipient addresses +- Minimal changes to maintain upstream compatibility + ### [v1.0.0](https://github.com/mailhog/MailHog/releases/v1.0.0) There's still outstanding PRs and issues which haven't been addressed in this release. diff --git a/vendor/github.com/mailhog/MailHog-Server/api/v2.go b/vendor/github.com/mailhog/MailHog-Server/api/v2.go index 4ef0c405..1ea5e1d1 100644 --- a/vendor/github.com/mailhog/MailHog-Server/api/v2.go +++ b/vendor/github.com/mailhog/MailHog-Server/api/v2.go @@ -46,6 +46,13 @@ func createAPIv2(conf *config.Config, r *pat.Router) *APIv2 { r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("GET").HandlerFunc(apiv2.listOutgoingSMTP) r.Path(conf.WebPath + "/api/v2/outgoing-smtp").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions) + r.Path(conf.WebPath + "/api/v2/blacklist").Methods("GET").HandlerFunc(apiv2.blacklist_list) + r.Path(conf.WebPath + "/api/v2/blacklist").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions) + + r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("POST").HandlerFunc(apiv2.blacklist_add) + r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("DELETE").HandlerFunc(apiv2.blacklist_remove) + r.Path(conf.WebPath + "/api/v2/blacklist/{email}").Methods("OPTIONS").HandlerFunc(apiv2.defaultOptions) + r.Path(conf.WebPath + "/api/v2/websocket").Methods("GET").HandlerFunc(apiv2.websocket) go func() { @@ -256,3 +263,64 @@ func (apiv2 *APIv2) broadcast(msg *data.Message) { apiv2.wsHub.Broadcast(msg) } + +func (apiv2 *APIv2) blacklist_list(w http.ResponseWriter, req *http.Request) { + log.Println("[APIv2] GET /api/v2/blacklist") + + apiv2.defaultOptions(w, req) + + emails := make([]string, 0, len(apiv2.config.Blacklist)) + for email := range apiv2.config.Blacklist { + emails = append(emails, email) + } + + bytes, err := json.Marshal(emails) + if err != nil { + log.Printf("Error marshaling blacklist: %s", err) + w.WriteHeader(500) + return + } + + w.Header().Add("Content-Type", "application/json") + w.Write(bytes) +} + +func (apiv2 *APIv2) blacklist_add(w http.ResponseWriter, req *http.Request) { + email := req.URL.Query().Get(":email") + log.Printf("[APIv2] POST /api/v2/blacklist/%s", email) + + apiv2.defaultOptions(w, req) + + if email == "" { + w.WriteHeader(400) + w.Write([]byte("Email address is required")) + return + } + + apiv2.config.Blacklist[email] = true + log.Printf("Added %s to blacklist", email) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{"success": true}`)) +} + +func (apiv2 *APIv2) blacklist_remove(w http.ResponseWriter, req *http.Request) { + email := req.URL.Query().Get(":email") + log.Printf("[APIv2] DELETE /api/v2/blacklist/%s", email) + + apiv2.defaultOptions(w, req) + + if email == "" { + w.WriteHeader(400) + w.Write([]byte("Email address is required")) + return + } + + delete(apiv2.config.Blacklist, email) + log.Printf("Removed %s from blacklist", email) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{"success": true}`)) +} diff --git a/vendor/github.com/mailhog/MailHog-Server/config/config.go b/vendor/github.com/mailhog/MailHog-Server/config/config.go index 59cb7061..b329b2c9 100644 --- a/vendor/github.com/mailhog/MailHog-Server/config/config.go +++ b/vendor/github.com/mailhog/MailHog-Server/config/config.go @@ -27,6 +27,7 @@ func DefaultConfig() *Config { WebPath: "", MessageChan: make(chan *data.Message), OutgoingSMTP: make(map[string]*OutgoingSMTP), + Blacklist: make(map[string]bool), } } @@ -49,6 +50,7 @@ type Config struct { OutgoingSMTPFile string OutgoingSMTP map[string]*OutgoingSMTP WebPath string + Blacklist map[string]bool } // OutgoingSMTP is an outgoing SMTP server config diff --git a/vendor/github.com/mailhog/MailHog-Server/smtp/session.go b/vendor/github.com/mailhog/MailHog-Server/smtp/session.go index 10a220dc..c7f872f1 100644 --- a/vendor/github.com/mailhog/MailHog-Server/smtp/session.go +++ b/vendor/github.com/mailhog/MailHog-Server/smtp/session.go @@ -24,6 +24,7 @@ type Session struct { isTLS bool line string link *linkio.Link + blacklist map[string]bool reader io.Reader writer io.Writer @@ -31,7 +32,7 @@ type Session struct { } // Accept starts a new SMTP session using io.ReadWriteCloser -func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Storage, messageChan chan *data.Message, hostname string, monkey monkey.ChaosMonkey) { +func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Storage, messageChan chan *data.Message, hostname string, monkey monkey.ChaosMonkey, blacklist map[string]bool) { defer conn.Close() proto := smtp.NewProtocol() @@ -48,7 +49,7 @@ func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Stora } } - session := &Session{conn, proto, storage, messageChan, remoteAddress, false, "", link, reader, writer, monkey} + session := &Session{conn, proto, storage, messageChan, remoteAddress, false, "", link, blacklist, reader, writer, monkey} proto.LogHandler = session.logf proto.MessageReceivedHandler = session.acceptMessage proto.ValidateSenderHandler = session.validateSender @@ -79,6 +80,10 @@ func (c *Session) validateAuthentication(mechanism string, args ...string) (erro } func (c *Session) validateRecipient(to string) bool { + if c.blacklist != nil && c.blacklist[to] { + c.logf("Rejecting email to blacklisted address: %s", to) + return false + } if c.monkey != nil { ok := c.monkey.ValidRCPT(to) if !ok { @@ -89,6 +94,10 @@ func (c *Session) validateRecipient(to string) bool { } func (c *Session) validateSender(from string) bool { + if c.blacklist != nil && c.blacklist[from] { + c.logf("Rejecting email from blacklisted address: %s", from) + return false + } if c.monkey != nil { ok := c.monkey.ValidMAIL(from) if !ok { diff --git a/vendor/github.com/mailhog/MailHog-Server/smtp/smtp.go b/vendor/github.com/mailhog/MailHog-Server/smtp/smtp.go index 38a9b515..836ba4e6 100644 --- a/vendor/github.com/mailhog/MailHog-Server/smtp/smtp.go +++ b/vendor/github.com/mailhog/MailHog-Server/smtp/smtp.go @@ -38,6 +38,7 @@ func Listen(cfg *config.Config, exitCh chan int) *net.TCPListener { cfg.MessageChan, cfg.Hostname, cfg.Monkey, + cfg.Blacklist, ) } }