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.
This commit is contained in:
Kailash Nadh 2022-01-03 19:28:36 +05:30
parent b48a15cfa3
commit dd061f56d4
22 changed files with 52 additions and 11 deletions

View file

@ -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"`

View file

@ -81,13 +81,17 @@
<div class="column">
<b-field grouped>
<b-field :label="$t('settings.mailserver.tls')" expanded
:message="$t('settings.mailserver.tlsHelp')">
<b-switch v-model="item.tls_enabled" name="item.tls_enabled" />
:message="$t('settings.mailserver.tlsHelp')" label-position="on-border">
<b-select v-model="item.tls_type" name="items.tls_type">
<option value="none">{{ $t('globals.states.off') }}</option>
<option value="STARTTLS">STARTTLS</option>
<option value="TLS">SSL/TLS</option>
</b-select>
</b-field>
<b-field :label="$t('settings.mailserver.skipTLS')" expanded
:message="$t('settings.mailserver.skipTLSHelp')">
<b-switch v-model="item.tls_skip_verify"
:disabled="!item.tls_enabled" name="item.tls_skip_verify" />
:disabled="item.tls_type === 'none'" name="item.tls_skip_verify" />
</b-field>
</b-field>
</div>
@ -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,
});

2
go.mod
View file

@ -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

2
go.sum
View file

@ -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=

View file

@ -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",

View file

@ -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",

View file

@ -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).",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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)

View file

@ -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
}

View file

@ -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'),