forked from drew/smtprelay
Replace iniflags config file parser with peterbourgon/ff/v3
This allows us to support configuration via environment variables which is useful in container environments.
This commit is contained in:
108
config.go
108
config.go
@@ -1,15 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/peterbourgon/ff/v3"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vharitonsky/iniflags"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -18,36 +21,43 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logFile = flag.String("logfile", "", "Path to logfile")
|
flagset = flag.NewFlagSet("smtprelay", flag.ContinueOnError)
|
||||||
logFormat = flag.String("log_format", "default", "Log output format")
|
|
||||||
logLevel = flag.String("log_level", "info", "Minimum log level to output")
|
// config flags
|
||||||
hostName = flag.String("hostname", "localhost.localdomain", "Server hostname")
|
logFile = flagset.String("logfile", "", "Path to logfile")
|
||||||
welcomeMsg = flag.String("welcome_msg", "", "Welcome message for SMTP session")
|
logFormat = flagset.String("log_format", "default", "Log output format")
|
||||||
listenStr = flag.String("listen", "127.0.0.1:25 [::1]:25", "Address and port to listen for incoming SMTP")
|
logLevel = flagset.String("log_level", "info", "Minimum log level to output")
|
||||||
|
hostName = flagset.String("hostname", "localhost.localdomain", "Server hostname")
|
||||||
|
welcomeMsg = flagset.String("welcome_msg", "", "Welcome message for SMTP session")
|
||||||
|
listenStr = flagset.String("listen", "127.0.0.1:25 [::1]:25", "Address and port to listen for incoming SMTP")
|
||||||
|
localCert = flagset.String("local_cert", "", "SSL certificate for STARTTLS/TLS")
|
||||||
|
localKey = flagset.String("local_key", "", "SSL private key for STARTTLS/TLS")
|
||||||
|
localForceTLS = flagset.Bool("local_forcetls", false, "Force STARTTLS (needs local_cert and local_key)")
|
||||||
|
readTimeoutStr = flagset.String("read_timeout", "60s", "Socket timeout for read operations")
|
||||||
|
writeTimeoutStr = flagset.String("write_timeout", "60s", "Socket timeout for write operations")
|
||||||
|
dataTimeoutStr = flagset.String("data_timeout", "5m", "Socket timeout for DATA command")
|
||||||
|
maxConnections = flagset.Int("max_connections", 100, "Max concurrent connections, use -1 to disable")
|
||||||
|
maxMessageSize = flagset.Int("max_message_size", 10240000, "Max message size in bytes")
|
||||||
|
maxRecipients = flagset.Int("max_recipients", 100, "Max RCPT TO calls for each envelope")
|
||||||
|
allowedNetsStr = flagset.String("allowed_nets", "127.0.0.0/8 ::1/128", "Networks allowed to send mails")
|
||||||
|
allowedSenderStr = flagset.String("allowed_sender", "", "Regular expression for valid FROM EMail addresses")
|
||||||
|
allowedRecipStr = flagset.String("allowed_recipients", "", "Regular expression for valid TO EMail addresses")
|
||||||
|
allowedUsers = flagset.String("allowed_users", "", "Path to file with valid users/passwords")
|
||||||
|
command = flagset.String("command", "", "Path to pipe command")
|
||||||
|
remotesStr = flagset.String("remotes", "", "Outgoing SMTP servers")
|
||||||
|
|
||||||
|
// additional flags
|
||||||
|
versionInfo = flagset.Bool("version", false, "Show version information")
|
||||||
|
|
||||||
|
// internal
|
||||||
listenAddrs = []protoAddr{}
|
listenAddrs = []protoAddr{}
|
||||||
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)")
|
|
||||||
readTimeoutStr = flag.String("read_timeout", "60s", "Socket timeout for read operations")
|
|
||||||
readTimeout time.Duration
|
readTimeout time.Duration
|
||||||
writeTimeoutStr = flag.String("write_timeout", "60s", "Socket timeout for write operations")
|
|
||||||
writeTimeout time.Duration
|
writeTimeout time.Duration
|
||||||
dataTimeoutStr = flag.String("data_timeout", "5m", "Socket timeout for DATA command")
|
|
||||||
dataTimeout time.Duration
|
dataTimeout time.Duration
|
||||||
maxConnections = flag.Int("max_connections", 100, "Max concurrent connections, use -1 to disable")
|
|
||||||
maxMessageSize = flag.Int("max_message_size", 10240000, "Max message size in bytes")
|
|
||||||
maxRecipients = flag.Int("max_recipients", 100, "Max RCPT TO calls for each envelope")
|
|
||||||
allowedNetsStr = flag.String("allowed_nets", "127.0.0.0/8 ::1/128", "Networks allowed to send mails")
|
|
||||||
allowedNets = []*net.IPNet{}
|
allowedNets = []*net.IPNet{}
|
||||||
allowedSenderStr = flag.String("allowed_sender", "", "Regular expression for valid FROM EMail addresses")
|
|
||||||
allowedSender *regexp.Regexp
|
allowedSender *regexp.Regexp
|
||||||
allowedRecipStr = flag.String("allowed_recipients", "", "Regular expression for valid TO EMail addresses")
|
|
||||||
allowedRecipients *regexp.Regexp
|
allowedRecipients *regexp.Regexp
|
||||||
allowedUsers = flag.String("allowed_users", "", "Path to file with valid users/passwords")
|
|
||||||
command = flag.String("command", "", "Path to pipe command")
|
|
||||||
remotesStr = flag.String("remotes", "", "Outgoing SMTP servers")
|
|
||||||
remotes = []*Remote{}
|
remotes = []*Remote{}
|
||||||
versionInfo = flag.Bool("version", false, "Show version information")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func localAuthRequired() bool {
|
func localAuthRequired() bool {
|
||||||
@@ -183,7 +193,18 @@ func setupTimeouts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ConfigLoad() {
|
func ConfigLoad() {
|
||||||
iniflags.Parse()
|
// configuration parsing
|
||||||
|
err := ff.Parse(flagset, os.Args[1:],
|
||||||
|
ff.WithEnvVarPrefix("smtprelay"),
|
||||||
|
ff.WithConfigFileFlag("config"),
|
||||||
|
ff.WithConfigFileParser(IniParser),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
//log.WithField("config_error", err).
|
||||||
|
// Fatal("Config parsing error")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
// Set up logging as soon as possible
|
// Set up logging as soon as possible
|
||||||
setupLogger()
|
setupLogger()
|
||||||
@@ -198,3 +219,42 @@ func ConfigLoad() {
|
|||||||
setupListeners()
|
setupListeners()
|
||||||
setupTimeouts()
|
setupTimeouts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IniParser is a parser for config files in classic key/value style format. Each
|
||||||
|
// line is tokenized as a single key/value pair. The first "=" delimited
|
||||||
|
// token in the line is interpreted as the flag name, and all remaining tokens
|
||||||
|
// are interpreted as the value. Any leading hyphens on the flag name are
|
||||||
|
// ignored.
|
||||||
|
func IniParser(r io.Reader, set func(name, value string) error) error {
|
||||||
|
s := bufio.NewScanner(r)
|
||||||
|
for s.Scan() {
|
||||||
|
line := strings.TrimSpace(s.Text())
|
||||||
|
if line == "" {
|
||||||
|
continue // skip empties
|
||||||
|
}
|
||||||
|
|
||||||
|
if line[0] == '#' {
|
||||||
|
continue // skip comments
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
value string
|
||||||
|
index = strings.IndexRune(line, '=')
|
||||||
|
)
|
||||||
|
if index < 0 {
|
||||||
|
name, value = line, "true" // boolean option
|
||||||
|
} else {
|
||||||
|
name, value = line[:index], strings.TrimSpace(line[index:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := strings.Index(value, " #"); i >= 0 {
|
||||||
|
value = strings.TrimSpace(value[:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := set(name, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -3,9 +3,9 @@ module github.com/decke/smtprelay
|
|||||||
require (
|
require (
|
||||||
github.com/chrj/smtpd v0.3.1
|
github.com/chrj/smtpd v0.3.1
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
github.com/peterbourgon/ff/v3 v3.1.2
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de
|
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
7
go.sum
7
go.sum
@@ -1,3 +1,4 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/chrj/smtpd v0.3.1 h1:kogHFkbFdKaoH3bgZkqNC9uVtKYOFfM3uV3rroBdooE=
|
github.com/chrj/smtpd v0.3.1 h1:kogHFkbFdKaoH3bgZkqNC9uVtKYOFfM3uV3rroBdooE=
|
||||||
github.com/chrj/smtpd v0.3.1/go.mod h1:JtABvV/LzvLmEIzy0NyDnrfMGOMd8wy5frAokwf6J9Q=
|
github.com/chrj/smtpd v0.3.1/go.mod h1:JtABvV/LzvLmEIzy0NyDnrfMGOMd8wy5frAokwf6J9Q=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -5,6 +6,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||||
|
github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP6JM=
|
||||||
|
github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
@@ -13,8 +17,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de h1:fkw+7JkxF3U1GzQoX9h69Wvtvxajo5Rbzy6+YMMzPIg=
|
|
||||||
github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de/go.mod h1:irMhzlTz8+fVFj6CH2AN2i+WI5S6wWFtK3MBCIxIpyI=
|
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -22,5 +24,6 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16C
|
|||||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Reference in New Issue
Block a user