2
0
forked from drew/smtprelay

Code refactoring and rename package

This commit is contained in:
Bernhard Froehlich
2018-12-29 12:24:32 +00:00
parent ab850e8765
commit ab341c697d
5 changed files with 115 additions and 66 deletions

View File

@@ -25,5 +25,5 @@ produces mail.
* Authentication support with file (LOGIN, PLAIN)
* Enforce encryption for authentication
* Forwards all mail to a smarthost (GMail, MailGun or any other SMTP server)
* Small codebase (smtp-proxy ~300 LoC, chrj/smtpd ~1200 LoC)
* Small codebase
* IPv6 support

68
auth.go Normal file
View File

@@ -0,0 +1,68 @@
package smtpproxy
import (
"bufio"
"errors"
"os"
"strings"
"golang.org/x/crypto/bcrypt"
)
var (
filename string
)
func AuthLoadFile(file string) error {
f, err := os.Open(file)
if err != nil {
return err
}
f.Close()
filename = file
return nil
}
func AuthReady() bool {
return (filename == "")
}
func AuthFetch(username string) (string, string, error) {
if ! AuthReady() {
return "", "", errors.New("Authentication file not specified. Call LoadFile() first")
}
file, err := os.Open(filename)
if err != nil {
return "", "", err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) != 3 {
continue
}
if strings.ToLower(username) == strings.ToLower(parts[0]) {
return parts[1], parts[2], nil
}
}
return "", "", errors.New("User not found")
}
func AuthCheckPassword(username string, secret string) error {
hash, _, err := AuthFetch(username)
if err != nil {
return err
}
if bcrypt.CompareHashAndPassword([]byte(hash), []byte(secret)) == nil {
return nil
}
return errors.New("Password invalid")
}

33
config.go Normal file
View File

@@ -0,0 +1,33 @@
package smtpproxy
import (
"flag"
"github.com/vharitonsky/iniflags"
)
const (
VERSION = "1.1.1-dev"
)
var (
logFile = flag.String("logfile", "/var/log/smtpd-proxy.log", "Path to logfile")
hostName = flag.String("hostname", "localhost.localdomain", "Server hostname")
welcomeMsg = flag.String("welcome_msg", "", "Welcome message for SMTP session")
listen = flag.String("listen", "127.0.0.1:25 [::1]:25", "Address and port to listen for incoming SMTP")
localCert = flag.String("local_cert", "", "SSL certificate for STARTTLS/TLS")
localKey = flag.String("local_key", "", "SSL private key for STARTTLS/TLS")
localForceTLS = flag.Bool("local_forcetls", false, "Force STARTTLS (needs local_cert and local_key)")
allowedNets = flag.String("allowed_nets", "127.0.0.1/8 ::1/128", "Networks allowed to send mails")
allowedSender = flag.String("allowed_sender", "", "Regular expression for valid FROM EMail adresses")
allowedRecipients = flag.String("allowed_recipients", "", "Regular expression for valid TO EMail adresses")
allowedUsers = flag.String("allowed_users", "", "Path to file with valid users/passwords")
remoteHost = flag.String("remote_host", "smtp.gmail.com:587", "Outgoing SMTP server")
remoteUser = flag.String("remote_user", "", "Username for authentication on outgoing SMTP server")
remotePass = flag.String("remote_pass", "", "Password for authentication on outgoing SMTP server")
versionInfo= flag.Bool("version", false, "Show version information")
)
func ConfigLoad() {
iniflags.Parse()
}

72
main.go
View File

@@ -1,9 +1,7 @@
package main
package smtpproxy
import (
"bufio"
"crypto/tls"
"flag"
"fmt"
"io"
"log"
@@ -15,30 +13,6 @@ import (
"time"
"github.com/chrj/smtpd"
"github.com/vharitonsky/iniflags"
"golang.org/x/crypto/bcrypt"
)
const (
VERSION = "1.1.1-dev"
)
var (
logFile = flag.String("logfile", "/var/log/smtpd-proxy.log", "Path to logfile")
hostName = flag.String("hostname", "localhost.localdomain", "Server hostname")
welcomeMsg = flag.String("welcome_msg", "", "Welcome message for SMTP session")
listen = flag.String("listen", "127.0.0.1:25 [::1]:25", "Address and port to listen for incoming SMTP")
localCert = flag.String("local_cert", "", "SSL certificate for STARTTLS/TLS")
localKey = flag.String("local_key", "", "SSL private key for STARTTLS/TLS")
localForceTLS = flag.Bool("local_forcetls", false, "Force STARTTLS (needs local_cert and local_key)")
allowedNets = flag.String("allowed_nets", "127.0.0.1/8 ::1/128", "Networks allowed to send mails")
allowedSender = flag.String("allowed_sender", "", "Regular expression for valid FROM EMail adresses")
allowedRecipients = flag.String("allowed_recipients", "", "Regular expression for valid TO EMail adresses")
allowedUsers = flag.String("allowed_users", "", "Path to file with valid users/passwords")
remoteHost = flag.String("remote_host", "smtp.gmail.com:587", "Outgoing SMTP server")
remoteUser = flag.String("remote_user", "", "Username for authentication on outgoing SMTP server")
remotePass = flag.String("remote_pass", "", "Password for authentication on outgoing SMTP server")
versionInfo= flag.Bool("version", false, "Show version information")
)
func connectionChecker(peer smtpd.Peer) error {
@@ -65,28 +39,15 @@ func connectionChecker(peer smtpd.Peer) error {
func senderChecker(peer smtpd.Peer, addr string) error {
// check sender address from auth file if user is authenticated
if *allowedUsers != "" && peer.Username != "" {
file, err := os.Open(*allowedUsers)
_, email, err := AuthFetch(peer.Username)
if err != nil {
log.Printf("User file not found %v", err)
return smtpd.Error{Code: 451, Message: "Bad sender address"}
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) != 3 {
continue
}
if peer.Username == parts[0] {
if strings.ToLower(addr) != strings.ToLower(parts[2]) {
if strings.ToLower(addr) != strings.ToLower(email) {
return smtpd.Error{Code: 451, Message: "Bad sender address"}
}
}
}
}
if *allowedSender == "" {
return nil
@@ -124,29 +85,11 @@ func recipientChecker(peer smtpd.Peer, addr string) error {
}
func authChecker(peer smtpd.Peer, username string, password string) error {
file, err := os.Open(*allowedUsers)
err := AuthCheckPassword(username, password)
if err != nil {
log.Printf("User file not found %v", err)
return smtpd.Error{Code: 535, Message: "Authentication credentials invalid"}
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) != 2 {
continue
}
if username == parts[0] {
if bcrypt.CompareHashAndPassword([]byte(parts[1]), []byte(password)) == nil {
return nil
}
}
}
return smtpd.Error{Code: 535, Message: "Authentication credentials invalid"}
}
func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
@@ -192,7 +135,7 @@ func mailHandler(peer smtpd.Peer, env smtpd.Envelope) error {
func main() {
iniflags.Parse()
ConfigLoad()
if *versionInfo {
fmt.Printf("smtpd-proxy/%s\n", VERSION)
@@ -224,6 +167,11 @@ func main() {
}
if *allowedUsers != "" {
err := AuthLoadFile(*allowedUsers)
if err != nil {
log.Fatalf("Authentication file: %s\n", err)
}
server.Authenticator = authChecker
}

View File

@@ -13,7 +13,7 @@
// Some external packages provide more functionality. See:
//
// https://godoc.org/?q=smtp
package main
package smtpproxy
import (
"crypto/tls"