From dd061f56d463460920fce2f26b99e5bf6624462b Mon Sep 17 00:00:00 2001 From: Kailash Nadh Date: Mon, 3 Jan 2022 19:28:36 +0530 Subject: [PATCH] Add support for direct SSL/TLS (non-STARTTLS) SMTP connections. - Add support for TLS in `smtppool` (v0.4.0) and upgrade the lib. - Change `tls_enabled: bool` in the settings table to string `tls_type: STARTTLS|TLS|none` and on the settings UI. - Add DB migrations and schema changes to apply the field change. Closes #504. --- cmd/settings.go | 2 +- frontend/src/views/settings/smtp.vue | 12 ++++++++---- go.mod | 2 +- go.sum | 2 ++ i18n/cs-cz.json | 1 + i18n/de.json | 1 + i18n/en.json | 3 ++- i18n/es.json | 1 + i18n/fr.json | 1 + i18n/hu.json | 1 + i18n/it.json | 1 + i18n/ml.json | 1 + i18n/nl.json | 1 + i18n/pl.json | 1 + i18n/pt-BR.json | 1 + i18n/pt.json | 1 + i18n/ro.json | 1 + i18n/ru.json | 1 + i18n/tr.json | 1 + internal/messenger/email/email.go | 9 +++++++-- internal/migrations/v2.1.0.go | 15 +++++++++++++++ schema.sql | 4 ++-- 22 files changed, 52 insertions(+), 11 deletions(-) diff --git a/cmd/settings.go b/cmd/settings.go index 2ae9fbf..9893c95 100644 --- a/cmd/settings.go +++ b/cmd/settings.go @@ -68,7 +68,7 @@ type settings struct { MaxMsgRetries int `json:"max_msg_retries"` IdleTimeout string `json:"idle_timeout"` WaitTimeout string `json:"wait_timeout"` - TLSEnabled bool `json:"tls_enabled"` + TLSType string `json:"tls_type"` TLSSkipVerify bool `json:"tls_skip_verify"` } `json:"smtp"` diff --git a/frontend/src/views/settings/smtp.vue b/frontend/src/views/settings/smtp.vue index 981dffa..c7734c7 100644 --- a/frontend/src/views/settings/smtp.vue +++ b/frontend/src/views/settings/smtp.vue @@ -81,13 +81,17 @@
- + :message="$t('settings.mailserver.tlsHelp')" label-position="on-border"> + + + + + + :disabled="item.tls_type === 'none'" name="item.tls_skip_verify" />
@@ -188,7 +192,7 @@ export default Vue.extend({ max_msg_retries: 2, idle_timeout: '15s', wait_timeout: '5s', - tls_enabled: true, + tls_type: 'STARTTLS', tls_skip_verify: false, }); diff --git a/go.mod b/go.mod index e14ef01..3f4e9e8 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/knadh/go-pop3 v0.3.0 github.com/knadh/goyesql/v2 v2.1.2 github.com/knadh/koanf v1.2.3 - github.com/knadh/smtppool v0.3.1 + github.com/knadh/smtppool v0.4.0 github.com/knadh/stuffbin v1.1.0 github.com/labstack/echo/v4 v4.6.1 github.com/labstack/gommon v0.3.1 // indirect diff --git a/go.sum b/go.sum index 4977c5b..65edb89 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/knadh/koanf v1.2.3 h1:2Rkr0YhhYk+4QEOm800Q3Pu0Wi87svTxM6uuEb4WhYw= github.com/knadh/koanf v1.2.3/go.mod h1:xpPTwMhsA/aaQLAilyCCqfpEiY1gpa160AiCuWHJUjY= github.com/knadh/smtppool v0.3.1 h1:teF/Lp/8wInTq2gTAOnmxPIcX9yPY6o4n57qf0qdJfM= github.com/knadh/smtppool v0.3.1/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA= +github.com/knadh/smtppool v0.4.0 h1:335iXPwZ6katJVhauV4O6e8uPvvPmO6YLrfDQhb6UvE= +github.com/knadh/smtppool v0.4.0/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA= github.com/knadh/stuffbin v1.1.0 h1:f5S5BHzZALjuJEgTIOMC9NidEnBJM7Ze6Lu1GHR/lwU= github.com/knadh/stuffbin v1.1.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= diff --git a/i18n/cs-cz.json b/i18n/cs-cz.json index d8934da..d84fac6 100644 --- a/i18n/cs-cz.json +++ b/i18n/cs-cz.json @@ -172,6 +172,7 @@ "globals.months.7": "Črc", "globals.months.8": "Srp", "globals.months.9": "Zář", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Nedoručitelnost | Případy nedoručitelnosti", "globals.terms.bounces": "Případy nedoručitelnosti", diff --git a/i18n/de.json b/i18n/de.json index 3d0ee2d..f67d947 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Aug", "globals.months.9": "Sep", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/en.json b/i18n/en.json index 4e10537..f18aa34 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Aug", "globals.months.9": "Sep", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", @@ -360,7 +361,7 @@ "settings.mailserver.skipTLS": "Skip TLS verification", "settings.mailserver.skipTLSHelp": "Skip hostname check on the TLS certificate.", "settings.mailserver.tls": "TLS", - "settings.mailserver.tlsHelp": "Enable STARTTLS.", + "settings.mailserver.tlsHelp": "TLS/SSL encryption. STARTTLS is commonly used.", "settings.mailserver.username": "Username", "settings.mailserver.waitTimeout": "Wait timeout", "settings.mailserver.waitTimeoutHelp": "Time to wait for new activity on a connection before closing it and removing it from the pool (s for second, m for minute).", diff --git a/i18n/es.json b/i18n/es.json index c41808c..a7ea471 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -172,6 +172,7 @@ "globals.months.7": "Julio", "globals.months.8": "Agosto", "globals.months.9": "Setiembre", + "globals.states.off": "Off", "globals.terms.analytics": "Analitica", "globals.terms.bounce": "Rebote | Rebotes", "globals.terms.bounces": "Rebotes", diff --git a/i18n/fr.json b/i18n/fr.json index 7d673dd..eb65639 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -172,6 +172,7 @@ "globals.months.7": "juil.", "globals.months.8": "août", "globals.months.9": "sept.", + "globals.states.off": "Off", "globals.terms.analytics": "Analyses", "globals.terms.bounce": "Rebond | Rebonds", "globals.terms.bounces": "Rebonds", diff --git a/i18n/hu.json b/i18n/hu.json index 5e0a195..e7b287a 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Aug", "globals.months.9": "Sep", + "globals.states.off": "Off", "globals.terms.analytics": "Analitika", "globals.terms.bounce": "Visszapattanó | Visszapattanók", "globals.terms.bounces": "Visszapattanók", diff --git a/i18n/it.json b/i18n/it.json index 757d12b..24859d7 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -172,6 +172,7 @@ "globals.months.7": "Lug", "globals.months.8": "Ago", "globals.months.9": "Set", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/ml.json b/i18n/ml.json index f19ef43..97d3999 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -172,6 +172,7 @@ "globals.months.7": "ജൂലൈ", "globals.months.8": "ഓഗസ്റ്റ്", "globals.months.9": "സെപ്റ്റംബർ", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/nl.json b/i18n/nl.json index 47897b5..9e14e9e 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Aug", "globals.months.9": "Sep", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/pl.json b/i18n/pl.json index b4fd312..c31847c 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -172,6 +172,7 @@ "globals.months.7": "Lip", "globals.months.8": "Sie", "globals.months.9": "Wrz", + "globals.states.off": "Off", "globals.terms.analytics": "Analityka", "globals.terms.bounce": "Odbicie | Obicia", "globals.terms.bounces": "Odbicia", diff --git a/i18n/pt-BR.json b/i18n/pt-BR.json index ecb9d2b..a5ae1ed 100644 --- a/i18n/pt-BR.json +++ b/i18n/pt-BR.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Ago", "globals.months.9": "Set", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/pt.json b/i18n/pt.json index c7f14ae..6a818fc 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -172,6 +172,7 @@ "globals.months.7": "Jul", "globals.months.8": "Ago", "globals.months.9": "Set", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/ro.json b/i18n/ro.json index a0c3829..6a0ab3c 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -172,6 +172,7 @@ "globals.months.7": "Iul", "globals.months.8": "Aug", "globals.months.9": "Sep", + "globals.states.off": "Off", "globals.terms.analytics": "Analitice", "globals.terms.bounce": "Respins | Respinse", "globals.terms.bounces": "Respinse", diff --git a/i18n/ru.json b/i18n/ru.json index beb6ed7..eb4386c 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -172,6 +172,7 @@ "globals.months.7": "Июл", "globals.months.8": "Авг", "globals.months.9": "Сен", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/i18n/tr.json b/i18n/tr.json index 139bfcc..67fea55 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -172,6 +172,7 @@ "globals.months.7": "Tem", "globals.months.8": "Aug", "globals.months.9": "Eyl", + "globals.states.off": "Off", "globals.terms.analytics": "Analytics", "globals.terms.bounce": "Bounce | Bounces", "globals.terms.bounces": "Bounces", diff --git a/internal/messenger/email/email.go b/internal/messenger/email/email.go index 4a48b7d..55d8809 100644 --- a/internal/messenger/email/email.go +++ b/internal/messenger/email/email.go @@ -18,7 +18,7 @@ type Server struct { Username string `json:"username"` Password string `json:"password"` AuthProtocol string `json:"auth_protocol"` - TLSEnabled bool `json:"tls_enabled"` + TLSType string `json:"tls_type"` TLSSkipVerify bool `json:"tls_skip_verify"` EmailHeaders map[string]string `json:"email_headers"` @@ -57,13 +57,18 @@ func New(servers ...Server) (*Emailer, error) { s.Opt.Auth = auth // TLS config. - if s.TLSEnabled { + if s.TLSType != "none" { s.TLSConfig = &tls.Config{} if s.TLSSkipVerify { s.TLSConfig.InsecureSkipVerify = s.TLSSkipVerify } else { s.TLSConfig.ServerName = s.Host } + + // SSL/TLS, not STARTTLS. + if s.TLSType == "TLS" { + s.Opt.SSL = true + } } pool, err := smtppool.New(s.Opt) diff --git a/internal/migrations/v2.1.0.go b/internal/migrations/v2.1.0.go index a15f03b..a55f34c 100644 --- a/internal/migrations/v2.1.0.go +++ b/internal/migrations/v2.1.0.go @@ -8,6 +8,7 @@ import ( // V2_1_0 performs the DB migrations for v.2.1.0. func V2_1_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error { + // Insert into appearance related settings. if _, err := db.Exec(` INSERT INTO settings (key, value) VALUES ('appearance.admin.custom_css', '""'), @@ -19,5 +20,19 @@ func V2_1_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error { return err } + // Replace all `tls_enabled: true/false` keys in the `smtp` settings JSON array + // with the new field `tls_type: STARTTLS|TLS|none`. + // The `tls_enabled` key is removed. + if _, err := db.Exec(` + UPDATE settings SET value = s.updated + FROM ( + SELECT JSONB_AGG( + JSONB_SET(v - 'tls_enabled', '{tls_type}', (CASE WHEN v->>'tls_enabled' = 'true' THEN '"STARTTLS"' ELSE '"none"' END)::JSONB) + ) AS updated FROM settings, JSONB_ARRAY_ELEMENTS(value) v WHERE key = 'smtp' + ) s WHERE key = 'smtp'; + `); err != nil { + return err + } + return nil } diff --git a/schema.sql b/schema.sql index b64a1db..c976e63 100644 --- a/schema.sql +++ b/schema.sql @@ -207,8 +207,8 @@ INSERT INTO settings (key, value) VALUES ('upload.s3.bucket_type', '"public"'), ('upload.s3.expiry', '"14d"'), ('smtp', - '[{"enabled":true, "host":"smtp.yoursite.com","port":25,"auth_protocol":"cram","username":"username","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_enabled":true,"tls_skip_verify":false,"email_headers":[]}, - {"enabled":false, "host":"smtp2.yoursite.com","port":587,"auth_protocol":"plain","username":"username","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_enabled":false,"tls_skip_verify":false,"email_headers":[]}]'), + '[{"enabled":true, "host":"smtp.yoursite.com","port":25,"auth_protocol":"cram","username":"username","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_type":"STARTTLS","tls_skip_verify":false,"email_headers":[]}, + {"enabled":false, "host":"smtp.gmail.com","port":465,"auth_protocol":"login","username":"username@gmail.com","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_type":"TLS","tls_skip_verify":false,"email_headers":[]}]'), ('messengers', '[]'), ('bounce.enabled', 'false'), ('bounce.webhooks_enabled', 'false'),