Solved Need to set up TLS on Postfix

Civica Election Services insist they will only send mail to servers which use TLS and as I need to receive their mail at present for an election in progress that means I have to set up TLS on my incoming Postfix server.
I have tried my best to follow the guide at https://www.postfix.org/TLS_README.html for server configuration but I'm struggling to test the configuration.

My outgoing client isn't TLS-enabled so can only send in plain text and although I see the following lines (addresses munged) in the logs I'm not convinced as nothing has been received from outside with TLS:
Code:
Aug 22 22:17:47 mailin postfix/smtp[96183]: initializing the client-side TLS engine
Aug 22 22:17:47 mailin postfix/smtp[96183]: 0B0B71398A: to=<yyyy@zzzz.aaaa>, orig_to=<xxxx@xxxx.co.uk>, relay=master.localdomain[192.168.1.64]:25, delay=0.2, delays=0.1/0.06/0.02/0.03, tls=may:none, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 2DB152E0067)

If I try to enable TLS on my outgoing machine and everything then works, fine, but in all likelihood something will be wrong at at least one end and the problem will be troubleshooting that.

I have tried to use
openssl s_client to test but though this works well with https servers it seems unable to communicate with smtp and postfix complains "warning: non-SMTP command" when I follow online tutorials for validating TLS on mail servers using that.

Postfix is listening on port 25 and though I have forwarded ports 465 and 587 in my router and pf it isn't currently listening on them. There was no mention of needing them when using STARTTLS in the postfix README.

I must admit I'm out of my depth here as I've always had mail just work since setting up DKIM, SPF, and DMARC, but with Civica being so fussy I have to move on and get this extra layer of security working.
 
Civica Election Services insist they will only send mail to servers which use TLS and as I need to receive their mail at present for an election in progress that means I have to set up TLS on my incoming Postfix server.
I have tried my best to follow the guide at https://www.postfix.org/TLS_README.html for server configuration but I'm struggling to test the configuration.

I think you're mixing up some concepts of the SMTP protocol.
Please dont take that as an offense, this is often the case for longtime postmasters during long threads happening on mailing lists.

As you need to offer TLS for smtp clients (here: Civica Election Services), only smtpd_tls_* postfix's knobs are the ones you need to tweak for setting up TLS on smtpd(8) (smtpd whos listening on TCP/25, is the server process that is listening for incoming emails from outside):

Code:
# Opportunistic TLS: announce STARTTLS support to remote SMTP clients,
# but do not require that clients use TLS encryption.
smtpd_tls_security_level = may

# Supporting AUTH over TLS only
#
# Sending AUTH data over an unencrypted channel poses a security risk.
# When TLS layer encryption is required ("smtpd_tls_security_level = encrypt"),
# the Postfix SMTP server will announce and accept AUTH only after the TLS layer
# has been activated with STARTTLS.
# When TLS layer encryption is optional ("smtpd_tls_security_level = may"),
# it may however still be useful to only offer AUTH when TLS is active.
# To maintain compatibility with non-TLS clients, the default is to accept
# AUTH without encryption. In order to change this behavior, set "smtpd_tls_auth_only = yes".
smtpd_tls_auth_only = yes

# 0 Disable logging of TLS activity.
# 1 Log only a summary message on TLS handshake completion
# — no logging of client certificate trust-chain verification errors if client certificate
# verification is not required. With Postfix 2.8 and earlier, log the summary message,
# peer certificate summary information and unconditionally log trust-chain verification errors.
# 2 Also enable verbose logging in the Postfix TLS library, log session cache operations, and enable OpenSSL logging of the progress of the SSL handshake.
# 3 Also log hexadecimal and ASCII dump of TLS negotiation process.
# 4 Also log hexadecimal and ASCII dump of complete transmission after STARTTLS.
smtpd_tls_loglevel = 1

# SSL certificates
smtpd_tls_chain_files = /usr/local/etc/ssl/example.net/privkey.pem,/usr/local/etc/ssl/example.net/fullchain.pem

I have tried to use openssl s_client to test but though this works well with https servers it seems unable to communicate with smtp and postfix complains "warning: non-SMTP command" when I follow online tutorials for validating TLS on mail servers using that.

openssl-s_client(1) can of course use smtp:

Code:
% openssl s_client -connect smtp.example.org:25 -starttls smtp -crlf
Connecting to x.x.x.x
CONNECTED(00000005)
depth=3 C=US, O=Internet Security Research Group, CN=ISRG Root X2
verify return:1
depth=2 C=US, O=ISRG, CN=Root YE
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=YE2
verify return:1
depth=0 CN=*.example.org
verify return:1
---
Certificate chain
 0 s:CN=*.example.org
   i:C=US, O=Let's Encrypt, CN=YE2
   a:PKEY: EC, (prime256v1); sigalg: ecdsa-with-SHA384
   v:NotBefore: Jul 11 02:29:46 2026 GMT; NotAfter: Oct  9 02:29:45 2026 GMT
 1 s:C=US, O=Let's Encrypt, CN=YE2
   i:C=US, O=ISRG, CN=Root YE
   a:PKEY: EC, (secp384r1); sigalg: ecdsa-with-SHA384
   v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
 2 s:C=US, O=ISRG, CN=Root YE
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   a:PKEY: EC, (secp384r1); sigalg: ecdsa-with-SHA384
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
 3 s:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
   a:PKEY: EC, (secp384r1); sigalg: sha256WithRSAEncryption
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
subject=CN=*.example.org
issuer=C=US, O=Let's Encrypt, CN=YE2
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ecdsa_secp256r1_sha256
Peer Temp Key: X25519, 253 bits
---
SSL handshake has read 4027 bytes and written 1672 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
Server public key is 256 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
250 DSN
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: [...]
    Session-ID-ctx:
    Resumption PSK: [...]
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    [...]

    Start Time: 1787442388
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
EHLO client.example.net
250-smtp.example.org
250-SIZE 52428800
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
quit
221 2.0.0 Bye
closed

Postfix is listening on port 25 and though I have forwarded ports 465 and 587 in my router and pf it isn't currently listening on them. There was no mention of needing them when using STARTTLS in the postfix README.

I must admit I'm out of my depth here as I've always had mail just work since setting up DKIM, SPF, and DMARC, but with Civica being so fussy I have to move on and get this extra layer of security working.

TCP/465 (smtps) or TCP/587 (submission) are services and ports used only for authenticated users through SASL submitting emails by a MUA (Mail User Agent).
 
Code:
~]$ openssl s_client -connect mailin.evenas.org:25 -starttls smtp -crlf
CONNECTED(00000003)
Didn't find STARTTLS in server response, trying anyway...
C042A97C737F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:ssl/r
ecord/ssl3_record.c:354:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 239 bytes and written 354 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

and a verbose log:
Code:
Aug 23 09:18:26 mailin postfix/smtpd[1398]: connect from kjpetrie.plus.com[84.92.47.176]
Aug 23 09:18:26 mailin postfix/smtpd[1398]: smtp_stream_setup: maxtime=300 enable_deadline=0 min_data_rate=0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_hostname: smtpd_client_event_limit_ex
ceptions: kjpetrie.plus.com ~? 127.0.1.3/32
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_hostaddr: smtpd_client_event_limit_ex
ceptions: 84.92.47.176 ~? 127.0.1.3/32
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: kjpetrie.plus.com: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: 84.92.47.176: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: auto_clnt_open: connected to private/anvil
Aug 23 09:18:26 mailin postfix/smtpd[1398]: event_enable_read: fd 17
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 17 got 25
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: protocol
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: protocol
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute value: anvil_protocol
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: (list terminator)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: (end)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: send attr request = connect
Aug 23 09:18:26 mailin postfix/smtpd[1398]: send attr ident = smtp:84.92.47.176
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 17 flush 41
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 17 got 25
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: status
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: status
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute value: 0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: count
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: count
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute value: 1
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: rate
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: rate
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute value: 1
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: (list terminator)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: (end)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: report connect to all milters
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: non-protocol events for protocol version 6:
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: transport=unix endpoint=/tmp/opendkim.socket
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: my_version=0x6
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: my_actions=0x1ff SMFIF_ADDHDRS SMFIF_CHGBODY SMFIF_ADDRCPT SMFIF_DELRCPT SMFIF_CHGHDRS SMFIF_QUARANTINE SMFIF_CHGFROM SMFIF_ADDRCPT_PAR SMFIF_SETSYMLIST
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: my_events=0x1fffff SMFIP_NOCONNECT SMFIP_NOHELO SMFIP_NOMAIL SMFIP_NORCPT SMFIP_NOBODY SMFIP_NOHDRS SMFIP_NOEOH SMFIP_NR_HDR SMFIP_NOUNKNOWN SMFIP_NODATA SMFIP_SKIP SMFIP_RCPT_REJ SMFIP_NR_CONN SMFIP_NR_HELO SMFIP_NR_MAIL SMFIP_NR_RCPT SMFIP_NR_DATA SMFIP_NR_UNKN SMFIP_NR_EOH SMFIP_NR_BODY SMFIP_HDR_LEADSPC
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 18 flush 17
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 18 got 17
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: milter unix:/tmp/opendkim.socket version 6
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: events SMFIP_NOHELO SMFIP_NOUNKNOWN SMFIP_NODATA SMFIP_SKIP SMFIP_HDR_LEADSPC
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_connect: requests SMFIF_ADDHDRS SMFIF_CHGHDRS SMFIF_SETSYMLIST
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "j"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: result "mailin.evenas.org"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_conn_event: milter unix:/tmp/opendkim.socket: connect kjpetrie.plus.com/84.92.47.176
Aug 23 09:18:26 mailin postfix/smtpd[1398]: event: SMFIC_CONNECT; macros: j=mailin.evenas.org
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 18 flush 65
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 18 got 5
Aug 23 09:18:26 mailin postfix/smtpd[1398]: reply: SMFIR_CONTINUE data 0 bytes
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 220 mailin.evenas.org ESMTP Postfix
Aug 23 09:18:26 mailin postfix/smtpd[1398]: smtp_stream_setup: maxtime=300 enable_deadline=0 min_data_rate=0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: watchdog_pat: 0x39c19dc098d0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 16 flush 37
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 16 got 23
Aug 23 09:18:26 mailin postfix/smtpd[1398]: < kjpetrie.plus.com[84.92.47.176]: EHLO mail.example.com
Aug 23 09:18:26 mailin postfix/smtpd[1398]: report helo to all milters
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "{tls_version}"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "{cipher}"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "{cipher_bits}"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "{cert_subject}"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter_macro_lookup: "{cert_issuer}"
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_helo_event: milter unix:/tmp/opendkim.socket: helo mail.example.com
Aug 23 09:18:26 mailin postfix/smtpd[1398]: event: SMFIC_HELO; macros: (none)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: skipping event SMFIC_HELO for milter unix:/tmp/opendkim.socket
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: kjpetrie.plus.com: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: 84.92.47.176: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-mailin.evenas.org
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-PIPELINING
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-SIZE 104857600
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-VRFY
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-ETRN
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-ENHANCEDSTATUSCODES
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-8BITMIME
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-DSN
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250-SMTPUTF8
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 250 CHUNKING
Aug 23 09:18:26 mailin postfix/smtpd[1398]: smtp_stream_setup: maxtime=300 enable_deadline=0 min_data_rate=0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: watchdog_pat: 0x39c19dc098d0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 16 flush 155
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 16 got 10
Aug 23 09:18:26 mailin postfix/smtpd[1398]: < kjpetrie.plus.com[84.92.47.176]: STARTTLS
Aug 23 09:18:26 mailin postfix/smtpd[1398]: query milter states for other event
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_other_event: milter unix:/tmp/opendkim.socket
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 502 5.5.1 Error: command not implemented
Aug 23 09:18:26 mailin postfix/smtpd[1398]: smtp_stream_setup: maxtime=300 enable_deadline=0 min_data_rate=0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: watchdog_pat: 0x39c19dc098d0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 16 flush 42
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 16 got 321
Aug 23 09:18:26 mailin postfix/smtpd[1398]: < kjpetrie.plus.com[84.92.47.176]: ????<?
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_string: smtpd_forbidden_commands: ????<? ~? connect
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_string: smtpd_forbidden_commands: ????<? ~? get
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_string: smtpd_forbidden_commands: ????<? ~? post
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_string: smtpd_forbidden_commands: ????<? ~? regexp:{{/^[^A-Z]/ Bogus}}(0,lock|utf8_request)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: dict_regexp_lookup: {{/^[^A-Z]/ Bogus}}: ????<?
Aug 23 09:18:26 mailin postfix/smtpd[1398]: warning: non-SMTP command from kjpetrie.plus.com[84.92.47.176]: \026\003\001\001<\001\000\0018\003\003?.\262\027$\204M\321lP\324\271j\242HYp\261\370WzG\351>(\t\3544
Aug 23 09:18:26 mailin postfix/smtpd[1398]: > kjpetrie.plus.com[84.92.47.176]: 221 2.7.0 Error: I can break rules, too. Goodbye.
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_hostname: smtpd_client_event_limit_exceptions: kjpetrie.plus.com ~? 127.0.1.3/32
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_hostaddr: smtpd_client_event_limit_exceptions: 84.92.47.176 ~? 127.0.1.3/32
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: kjpetrie.plus.com: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: match_list_match: 84.92.47.176: no match
Aug 23 09:18:26 mailin postfix/smtpd[1398]: send attr request = disconnect
Aug 23 09:18:26 mailin postfix/smtpd[1398]: send attr ident = smtp:84.92.47.176
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 17 flush 44
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_buf_get_ready: fd 17 got 10
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: status
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: status
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute value: 0
Aug 23 09:18:26 mailin postfix/smtpd[1398]: private/anvil: wanted attribute: (list terminator)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: input attribute name: (end)
Aug 23 09:18:26 mailin postfix/smtpd[1398]: abort all milters
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_abort: abort milter unix:/tmp/opendkim.socket
Aug 23 09:18:26 mailin postfix/smtpd[1398]: disconnect event to all milters
Aug 23 09:18:26 mailin postfix/smtpd[1398]: milter8_disc_event: quit milter unix:/tmp/opendkim.socket
Aug 23 09:18:26 mailin postfix/smtpd[1398]: vstream_fflush_some: fd 18 flush 16
Aug 23 09:18:26 mailin postfix/smtpd[1398]: disconnect from kjpetrie.plus.com[84.92.47.176] ehlo=1 starttls=0/1 unknown=0/1 commands=1/3
Aug 23 09:18:26 mailin postfix/smtpd[1398]: free all milters
Aug 23 09:18:26 mailin postfix/smtpd[1398]: free milter unix:/tmp/opendkim.socket
 
No SSL certificates are presented by mailin.evenas.org, confirmed by openssl s_client

On a root shell, what's the output of the following commands?
postconf -nf smtpd_tls_chain_files
postconf mail_version
 
I tested my postfix TLS config using <https://luxsci.com/smtp-tls-checker/>. They can only check your incoming smtp server, of course, so if you've split incoming and outgoing it's not definitive.

Just tested that ssl checker I haven't heard before for one of our primary MX:
Code:
LuxSci does NOT support forced TLS with this domain.

Ok, let's try a secondary MX (with opportunistic TLS):
Code:
Add DNS MX records for this domain.
How Can You Improve Your Grade?
Speak with one of our secure email specialists. Free consultation or talk with an expert

How funny for those two MXs alone handling ten of thousands emails per day since 18 years :)

