LINUXOR.SK ... open source notes ...

Email 08 - TLS and certificates

category: solutionz · date: 2019-12-31 · updated: 2026-10-02 · author: LALA

Email Solution · Previous: Dovecot authentication · Next: Automatic email encryption

Four requirements of the design are about encryption in transit: clients should reach the SMTP server over a secured channel (FR3), the webmail is HTTPS only (FR6), the directory is queried over LDAPS (FR8), and every certificate comes from the organisation's internal certification authority (FR9). Because the clients authenticate with plaintext mechanisms (see Dovecot authentication), TLS towards the clients is not an extra here but the thing the passwords depend on. This Article goes through every connection of the system and says what protects it as configured, which is not in every case what the design says.

The connections

mermaid
flowchart LR
  cli["Email clients"]
  br["Browser"]
  subgraph pair["Internal pair behind the VIP"]
    pf["Postfix, ports 25 and 465"]
    dv["Dovecot auth"]
  end
  subgraph mbx["DC2-A-VCMSX002"]
    pf2["Postfix"]
    web["Apache and Roundcube"]
    im["Dovecot IMAP"]
  end
  rel["Relays"]
  inet["Mail servers on the internet"]
  ad["Domain controllers"]
  cli -->|"STARTTLS required, or SMTPS"| pf
  pf -->|"STARTTLS, required by the receiver"| pf2
  pf -->|"SMTP without TLS"| rel
  rel -->|"STARTTLS if offered"| inet
  pf -->|"LDAPS 636"| ad
  dv -->|"LDAPS 636"| ad
  pf2 -->|"LDAPS 636"| ad
  br -->|"HTTPS, TLS 1.2"| web
  web -->|"SMTPS on 127.0.0.1"| pf2
  web -->|"IMAP on 127.0.0.1, no TLS"| im
ConnectionProtection as configuredPeer certificate verified
Client to internal server, port 25STARTTLS mandatoryby the client, if it does
Client to internal server, port 465TLS from the first byteby the client, if it does
Internal pair to mailbox server and backSTARTTLS, because the receiving side demands itno
Internal pair to relaysnone: the relays offer no STARTTLSnot applicable
Relays to the internetSTARTTLS when the other side offers itno
Postfix and Dovecot to Active DirectoryLDAPS, port 636Postfix: no, the LDAP maps set no tls_require_cert and the default is not to check; Dovecot: not visible in the Source material
Browser to webmailHTTPS, TLS 1.2 onlyby the browser
Roundcube to Postfix and DovecotSMTPS and plain IMAP on the loopback interfacesee Webmail

The internal CA and the certificates

Every server has one certificate, with its own FQDN as subject. The two nodes of the internal pair carry the name of the virtual address as subject alternative name in addition, because that name is what the clients are configured with (see High availability and key synchronization): whichever node holds the VIP must present a certificate that matches it.

Site 2, the worked example:

ServerSubjectAlternative subject
DC2-A-VCMSX001dc2-a-vcmsx001.adm.example.netdc2-s-xcmsx001.adm.example.net
DC2-B-VCMSX001dc2-b-vcmsx001.adm.example.netdc2-s-xcmsx001.adm.example.net
DC2-A-VCMSX002dc2-a-vcmsx002.adm.example.netnone
DC2-A-VCMSR001dc2-a-vcmsr001.adm.example.netnone
DC2-B-VCMSR001dc2-b-vcmsr001.adm.example.netnone

Site 1 is known from the design only, which gives the same table with the other site code:

ServerSubjectAlternative subject
DC1-A-VCMSX001dc1-a-vcmsx001.adm.example.netdc1-s-xcmsx001.adm.example.net
DC1-B-VCMSX001dc1-b-vcmsx001.adm.example.netdc1-s-xcmsx001.adm.example.net
DC1-A-VCMSX002dc1-a-vcmsx002.adm.example.netnone
DC1-A-VCMSR001dc1-a-vcmsr001.adm.example.netnone
DC1-B-VCMSR001dc1-b-vcmsr001.adm.example.netnone

The design is not consistent about the VIP name. Its certificate tables and its component tables write dc2-s-vcmsx001 and dc1-s-vcmsx001; the Keepalived section, the client settings and the install notes write dc2-s-xcmsx001 and dc1-s-xcmsx001. The tables above use the second form, because that is the name that went into the certificate request.

All five servers keep the files in the same place: the key in /etc/pki/tls/private/<FQDN>.key, the certificate in /etc/pki/tls/certs/<FQDN>.crt. On the mailbox server Apache also reads <FQDN>.chain.crt from the same directory.

Key, request, certificate

The procedure is the same in all five install notes. All commands ran as root. On the two nodes of the pair, the OpenSSL configuration was first taught to put the VIP name into requests, by an edit of /etc/pki/tls/openssl.cnf:

ini
[ req ]
...
# CFG - ADD - request extention - subject alternative names
req_extensions = req_ext
...
# CFG - ADD - subject alternative names
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = dc2-s-xcmsx001.adm.example.net

Then the key and the request:

bash
$ export FQDN=dc2-a-vcmsx001.adm.example.net
$ openssl genrsa -out /etc/pki/tls/private/$FQDN.key 2048
$ openssl req -new -out /etc/pki/tls/private/$FQDN.csr -key /etc/pki/tls/private/$FQDN.key

openssl req asked its questions; the common name was the FQDN of the server, the organisation and a functional mailbox of the security team filled the rest, and the challenge password stayed empty. The key is a 2048-bit RSA key without a passphrase, which Postfix requires. The notes say only that the request is sent to the CA administrator for signing. They do not show how it was transported, how the signed certificate came back, or with which owner and mode key and certificate were stored. They show the checks:

bash
$ openssl req -text -noout -verify -in /etc/pki/tls/private/$FQDN.csr
$ openssl rsa -in /etc/pki/tls/private/$FQDN.key -check
$ openssl x509 -in /etc/pki/tls/certs/$FQDN.crt -text -noout

One detail of the request deserves a second look. The alternative names section lists the VIP name only; the server's own name is in the common name and nowhere else. A client that follows the current rules ignores the common name as soon as a certificate has alternative names, and would then accept the certificate for the VIP name and reject it for the node name. Whether the CA added the node name when signing cannot be told: the certificates are not part of the Source material, and they would not be published here anyway.

The same notes generate Diffie-Hellman parameters:

bash
$ cd /etc/pki/tls/certs/
$ openssl dhparam -2 -out dh_512.pem 512
$ openssl dhparam -2 -out dh_1024.pem 1024
$ openssl dhparam -2 -out dh_2048.pem 2048

Three files are made and two are used. main.cf refers to dh_512.pem and dh_1024.pem; nothing refers to dh_2048.pem.

Postfix on the internal servers

The TLS block of main.cf is the same on the three internal servers except for the names of the certificate and key files. Its core:

ini
smtpd_tls_security_level = encrypt
smtpd_tls_auth_only = yes
smtpd_tls_cert_file = /etc/pki/tls/certs/dc2-a-vcmsx001.adm.example.net.crt
smtpd_tls_key_file = /etc/pki/tls/private/dc2-a-vcmsx001.adm.example.net.key
smtpd_tls_dh512_param_file = /etc/pki/tls/certs/dh_512.pem
smtpd_tls_dh1024_param_file = /etc/pki/tls/certs/dh_1024.pem
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2, !SSLv3
smtp_tls_security_level = may
SettingValueWhat it does here
smtpd_tls_security_levelencryptport 25 announces STARTTLS and refuses mail commands until it is used
smtpd_tls_auth_onlyyesAUTH is offered inside TLS only
smtpd_tls_CAfilecommented outno CA file; Postfix sends the server certificate alone
smtpd_tls_loglevel0no TLS logging; the comment says to set 2 for debugging
smtpd_client_new_tls_session_rate_limit10at most ten new TLS handshakes per client and time unit
smtpd_tls_session_cache_databasebtree:/etc/postfix/smtpd_session_cachesession cache, placed in the configuration directory
smtpd_tls_exclude_ciphersEXP, EDH-RSA-DES-CBC-SHA, ADH-DES-CBC-SHA, DES-CBC-SHA, SEED-SHAexport, single DES and SEED suites removed
smtpd_tls_protocols and _mandatory_protocols!SSLv2, !SSLv3TLS 1.0 and later
smtp_tls_security_levelmayas a client, use TLS when the next server offers it

encrypt on port 25 is something a public mail exchanger must not do, since it would lose mail from servers without TLS. Here port 25 is a submission port for known clients inside the management infrastructure, and the setting does what FR3 asks. It has a consequence for the fourth kind of client the design lists, the appliance with neither TLS nor authentication (FR4): an entry in mynetworks spares such a client the login, but not the STARTTLS. As archived, mynetworks on the pair contains the two relays and the mailbox server and no such client.

The cipher grade parameters (smtpd_tls_ciphers, smtpd_tls_mandatory_ciphers) are not set, so the defaults of Postfix 2.10 apply, less the five excluded names. The protocol lines carry the comment "Turning off SSLv2 / v3 due to the DROWN attack"; they leave TLS 1.0 and 1.1 enabled. The Source material gives no reason; I assume it was for the sake of older clients.

Port 465 is the same smtpd with TLS wrapped around the whole session, defined in master.cf:

ini
smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes

The design calls SMTPS the preferred way and STARTTLS on 25 the alternative. The whole files are Config documents: Postfix main.cf, internal servers, Postfix master.cf, internal servers and, for the third server, Postfix main.cf, mailbox server.

Between the internal servers the two settings meet. The pair forwards mail for the site's own domain to the mailbox server as a client with may; the mailbox server receives with encrypt; so that hop is always encrypted, and the same holds in the other direction for mail from the webmail. Nothing verifies the certificate of the other side: there is no smtp_tls_CAfile and the level is not verify.

The relays

The design lists a certificate and a key for each relay, and the install notes of the relays generate both, under the heading "Certificates (currently not used on this server, but certificate is generated)". The heading is accurate. /etc/postfix/main.cf of DC2-A-VCMSR001 contains these TLS lines and no others:

ini
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
smtp_tls_protocols = !SSLv2, !SSLv3
lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3
lmtp_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2, !SSLv3
smtp_tls_security_level = may

There is no smtpd_tls_cert_file and no smtpd_tls_security_level, so the relay does not offer STARTTLS to the internal pair, and the smtpd_tls_* protocol lines have nothing to act on. Mail travels from the pair to the relay unencrypted, inside one subnet of the management infrastructure. Towards the internet the relay is a client with smtp_tls_security_level = may: it encrypts when the receiving server offers STARTTLS and delivers in the clear when it does not, without verifying certificates. That is ordinary opportunistic TLS, and it is all the protection the message has in transit unless the content filter has encrypted its body (see Automatic email encryption). The file is identical on DC2-B-VCMSR001 apart from the host name and the spelling of mynetworks: Postfix main.cf, relay servers; the servers are described in Relay servers.

Webmail

The webmail is the only place where a person with a browser meets one of these certificates. The Apache virtual host on DC2-A-VCMSX002 uses the server's certificate, key and a chain file, allows TLS 1.2 only (SSLProtocol -all +TLSv1.2), restricts the suites to EECDH+AESGCM with the server's order, and switches compression off. It is a stricter profile than the one on the SMTP ports, because browsers could be expected to keep up and appliances could not. The details are in Webmail and in Apache virtual host, IPv4.

LDAPS to Active Directory

