diff --git a/cmd/root.go b/cmd/root.go index 9958478..a8de3f1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -292,6 +292,7 @@ func initConfigFromEnv() { config.SMTPRelayConfig.Port, _ = strconv.Atoi(os.Getenv("MP_SMTP_RELAY_PORT")) } config.SMTPRelayConfig.STARTTLS = getEnabledFromEnv("MP_SMTP_RELAY_STARTTLS") + config.SMTPRelayConfig.TLS = getEnabledFromEnv("MP_SMTP_RELAY_TLS") config.SMTPRelayConfig.AllowInsecure = getEnabledFromEnv("MP_SMTP_RELAY_ALLOW_INSECURE") config.SMTPRelayConfig.Auth = os.Getenv("MP_SMTP_RELAY_AUTH") config.SMTPRelayConfig.Username = os.Getenv("MP_SMTP_RELAY_USERNAME") diff --git a/config/config.go b/config/config.go index 8dda670..9174a86 100644 --- a/config/config.go +++ b/config/config.go @@ -212,6 +212,7 @@ type SMTPRelayConfigStruct struct { Host string `yaml:"host"` Port int `yaml:"port"` STARTTLS bool `yaml:"starttls"` + TLS bool `yaml:"tls"` AllowInsecure bool `yaml:"allow-insecure"` Auth string `yaml:"auth"` // none, plain, login, cram-md5 Username string `yaml:"username"` // plain & cram-md5 diff --git a/config/validators.go b/config/validators.go index 1056a9d..be63e55 100644 --- a/config/validators.go +++ b/config/validators.go @@ -94,6 +94,10 @@ func validateRelayConfig() error { SMTPRelayConfig.Port = 25 // default } + if SMTPRelayConfig.STARTTLS && SMTPRelayConfig.TLS { + return fmt.Errorf("[relay] TLS & STARTTLS cannot be required together") + } + SMTPRelayConfig.Auth = strings.ToLower(SMTPRelayConfig.Auth) if SMTPRelayConfig.Auth == "" || SMTPRelayConfig.Auth == "none" || SMTPRelayConfig.Auth == "false" { diff --git a/internal/smtpd/relay.go b/internal/smtpd/relay.go index 21e379c..1386c1f 100644 --- a/internal/smtpd/relay.go +++ b/internal/smtpd/relay.go @@ -59,27 +59,55 @@ func autoRelayMessage(from string, to []string, data *[]byte) { } } +func createSMTPClient(config config.SMTPRelayConfigStruct, addr string) (*smtp.Client, error) { + if config.TLS { + tlsConf := &tls.Config{ServerName: config.Host} // #nosec + tlsConf.InsecureSkipVerify = config.AllowInsecure + + conn, err := tls.Dial("tcp", addr, tlsConf) + if err != nil { + return nil, fmt.Errorf("TLS Dial error: %v", err) + } + + client, err := smtp.NewClient(conn, tlsConf.ServerName) + if err != nil { + conn.Close() + return nil, fmt.Errorf("SMTP client error: %v", err) + } + + // Note: The caller is responsible for closing the client + return client, nil + } + + client, err := smtp.Dial(addr) + if err != nil { + return nil, fmt.Errorf("error connecting to %s: %v", addr, err) + } + + if config.STARTTLS { + tlsConf := &tls.Config{ServerName: config.Host} // #nosec + tlsConf.InsecureSkipVerify = config.AllowInsecure + + if err = client.StartTLS(tlsConf); err != nil { + client.Close() + return nil, fmt.Errorf("error creating StartTLS config: %v", err) + } + } + + // Note: The caller is responsible for closing the client + return client, nil +} + // Relay will connect to a pre-configured SMTP server and send a message to one or more recipients. func Relay(from string, to []string, msg []byte) error { addr := fmt.Sprintf("%s:%d", config.SMTPRelayConfig.Host, config.SMTPRelayConfig.Port) - c, err := smtp.Dial(addr) + c, err := createSMTPClient(config.SMTPRelayConfig, addr) if err != nil { - return fmt.Errorf("error connecting to %s: %s", addr, err.Error()) + return err } - defer c.Close() - if config.SMTPRelayConfig.STARTTLS { - conf := &tls.Config{ServerName: config.SMTPRelayConfig.Host} // #nosec - - conf.InsecureSkipVerify = config.SMTPRelayConfig.AllowInsecure - - if err = c.StartTLS(conf); err != nil { - return fmt.Errorf("error creating StartTLS config: %s", err.Error()) - } - } - auth := relayAuthFromConfig() if auth != nil {