Here's a reliable ssl checker: https://ssl-tools.net/mailservers/
 
Thanks.

Code:
# postconf -nf smtpd_tls_chain_files
smtpd_tls_chain_files =
    /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/privkey.pem,
    /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/fullchain.pem

# ls -l /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/
total 20
-rw-------  1 root wheel 1627 Aug 23 01:33 cert.pem
-rw-------  1 root wheel 3526 Aug 23 01:33 chain.pem
-rw-------  1 root wheel 5153 Aug 23 01:33 fullchain.pem
-rw-------  1 root wheel  288 Aug 23 01:33 privkey.pem

The certificate is a multi-domain one with mailin.evenas.org as one of the domains it's valid for. Could that be a problem?

And the TLS entries in main.cf:
Code:
# tail /usr/local/etc/postfix/main.cf
message_size_limit = 104857600

#TLS
smtpd_tls_CAfile = /usr/local/etc/ssl/cert.pem
smtpd_tls_chain_files = /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/privkey.pem, /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/fullchain.pem
smtp_tls_loglevel = 2
smtp_tls_security_level = may

smtpd_tls_session_cache_timeout = 0s
 
Hello,
I have used STARTTLS and it is running at port 25. No need to forward to other ports.

I'll leave you a piece of my configuration for TLS.

Code:
#TLS
smtpd_use_tls = yes
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/pki/tls/certs/postfix-public.cert
smtpd_tls_key_file = /etc/pki/tls/private/postfix-private.key
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_tls_cache
tls_random_source = dev:/dev/urandom

#TLSv1 or better
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2, !SSLv3

smtpd_tls_exclude_ciphers = EXP, MEDIUM, LOW, DES, 3DES, SSLv2
tls_high_cipherlist = kEECDH:+kEECDH+SHA:kEDH:+kEDH+SHA:+kEDH+CAMELLIA:kECDH:+kECDH+SHA:kRSA:+kRSA+SHA:+kRSA+CAMELLIA:!aNULL:!eNULL:!SSLv2:!RC4:!MD5:!DES:!EXP:!SEED:!IDEA:!3DES
tls_medium_cipherlist = kEECDH:+kEECDH+SHA:kEDH:+kEDH+SHA:+kEDH+CAMELLIA:kECDH:+kECDH+SHA:kRSA:+kRSA+SHA:+kRSA+CAMELLIA:!aNULL:!eNULL:!SSLv2:!MD5:!DES:!EXP:!SEED:!IDEA:!3DES

smtpd_tls_ciphers = high

I've been doing this for a long time ago on servere postfix itself.
Now I use F5 BIG-IP for balancing and all certificates are on BIGIP.
 
Thank you. I'll take a look at those if the below doesn't help.

I ran
[URL='https://www.postfix.org/postfix-tls.1.html']postfix tls[/URL] enable-server
to see what it would do and it added the following:
Code:
smtpd_tls_cert_file = /usr/local/etc/postfix/cert-20260823-203531.pem
smtpd_tls_key_file = /usr/local/etc/postfix/key-20260823-203531.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_security_level = may
tls_random_source = dev:/dev/urandom

I commented out the loglevel because I've already set a higher one but left the rest as is and ran the test again:
Code:
# openssl s_client -connect mailin.evenas.org:25 -starttls smtp -crlf
Connecting to 127.0.1.3
CONNECTED(00000003)
depth=3 C=US, O=Internet Security Research Group, CN=ISRG Root X2
verify return:1
depth=2 C=US, O=ISRG, CN=Root YE
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=YE2
verify return:1
depth=0 CN=diversediversity.org.uk
verify return:1
---
Certificate chain
 0 s:CN=diversediversity.org.uk
   i:C=US, O=Let's Encrypt, CN=YE2
   a:PKEY: EC, (secp384r1); sigalg: ecdsa-with-SHA384
   v:NotBefore: Aug 21 23:36:21 2026 GMT; NotAfter: Nov 19 23:36:20 2026 GMT
 1 s:C=US, O=Let's Encrypt, CN=YE2
   i:C=US, O=ISRG, CN=Root YE
   a:PKEY: EC, (secp384r1); sigalg: ecdsa-with-SHA384
   v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
 2 s:C=US, O=ISRG, CN=Root YE
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   a:PKEY: EC, (secp384r1); sigalg: ecdsa-with-SHA384
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
 3 s:C=US, O=Internet Security Research Group, CN=ISRG Root X2
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
   a:PKEY: EC, (secp384r1); sigalg: sha256WithRSAEncryption
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
...

That looks much better and it's using my certificate rather than the self-signed ones so now I can comment out the lines for those and move the other lines around to fit what I'd already done and keep checking I haven't broken it again and then we'll see what happens. I expect it's the tls_random_source = dev:/dev/urandom line I was missing, but the README didn't seem to suggest it was needed.

I'll keep people posted.
 
Thanks.

Code:
# postconf -nf smtpd_tls_chain_files
smtpd_tls_chain_files =
    /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/privkey.pem,
    /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/fullchain.pem

# ls -l /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/
total 20
-rw-------  1 root wheel 1627 Aug 23 01:33 cert.pem
-rw-------  1 root wheel 3526 Aug 23 01:33 chain.pem
-rw-------  1 root wheel 5153 Aug 23 01:33 fullchain.pem
-rw-------  1 root wheel  288 Aug 23 01:33 privkey.pem

The certificate is a multi-domain one with mailin.evenas.org as one of the domains it's valid for. Could that be a problem?

No as your TLS includes a SAN (Subject Alternative Name) for mailin.evenas.org:

Bash:
openssl s_client -connect diversediversity.org.uk:443 \
  -servername diversediversity.org.uk </dev/null 2>/dev/null |
openssl x509 -noout -subject -issuer -dates -ext subjectAltName
subject=CN=diversediversity.org.uk
issuer=C=US, O=Let's Encrypt, CN=YE2
notBefore=Aug 21 23:36:21 2026 GMT
notAfter=Nov 19 23:36:20 2026 GMT
X509v3 Subject Alternative Name:
    DNS:diversediversity.org.uk, DNS:diversity.kjpetrie.co.uk, DNS:evenas.org, DNS:kjpetrie.co.uk, DNS:mailin.evenas.org, DNS:pclos.kjpetrie.co.uk, DNS:sd-forward.org.uk, DNS:www.diversediversity.org.uk, DNS:www.evenas.org, DNS:www.kjpetrie.co.uk, DNS:www.sd-forward.org.uk

And the TLS entries in main.cf:
Code:
# tail /usr/local/etc/postfix/main.cf
message_size_limit = 104857600

#TLS
smtpd_tls_CAfile = /usr/local/etc/ssl/cert.pem
smtpd_tls_chain_files = /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/privkey.pem, /usr/local/etc/letsencrypt/archive/diversediversity.org.uk/fullchain.pem
smtp_tls_loglevel = 2
smtp_tls_security_level = may

smtpd_tls_session_cache_timeout = 0s

smtpd_tls_chain_files is only available since postfix >= 3.4 so can you please show us what's version of postfix you are using with postconf mail_version because previously TLS certificates and keys were set with smtpd_tls_cert_file and smtpd_tls_key_file
 
That looks much better and it's using my certificate rather than the self-signed ones so now I can comment out the lines for those and move the other lines around to fit what I'd already done and keep checking I haven't broken it again and then we'll see what happens. I expect it's the tls_random_source = dev:/dev/urandom line I was missing, but the README didn't seem to suggest it was needed.

Great! postfix-tls(1) is a new welcoming helper I forgot to check and you can mostly blindly trust it as it is from Viktor Dukhovni (a trusted postfix dev for all tls/ssl related thingies).

As for tls_random_source, on FreeBSD it is already a default but you can leave it on your /usr/local/etc/postfix/main.cf:
Bash:
postconf tls_random_source
tls_random_source = dev:/dev/urandom
 
Code:
 postconf mail_version
mail_version = 3.11.5
, hence the use of my files rather than the self-signed ones.

The proof of the pudding will be whether it works when TLS mail is received. The forum seems to use TLS internally:
Code:
Received: from forums.freebsd.org (localhost [127.0.0.1])
    by forums.freebsd.org (8.18.1/8.18.1) with ESMTPS id 67NLcGuI036977
    (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO)
    for <xxxx@yyyy>; Sun, 23 Aug 2026 17:38:16 -0400 (EDT)

Does it use it externally when it sends mail out, as I'm not getting a similar header added by my server on receiving the mail?

Oh wow, sending a test message from my own outgoing client machine:
Code:
Received: from zzzz (kjpetrie.plus.com [84.92.47.176])
    (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
     key-exchange X25519MLKEM768 server-signature ECDSA (secp384r1) server-digest SHA384)
    (No client certificate requested)
    by mailin.evenas.org (Postfix) with ESMTPS id 98A491398A
    for <xxxx@yyyy>; Sun, 23 Aug 2026 23:26:48 +0100 (BST)
 
Have you solved the problem?
if possible, post your configuration file to see, please.
Maybe this configuration will be useful to someone.
 
I still have it set up for debugging so it needs a bit of tweaking and testing to move it to a better production configuration. The order of some of the parameters seems to matter as well.

