Pre-submission checks
Affected component
Email / SMTP
Severity
Medium — feature broken with a workaround
Summary
The "Email security" dropdown (TLS/SSL/None) is only actually read for one case — implicit SSL on port 465. For every other case, including when "None" is explicitly selected, the app falls through to Go's stdlib smtp.SendMail, which unconditionally attempts opportunistic STARTTLS if the server advertises it. There's no way to force plaintext, and the Host field doubles as both the connection target and the hostname used for TLS certificate verification, with no separate override.
Steps to reproduce
- Go to Instance Admin → Email.
- Set Host to an IP address (not a hostname) of a relay that advertises STARTTLS, and set Email security to "None".
- Save changes.
- Trigger any outbound email.
Expected behavior
Selecting "None" should mean no TLS is attempted at all, or at minimum the docs/UI should make clear that STARTTLS may still be negotiated opportunistically.
Actual behavior
STARTTLS is attempted regardless of the "None" selection. If the relay's certificate doesn't validate for the exact string in Host (e.g. Host is an IP but the cert only has a DNS SAN, or any other TLS chain-of-trust mismatch), the send fails with a TLS error — with no field to separate "what I connect to" from "what hostname the cert should match."
Devlane version or commit
7719dca
Environment
Alpine 3.21 (container), Go 1.26.4
Deployment mode
Self-hosted production
Database state
Fresh — migrations applied cleanly on startup
API logs
{"level":"ERROR","msg":"mail send failed","to":"[redacted]","subject":"Reset your Devlane password","error":"tls: failed to verify certificate: x509: cannot validate certificate for [redacted IP] because it doesn't contain any IP SANs"}
Browser console / network output
Additional context
api/internal/mail/mail.go, sendMailWithConfig:
go
useImplicitTLS := port == 465 && strings.EqualFold(strings.TrimSpace(security), "SSL")
if useImplicitTLS {
... // explicit tls.Dial path
}
// STARTTLS (port 587) or no security: standard SendMail
return smtp.SendMail(addr, auth, from, []string{to}, msg)
The security value is never consulted in the fallthrough branch.
AI assistance
Pre-submission checks
mainor the most recent release.Affected component
Email / SMTP
Severity
Medium — feature broken with a workaround
Summary
The "Email security" dropdown (TLS/SSL/None) is only actually read for one case — implicit SSL on port 465. For every other case, including when "None" is explicitly selected, the app falls through to Go's stdlib smtp.SendMail, which unconditionally attempts opportunistic STARTTLS if the server advertises it. There's no way to force plaintext, and the Host field doubles as both the connection target and the hostname used for TLS certificate verification, with no separate override.
Steps to reproduce
Expected behavior
Selecting "None" should mean no TLS is attempted at all, or at minimum the docs/UI should make clear that STARTTLS may still be negotiated opportunistically.
Actual behavior
STARTTLS is attempted regardless of the "None" selection. If the relay's certificate doesn't validate for the exact string in Host (e.g. Host is an IP but the cert only has a DNS SAN, or any other TLS chain-of-trust mismatch), the send fails with a TLS error — with no field to separate "what I connect to" from "what hostname the cert should match."
Devlane version or commit
7719dca
Environment
Alpine 3.21 (container), Go 1.26.4
Deployment mode
Self-hosted production
Database state
Fresh — migrations applied cleanly on startup
API logs
{"level":"ERROR","msg":"mail send failed","to":"[redacted]","subject":"Reset your Devlane password","error":"tls: failed to verify certificate: x509: cannot validate certificate for [redacted IP] because it doesn't contain any IP SANs"}Browser console / network output
Additional context
api/internal/mail/mail.go, sendMailWithConfig:
go
useImplicitTLS := port == 465 && strings.EqualFold(strings.TrimSpace(security), "SSL")
if useImplicitTLS {
... // explicit tls.Dial path
}
// STARTTLS (port 587) or no security: standard SendMail
return smtp.SendMail(addr, auth, from, []string{to}, msg)
The security value is never consulted in the fallthrough branch.
AI assistance