Every lookup goes to the two domain controllers of the site on port 636. The Postfix tables say server_host = ldaps://10.12.16.209:636, ldaps://10.12.16.210:636 with start_tls = no, the Dovecot files say the same in uris: TLS from the first byte, never an upgrade of a plain connection, and IPv4 addresses instead of names because of the trouble LDAP libraries had on dual-stack hosts (see Active Directory integration).

How the certificate of a domain controller is trusted is the part I cannot document. None of the LDAP files sets a CA file or a verification level: no tls_ca_cert_file and no tls_require_cert, neither in the Postfix tables nor in the Dovecot files. The archived trees contain neither /etc/openldap/ldap.conf nor the system trust store, and the install notes have no step that installs the CA certificate. Two things can be said. For the Postfix tables, ldap_table(5) gives tls_require_cert the default no, which means the certificate is not checked at all. For Dovecot the decision is left to the OpenLDAP library and its configuration file, which I do not have. And since the servers are addressed by IP address, a strict check would only pass if the certificates of the domain controllers carried those addresses. The channel is encrypted; that the other end is authenticated is not shown by the Source material.

Dovecot's own TLS settings are a short story: ssl = no on all three servers, with certificate paths that name a server of site 1 (see 10-ssl.conf). Dovecot has no listener that needs TLS: the SASL socket is a UNIX socket and IMAP is used from the same host.

No client certificates

TLS can authenticate the client as well, and for machine accounts that would be the stronger method. The design rules it out in one sentence: client certificates are not used "because many email clients which are deployed in the infrastructure do not support TLS client certificates for authentication purposes". The same population of appliances that forced PLAIN and LOGIN decided this too. Accordingly nothing asks for one: no smtpd_tls_ask_ccert in Postfix, and the commented smtpd_tls_CAfile would only have been needed for that.

What changed since

Checked against Postfix 3.11.7, Dovecot 2.4.5 and Apache httpd 2.4.69, the current releases in October 2026.

As builtToday
smtpd_tls_dh512_param_fileexport-grade Diffie-Hellman is gone; the parameter is silently ignored since Postfix 3.6
smtpd_tls_dh1024_param_filedeprecated since 3.9; left empty, OpenSSL 3 picks the group
!SSLv2, !SSLv3still accepted; written today as a lower bound, >=TLSv1.2. The as-built lists allow TLS 1.0 and 1.1
smtpd_tls_cert_file and smtpd_tls_key_filestill valid; smtpd_tls_chain_files is preferred since 3.4
smtpd_tls_auth_only = yes next to encryptstill valid, and redundant on a port that already demands TLS
smtpd_tls_exclude_ciphersstill valid; upstream: do not exclude ciphers unless it is essential
btree: session cacheBerkeley DB is not in RHEL 10; Postfix 3.11 has default_cache_db_type
smtp_tls_security_level = maythe default since 3.11
relays without server TLSunchanged: a Postfix server offers STARTTLS only with an explicit key and certificate
port 465 as smtpsthe stock master.cf names the service submissions
LDAP tables without tls_require_certthe default is still no: the trust chain of the server certificate is not checked
Dovecot ssl_cert, ssl_keyrenamed ssl_server_cert_file, ssl_server_key_file; LDAP in 2.4 verifies the server certificate by default
Apache SSLProtocol -all +TLSv1.2valid, and it excludes TLS 1.3
Apache SSLCertificateChainFiledeprecated since httpd 2.4.8

Two of these rows confirm what the sections above read from the files. The Postfix LDAP tables did not verify the certificate of the domain controllers, in 2019 as today, because that is the default and the files do not change it. And the pair-to-relay hop was unencrypted by construction, not by oversight of a single parameter. On the client side Postfix has since gained per-destination policies (smtp_tls_policy_maps), DANE, MTA-STS through a policy plugin, TLS reporting (3.10) and REQUIRETLS (3.11); the relays use none of them. OpenSSL 1.0.2, which did the work in 2019, is no longer supported.

← solutionz