Skip to content

[BUG] "Email security" setting doesn't control whether STARTTLS is attempted #404

Description

@tristansgray

Pre-submission checks

  • I have searched existing issues and this is not a duplicate.
  • I am using the latest main or the most recent release.
  • This is not a security vulnerability (those go to SECURITY.md).

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

  1. Go to Instance Admin → Email.
  2. Set Host to an IP address (not a hostname) of a relay that advertises STARTTLS, and set Email security to "None".
  3. Save changes.
  4. 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

  • This issue was created or substantially drafted with AI assistance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions