Skip to content

Commit d60c438

Browse files
committed
Rework mailer settings (fixes #18901)
1 parent af09136 commit d60c438

File tree

11 files changed

+227
-103
lines changed

11 files changed

+227
-103
lines changed

Diff for: cmd/admin.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,9 @@ var (
404404
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
405405
},
406406
cli.StringFlag{
407-
Name: "host",
407+
Name: "addr",
408408
Value: "",
409-
Usage: "SMTP Host",
409+
Usage: "SMTP Addr",
410410
},
411411
cli.IntFlag{
412412
Name: "port",
@@ -936,8 +936,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
936936
}
937937
conf.Auth = c.String("auth-type")
938938
}
939-
if c.IsSet("host") {
940-
conf.Host = c.String("host")
939+
if c.IsSet("addr") {
940+
conf.Addr = c.String("addr")
941941
}
942942
if c.IsSet("port") {
943943
conf.Port = c.Int("port")

Diff for: custom/conf/app.example.ini

+29-23
Original file line numberDiff line numberDiff line change
@@ -1503,39 +1503,45 @@ PATH =
15031503
;; Prefix displayed before subject in mail
15041504
;SUBJECT_PREFIX =
15051505
;;
1506-
;; Mail server
1507-
;; Gmail: smtp.gmail.com:587
1508-
;; QQ: smtp.qq.com:465
1509-
;; As per RFC 8314 using Implicit TLS/SMTPS on port 465 (if supported) is recommended,
1510-
;; otherwise STARTTLS on port 587 should be used.
1511-
;HOST =
1512-
;;
1513-
;; Disable HELO operation when hostnames are different.
1514-
;DISABLE_HELO =
1515-
;;
1516-
;; Custom hostname for HELO operation, if no value is provided, one is retrieved from system.
1506+
;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix".
1507+
;; If your provider does not explicitly say which protocol it uses but does provide a port,
1508+
;; you can set SMTP_PORT instead and this will be inferred.
1509+
;PROTOCOL =
1510+
;
1511+
;; Mail server address, e.g. smtp.gmail.com.
1512+
;; For smtp+unix, this should be a path to a unix socket instead.
1513+
;SMTP_ADDR =
1514+
;;
1515+
;; Mail server port. Common ports are:
1516+
;; 25: insecure SMTP
1517+
;; 465: SMTP Secure
1518+
;; 587: StartTLS
1519+
;; If no protocol is specified, it will be inferred by this setting.
1520+
;SMTP_PORT =
1521+
;;
1522+
;; Enable HELO operation. Defaults to true.
1523+
;ENABLE_HELO = true
1524+
;;
1525+
;; Custom hostname for HELO operation.
1526+
;; If no value is provided, one is retrieved from system.
15171527
;HELO_HOSTNAME =
15181528
;;
1519-
;; Whether or not to skip verification of certificates; `true` to disable verification. This option is unsafe. Consider adding the certificate to the system trust store instead.
1520-
;SKIP_VERIFY = false
1521-
;;
1522-
;; Use client certificate
1523-
;USE_CERTIFICATE = false
1524-
;CERT_FILE = custom/mailer/cert.pem
1525-
;KEY_FILE = custom/mailer/key.pem
1529+
;; If set to `true`, completely ignores server certificate validation errors.
1530+
;; This option is unsafe. Consider adding the certificate to the system trust store instead.
1531+
;FORCE_TRUST_SERVER_CERT = false
15261532
;;
1527-
;; Should SMTP connect with TLS, (if port ends with 465 TLS will always be used.)
1528-
;; If this is false but STARTTLS is supported the connection will be upgraded to TLS opportunistically.
1529-
;IS_TLS_ENABLED = false
1533+
;; Use client certificate in connection.
1534+
;USE_CLIENT_CERT = false
1535+
;CLIENT_CERT_FILE = custom/mailer/cert.pem
1536+
;CLIENT_KEY_FILE = custom/mailer/key.pem
15301537
;;
15311538
;; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
15321539
;FROM =
15331540
;;
15341541
;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address.
15351542
;ENVELOPE_FROM =
15361543
;;
1537-
;; Mailer user name and password
1538-
;; Please Note: Authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via STARTTLS) or `HOST=localhost`.
1544+
;; Mailer user name and password, if required by provider.
15391545
;USER =
15401546
;;
15411547
;; Use PASSWD = `your password` for quoting if you use special characters in the password.

Diff for: modules/setting/mailer.go

+127-29
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package setting
66

77
import (
8+
"net"
89
"net/mail"
10+
"strings"
911
"time"
1012

1113
"code.gitea.io/gitea/modules/log"
@@ -27,14 +29,16 @@ type Mailer struct {
2729
SubjectPrefix string
2830

2931
// SMTP sender
30-
Host string
31-
User, Passwd string
32-
DisableHelo bool
33-
HeloHostname string
34-
SkipVerify bool
35-
UseCertificate bool
36-
CertFile, KeyFile string
37-
IsTLSEnabled bool
32+
Protocol string
33+
SMTPAddr string
34+
SMTPPort string
35+
User, Passwd string
36+
EnableHelo bool
37+
HeloHostname string
38+
ForceTrustServerCert bool
39+
UseClientCert bool
40+
ClientCertFile string
41+
ClientKeyFile string
3842

3943
// Sendmail sender
4044
SendmailPath string
@@ -58,17 +62,18 @@ func newMailService() {
5862
SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
5963
MailerType: sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}),
6064

61-
Host: sec.Key("HOST").String(),
62-
User: sec.Key("USER").String(),
63-
Passwd: sec.Key("PASSWD").String(),
64-
DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
65-
HeloHostname: sec.Key("HELO_HOSTNAME").String(),
66-
SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
67-
UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
68-
CertFile: sec.Key("CERT_FILE").String(),
69-
KeyFile: sec.Key("KEY_FILE").String(),
70-
IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(),
71-
SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""),
65+
Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix"}),
66+
SMTPAddr: sec.Key("SMTP_ADDR").String(),
67+
SMTPPort: sec.Key("SMTP_PORT").String(),
68+
User: sec.Key("USER").String(),
69+
Passwd: sec.Key("PASSWD").String(),
70+
EnableHelo: sec.Key("ENABLE_HELO").MustBool(true),
71+
HeloHostname: sec.Key("HELO_HOSTNAME").String(),
72+
ForceTrustServerCert: sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false),
73+
UseClientCert: sec.Key("USE_CLIENT_CERT").MustBool(false),
74+
ClientCertFile: sec.Key("CLIENT_CERT_FILE").String(),
75+
ClientKeyFile: sec.Key("CLIENT_KEY_FILE").String(),
76+
SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""),
7277

7378
SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"),
7479
SendmailTimeout: sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute),
@@ -77,12 +82,6 @@ func newMailService() {
7782
MailService.From = sec.Key("FROM").MustString(MailService.User)
7883
MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("")
7984

80-
// FIXME: DEPRECATED to be removed in v1.18.0
81-
deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
82-
if sec.HasKey("ENABLE_HTML_ALTERNATIVE") {
83-
MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
84-
}
85-
8685
// FIXME: DEPRECATED to be removed in v1.18.0
8786
deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE")
8887
if sec.HasKey("USE_SENDMAIL") {
@@ -91,6 +90,109 @@ func newMailService() {
9190
}
9291
}
9392

93+
if MailService.MailerType == "" {
94+
MailService.MailerType = "smtp"
95+
}
96+
97+
// FIXME: DEPRECATED to be removed in v1.18.0
98+
deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR")
99+
if sec.HasKey("HOST") {
100+
givenHost := sec.Key("HOST").String()
101+
addr, port, err := net.SplitHostPort(givenHost)
102+
if err != nil {
103+
log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err)
104+
}
105+
MailService.SMTPAddr = addr
106+
MailService.SMTPPort = port
107+
}
108+
109+
// FIXME: DEPRECATED to be removed in v1.18.0
110+
deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
111+
if sec.HasKey("IS_TLS_ENABLED") {
112+
if sec.Key("IS_TLS_ENABLED").MustBool() {
113+
MailService.Protocol = "smtps"
114+
} else {
115+
MailService.Protocol = "smtp+startls"
116+
}
117+
}
118+
119+
if MailService.SMTPPort == "" {
120+
switch MailService.Protocol {
121+
case "smtp":
122+
MailService.SMTPPort = "25"
123+
case "smtps":
124+
MailService.SMTPPort = "465"
125+
case "smtp+startls":
126+
MailService.SMTPPort = "587"
127+
}
128+
}
129+
130+
if MailService.MailerType == "smtp" && MailService.Protocol == "" {
131+
if strings.ContainsAny(MailService.SMTPAddr, "/\\") {
132+
MailService.Protocol = "smtp+unix"
133+
} else {
134+
switch MailService.SMTPPort {
135+
case "25":
136+
MailService.Protocol = "smtp"
137+
case "465":
138+
MailService.Protocol = "smtps"
139+
case "587":
140+
MailService.Protocol = "smtp+startls"
141+
default:
142+
log.Fatal("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = \"%s\"", MailService.SMTPPort)
143+
}
144+
}
145+
}
146+
147+
if MailService.Protocol == "smtp" {
148+
switch MailService.SMTPAddr {
149+
case "localhost":
150+
case "127.0.0.1":
151+
case "::1":
152+
case "[::1]":
153+
// this is a local address, so, we're fine
154+
break
155+
default:
156+
log.Warn("connect via insecure SMTP to non-local address")
157+
}
158+
}
159+
160+
// FIXME: DEPRECATED to be removed in v1.18.0
161+
deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO")
162+
if sec.HasKey("DISABLE_HELO") {
163+
MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool()
164+
}
165+
166+
// FIXME: DEPRECATED to be removed in v1.18.0
167+
deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT")
168+
if sec.HasKey("SKIP_VERIFY") {
169+
MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool()
170+
}
171+
172+
// FIXME: DEPRECATED to be removed in v1.18.0
173+
deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT")
174+
if sec.HasKey("USE_CERTIFICATE") {
175+
MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool()
176+
}
177+
178+
// FIXME: DEPRECATED to be removed in v1.18.0
179+
deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE")
180+
if sec.HasKey("CERT_FILE") {
181+
MailService.ClientCertFile = sec.Key("CERT_FILE").String()
182+
}
183+
184+
// FIXME: DEPRECATED to be removed in v1.18.0
185+
deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE")
186+
if sec.HasKey("KEY_FILE") {
187+
MailService.ClientKeyFile = sec.Key("KEY_FILE").String()
188+
}
189+
190+
// FIXME: DEPRECATED to be removed in v1.18.0
191+
deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
192+
if sec.HasKey("ENABLE_HTML_ALTERNATIVE") {
193+
MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
194+
}
195+
94196
parsed, err := mail.ParseAddress(MailService.From)
95197
if err != nil {
96198
log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err)
@@ -113,10 +215,6 @@ func newMailService() {
113215
MailService.EnvelopeFrom = parsed.Address
114216
}
115217

116-
if MailService.MailerType == "" {
117-
MailService.MailerType = "smtp"
118-
}
119-
120218
if MailService.MailerType == "sendmail" {
121219
MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
122220
if err != nil {

Diff for: routers/install/install.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ func Install(ctx *context.Context) {
131131

132132
// E-mail service settings
133133
if setting.MailService != nil {
134-
form.SMTPHost = setting.MailService.Host
134+
form.SMTPAddr = setting.MailService.SMTPAddr
135+
form.SMTPPort = setting.MailService.SMTPPort
135136
form.SMTPFrom = setting.MailService.From
136137
form.SMTPUser = setting.MailService.User
137138
}
@@ -418,9 +419,10 @@ func SubmitInstall(ctx *context.Context) {
418419
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
419420
}
420421

421-
if len(strings.TrimSpace(form.SMTPHost)) > 0 {
422+
if len(strings.TrimSpace(form.SMTPAddr)) > 0 {
422423
cfg.Section("mailer").Key("ENABLED").SetValue("true")
423-
cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost)
424+
cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr)
425+
cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort)
424426
cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom)
425427
cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser)
426428
cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)

Diff for: routers/web/admin/auths.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
159159
func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
160160
return &smtp.Source{
161161
Auth: form.SMTPAuth,
162-
Host: form.SMTPHost,
162+
Addr: form.SMTPAddr,
163163
Port: form.SMTPPort,
164164
AllowedDomains: form.AllowedDomains,
165165
ForceSMTPS: form.ForceSMTPS,

Diff for: services/auth/source/smtp/auth.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ var ErrUnsupportedLoginType = errors.New("Login source is unknown")
5858
func Authenticate(a smtp.Auth, source *Source) error {
5959
tlsConfig := &tls.Config{
6060
InsecureSkipVerify: source.SkipVerify,
61-
ServerName: source.Host,
61+
ServerName: source.Addr,
6262
}
6363

64-
conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)))
64+
conn, err := net.Dial("tcp", net.JoinHostPort(source.Addr, strconv.Itoa(source.Port)))
6565
if err != nil {
6666
return err
6767
}
@@ -71,7 +71,7 @@ func Authenticate(a smtp.Auth, source *Source) error {
7171
conn = tls.Client(conn, tlsConfig)
7272
}
7373

74-
client, err := smtp.NewClient(conn, source.Host)
74+
client, err := smtp.NewClient(conn, source.Addr)
7575
if err != nil {
7676
return fmt.Errorf("failed to create NewClient: %w", err)
7777
}

Diff for: services/auth/source/smtp/source.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
// Source holds configuration for the SMTP login source.
2020
type Source struct {
2121
Auth string
22-
Host string
22+
Addr string
2323
Port int
2424
AllowedDomains string `xorm:"TEXT"`
2525
ForceSMTPS bool

Diff for: services/auth/source/smtp/source_authenticate.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
3232
var auth smtp.Auth
3333
switch source.Auth {
3434
case PlainAuthentication:
35-
auth = smtp.PlainAuth("", userName, password, source.Host)
35+
auth = smtp.PlainAuth("", userName, password, source.Addr)
3636
case LoginAuthentication:
3737
auth = &loginAuthenticator{userName, password}
3838
case CRAMMD5Authentication:

Diff for: services/forms/auth_form.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type AuthenticationForm struct {
4545
IsActive bool
4646
IsSyncEnabled bool
4747
SMTPAuth string
48-
SMTPHost string
48+
SMTPAddr string
4949
SMTPPort int
5050
AllowedDomains string
5151
SecurityProtocol int `binding:"Range(0,2)"`

Diff for: services/forms/user_form.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ type InstallForm struct {
4040
AppURL string `binding:"Required"`
4141
LogRootPath string `binding:"Required"`
4242

43-
SMTPHost string
43+
SMTPAddr string
44+
SMTPPort string
4445
SMTPFrom string
4546
SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
4647
SMTPPasswd string

0 commit comments

Comments
 (0)