diff --git a/client.go b/client.go index 5579a1c..e9f3fac 100644 --- a/client.go +++ b/client.go @@ -27,11 +27,18 @@ func sendMail(e *mail.Envelope, config *relayConfig) error { msg.WriteString("\r\n") Logger.Infof("starting email send -- from:%s, starttls:%t", e.MailFrom.String(), config.STARTTLS) + Logger.Infof("Client Remote IP: %s", e.RemoteIP) + var err error var conn net.Conn var client *smtp.Client var writer io.WriteCloser + if AllowedSendersFilter.Blocked(e.RemoteIP) { + Logger.Info("Remote IP of " + e.RemoteIP + " not allowed to send email.") + return errors.Wrap(err, "Remote IP of "+e.RemoteIP+" not allowed to send email.") + } + tlsconfig := &tls.Config{ InsecureSkipVerify: config.SkipVerify, //nolint:gosec ServerName: config.Server, diff --git a/go.mod b/go.mod index c03ae7e..0fd9d35 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/asaskevich/EventBus v0.0.0-20180103000110-68a521d7cbbb // indirect github.com/flashmob/go-guerrilla v1.6.1 github.com/go-sql-driver/mysql v1.5.0 // indirect + github.com/jpillora/ipfilter v1.2.2 github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 153533d..2940bcc 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/flashmob/go-guerrilla v1.6.1 h1:MLkqzRFUJveVAWuQ3s2MNPTAWbvXLt8EFsBor github.com/flashmob/go-guerrilla v1.6.1/go.mod h1:ZT9TRggRsSY4ZVndoyx8TRUxi3tM/nOYtKWKDX94H0I= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/jpillora/ipfilter v1.2.2 h1:lfENG7V1/T+ZutAtSbt6gssvzj3Ql0JmcFlqS/BES2E= +github.com/jpillora/ipfilter v1.2.2/go.mod h1:xvAYjA+48eM9E5+sg9yI55N5lE9sefckjsnDvSiEA+g= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -17,6 +19,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/phuslu/iploc v1.0.20200807 h1:LIBm2Y9l5zmUvnJhQgMcLZ0iVwuG+5/L6AgbMwSOpE4= +github.com/phuslu/iploc v1.0.20200807/go.mod h1:Q/0VX0txvbxekt4NhWIi3Q3eyZ139lHhnlzvDxyXhuc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -25,8 +29,11 @@ github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/main.go b/main.go index d2939a6..41bb96d 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "encoding/json" "flag" "fmt" @@ -10,6 +11,7 @@ import ( "syscall" log "github.com/flashmob/go-guerrilla/log" + "github.com/jpillora/ipfilter" ) const ( @@ -23,6 +25,9 @@ const ( // Logger is the global logger var Logger log.Logger +// Global List of Allowed Sender IPs: +var AllowedSendersFilter = ipfilter.New(ipfilter.Options{}) + type mailRelayConfig struct { SMTPServer string `json:"smtp_server"` SMTPPort int `json:"smtp_port"` @@ -35,6 +40,7 @@ type mailRelayConfig struct { LocalListenIP string `json:"local_listen_ip"` LocalListenPort int `json:"local_listen_port"` AllowedHosts []string `json:"allowed_hosts"` + AllowedSenders string `json:"allowed_senders"` TimeoutSecs int `json:"timeout_secs"` } @@ -52,12 +58,16 @@ func run() error { var test bool var testsender string var testrcpt string + var checkIP bool + var ipToCheck string var verbose bool flag.StringVar(&configFile, "config", "/etc/mailrelay.json", "specifies JSON config file") flag.BoolVar(&test, "test", false, "sends a test message to SMTP server") flag.StringVar(&testsender, "sender", "", "used with 'test' to specify sender email address") flag.StringVar(&testrcpt, "rcpt", "", "used with 'test' to specify recipient email address") flag.BoolVar(&verbose, "verbose", false, "verbose output") + flag.BoolVar(&checkIP, "checkIP", false, "Checks a provided IP address to see if it would be allowed") + flag.StringVar(&ipToCheck, "ip", "", "used with 'checkIP' to specify IP address to test") flag.Parse() appConfig, err := loadConfig(configFile) @@ -66,6 +76,34 @@ func run() error { return fmt.Errorf("loading config: %w", err) } + if appConfig.AllowedSenders != "*" { + file, err := os.Open(appConfig.AllowedSenders) + + if err != nil { + return fmt.Errorf("failed opening file: %s", err) + } + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var allowedIPsAndRanges []string + + for scanner.Scan() { + allowedIPsAndRanges = append(allowedIPsAndRanges, scanner.Text()) + } + + file.Close() + + for _, eachline := range allowedIPsAndRanges { + fmt.Println(eachline) + } + + AllowedSendersFilter = ipfilter.New(ipfilter.Options{ + //AllowedIPs: []string{"192.168.0.0/24"}, + AllowedIPs: allowedIPsAndRanges, + BlockByDefault: true, + }) + } + err = Start(appConfig, verbose) if err != nil { flag.Usage() @@ -89,6 +127,11 @@ func run() error { return nil } + if checkIP { + Logger.Infof("Checking to see if %s is allowed to send email: %t", ipToCheck, AllowedSendersFilter.Allowed(ipToCheck)) + return nil + } + // Wait for SIGINT c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) @@ -124,6 +167,7 @@ func configDefaults(config *mailRelayConfig) { config.LocalListenIP = DefaultLocalListenIP config.LocalListenPort = DefaultLocalListenPort config.AllowedHosts = []string{"*"} + config.AllowedSenders = "*" config.TimeoutSecs = DefaultTimeoutSecs }