The current debugging (file paths removed - no point making it easier for an intruder to find them) configuration is:
Code:
#TLS
smtpd_tls_CAfile = /usr/local/etc/ssl/cert.pem
smtpd_tls_chain_files = /usr/local/etc/...../privkey.pem, /usr/local/etc/....../fullchain.pem
smtp_tls_loglevel = 2

smtpd_tls_session_cache_timeout = 0s

smtpd_tls_received_header = yes
smtpd_tls_security_level = may
tls_random_source = dev:/dev/urandom

I need to reduce the loglevel to 0 or 1 and re-enable the session cache (by removing the entry) but I need time to test and ensure it still works.
 
Code:
 postconf mail_version
mail_version = 3.11.5
, hence the use of my files rather than the self-signed ones.

Thank you.

Then smtpd_tls_chain_files can be used and is now prefered over old deprecated smtpd_tls_cert_file and smtpd_tls_key_file knobs.

The proof of the pudding will be whether it works when TLS mail is received. The forum seems to use TLS internally:
Code:
Received: from forums.freebsd.org (localhost [127.0.0.1])
    by forums.freebsd.org (8.18.1/8.18.1) with ESMTPS id 67NLcGuI036977
    (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO)
    for <xxxx@yyyy>; Sun, 23 Aug 2026 17:38:16 -0400 (EDT)

Does it use it externally when it sends mail out, as I'm not getting a similar header added by my server on receiving the mail?

It does. This envelope header snippet regarding TLS informations is added by your smtpd(8) with smtpd_tls_received_header = yes

Oh wow, sending a test message from my own outgoing client machine:
Code:
Received: from zzzz (kjpetrie.plus.com [84.92.47.176])
    (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
     key-exchange X25519MLKEM768 server-signature ECDSA (secp384r1) server-digest SHA384)
    (No client certificate requested)
    by mailin.evenas.org (Postfix) with ESMTPS id 98A491398A
    for <xxxx@yyyy>; Sun, 23 Aug 2026 23:26:48 +0100 (BST)

This envelope header tells the following:
  1. This indicates that the SMTP connection between kjpetrie.plus.com [84.92.47.176] and mailin.evenas.org was encrypted using TLS 1.3 (AES-256-GCM, X25519MLKEM768 keys exchange and ECDSA P-384 server signature) ;
  2. mailin.evenas.org server did not request a TLS client certificate to kjpetrie.plus.com (this is not unusual for incoming SMTP. It simply means that mutual authentication via certificate is not used.).
Checking your incoming MX (mailin.evenas.org):

Code:
openssl s_client -connect mailin.evenas.org:25 -starttls smtp -crlf
CONNECTED(00000003)
depth=3 C = US, O = Internet Security Research Group, CN = ISRG Root X2
verify return:1
depth=2 C = US, O = ISRG, CN = Root YE
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = YE2
verify return:1
depth=0 CN = diversediversity.org.uk
verify return:1
---
Certificate chain
 0 s:CN = diversediversity.org.uk
   i:C = US, O = Let's Encrypt, CN = YE2
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Aug 21 23:36:21 2026 GMT; NotAfter: Nov 19 23:36:20 2026 GMT
 1 s:C = US, O = Let's Encrypt, CN = YE2
   i:C = US, O = ISRG, CN = Root YE
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
 2 s:C = US, O = ISRG, CN = Root YE
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X2
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
 3 s:C = US, O = Internet Security Research Group, CN = ISRG Root X2
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
   v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
subject=CN = diversediversity.org.uk
issuer=C = US, O = Let's Encrypt, CN = YE2
---
No client certificate CA names sent
Peer signing digest: SHA384
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 4314 bytes and written 436 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 384 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
250 CHUNKING
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: [...]
    Resumption PSK: [...]
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    [...]


    Start Time: 1787572394
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: [...]
    Session-ID-ctx:
    Resumption PSK: [...]
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    [...]


    Start Time: 1787572394
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
ehlo client.example.net
250-mailin.evenas.org
250-PIPELINING
250-SIZE 104857600
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250-CHUNKING
250 REQUIRETLS
quit
221 2.0.0 Bye
closed

You MX does offer TLS. Good.

One last word: dont enforce TLS (REQUIRETLS is announced by your server) as this can break incoming mail flow from smtp clients and local mail stack (see and follow RFC2487)
 
Back
Top