Skip to content

Add configurable TLS profiles for listeners#295

Open
StrongWind1 wants to merge 1 commit intodecke:masterfrom
StrongWind1:tls_profile
Open

Add configurable TLS profiles for listeners#295
StrongWind1 wants to merge 1 commit intodecke:masterfrom
StrongWind1:tls_profile

Conversation

@StrongWind1
Copy link
Copy Markdown

This PR adds a new configuration option, tls_profile, to control the TLS policy used by inbound starttls:// and tls:// listeners.

Why:

  • I deploy smtprelay to internal networks, where we sometimes need to accept connections from older/embedded devices (e.g., printers, scanners, legacy appliances) that don’t support modern cipher suites
  • I wanted to include the internet facing option where stricter TLS settings are needed (e.g., TLS 1.3 only or TLS 1.2+ without CBC fallback suites)
  • Even in "legacy" environments, SMTP over TLS with weaker ciphers is still preferable to plaintext/unauthenticated SMTP

Changes:

  • Add a new config/flag: tls_profile (default: default)
  • Implement five TLS profiles:
    • default: Go standard library defaults (recommended for most deployments)
    • modern: TLS 1.3 only
    • hardened: TLS 1.2 and TLS 1.3; TLS 1.2 restricted to modern AEAD + ECDHE suites (GCM + ChaCha20). No CBC fallback suites.
    • extended: TLS 1.2 and TLS 1.3; hardened plus additional TLS 1.2 compatibility suites (CBC + RSA key exchange) to support older internal clients.
    • legacy: last resort; enables TLS 1.0+ and all TLS 1.0–1.2 cipher suites exposed by the Go standard library.

File updates:

  • config.go
    • Add tls_profile option (flag/config) with accepted values: modern | hardened | default | extended | legacy (default is the default )
  • main.go
    • Update getTLSConfig() to apply the selected TLS profile
  • smtprelay.ini
    • Add a commented help section describing tls_profile and example usage

@decke
Copy link
Copy Markdown
Owner

decke commented Jan 2, 2026

Thanks for the PR! I do agree that this is useful and the implementation is technically okay as well.

I still try to understand what the practical difference between hardened and extended is. I guess you added hardened because testssl.sh reported some worrying vulns against extended?

I really think having 5 profiles is too much and explaining the difference between hardened and extended is a waste of time.

What do you think about keeping hardened and giving it a new name (Mozilla SSL Config Generator would call it intermediate) and remove extended? The legacy profile should cover that anyway and using it does not weaken your modern clients - they can continue using TLS1.3.

@StrongWind1 StrongWind1 force-pushed the tls_profile branch 2 times, most recently from 69de251 to 3dec4ed Compare March 26, 2026 03:32
Add a tls_profile option that controls the minimum TLS version and
allowed cipher suites for STARTTLS/TLS listeners. Profiles align with
the Mozilla SSL Configuration Generator guidelines.

Profiles:
  default      - Go standard library defaults (no explicit constraints)
  modern       - TLS 1.3+ only (Mozilla modern)
  intermediate - TLS 1.2+ with AEAD + ECDHE suites only (Mozilla intermediate)
  legacy       - TLS 1.0+ with all Go cipher suites including insecure ones
@StrongWind1
Copy link
Copy Markdown
Author

Thanks for the review! Apologies on taking so long to get back to this. You're right, the hardened/extended split was hard to justify. I've reworked the PR down to 4 profiles and matched them to the Mozilla SSL Configuration Generator naming.

What changed

  • Renamed hardened to intermediate to match Mozilla's naming
  • Removed extended entirely. Like you said, legacy already covers those clients and doesn't weaken modern ones
  • Fixed a bug in modern: it was setting MaxVersion = TLS 1.3 which would have blocked future TLS versions. Now it only sets MinVersion
  • Updated the config docs to reference the Mozilla guidelines

Current profiles

Profile Min TLS TLS 1.2 cipher suites Use case
default (Go default) (Go default) Most deployments, tracks Go stdlib improvements
modern 1.3 none TLS 1.3 capable clients only
intermediate 1.2 6 AEAD + ECDHE only Internet facing, forward secrecy required
legacy 1.0 all (including insecure) Very old clients that can't do TLS 1.2

Intermediate TLS 1.2 cipher suites

TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256

How this lines up with Mozilla

I checked against Mozilla guidelines v5.7 (current), v5.8, and v6.0 (still a proposal, not final yet) to make sure we stay aligned as the spec evolves.

modern

Mozilla 5.7 Mozilla 5.8 Mozilla 6.0 Our modern
TLS versions 1.3 1.3 1.3 1.3+
TLS 1.2 ciphers none none none none
Curves X25519, P-256, P-384 +X25519MLKEM768 +X25519MLKEM768 Go default

Lines up across all three versions. We don't configure curves explicitly so Go's defaults apply, and they'll pick up post quantum curve support as Go adds it.

intermediate

Mozilla 5.7 Mozilla 5.8 Mozilla 6.0 Our intermediate
TLS versions 1.2+ 1.2+ 1.2+ 1.2+
ECDHE AEAD suites 6 6 6 6
DHE suites 3 0 (dropped) 0 0

Same 6 suites across the board. Mozilla 5.7 had 3 DHE-RSA suites but Go doesn't implement DHE at all, and Mozilla dropped them in 5.8 anyway.

legacy vs Mozilla old

Mozilla 5.7 old Mozilla 5.8 old Mozilla 6.0 Our legacy
TLS versions 1.0+ 1.0+ removed 1.0+
ECDHE AEAD 6 6 6
ECDHE CBC 6 8 6
RSA kex 6 7 7
3DES 1 (RSA only) 1 (RSA only) 2 (RSA + ECDHE)
RC4 no no yes

Our legacy is intentionally broader than Mozilla old. It enables everything Go supports as a last resort option. Worth noting that Mozilla is removing their old profile entirely in the proposed v6.0.

@StrongWind1 StrongWind1 requested a review from decke March 26, 2026 04:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants