Internal LAN mail-server
02 June 2025Although I successfully experimented setting up my own mail-server with redundancy on a pair of VPNs it is not a setup I would want to use in production, instead preferring to run my mail-server on one of the machines within my server cabinet, so described here is an alternative architecture I have also tried out. Rather than third-parties connecting via SMTP directly emails are relayed in via a pair of VPNs act as the public-facing SMTP interfaces that have a locked-down channel to the internal mail-server. There are several partly overlapping reasons for this architecture:
- Little faith in VPS suitability
- I do not think a VPS can match either the reliability nor the security that one of my own machines would have and past trouble with VPSs going down with little or no notice does not fill me with confidence. My mailboxes run into the gigabytes which is fine with dedicated hosts who deal with everything for you — for a fee — but not with a VPS without automatic backups up and which might have to suddenly be rebuilt one day.
- No direct external access
- My LAN's external gateway address is not something I want to become public knowledge by being listed in DNS MX records, and I am not fond either of opening up ports for use by the general public as this invites attacks. Therefore the internal mail-server is only to be accessed by the external relays rather than up-stream third-party servers.
- Universal PKI use
- For connections both to my LAN and within it is policy to use certificate-based authentication wherever possible. I only use passwords for securing private keys.
- Rarely access emails externally
- Even when off-site which is more often than not I usually access emails using an email client running back in my apartment. These days I am making ever greater use of remote desktops and head-less servers.
Client-side certificate generation
Aside from changing the certificate indentity parameters to something more appropriate the script used for generating key sets for VPN can be used as-is for generating client certificate for use between Postfox instances. Of the generated files onlycacert.pem
, client.cert.pem
, and client.key.pem
are needed by the servers, and the cut-down Makefile below generates ECDSA versions of all of these:
IPSEC=/usr/sbin/ipsec KEYTYPE=--type ecdsa KEYSIZE=--size 521 all: cacert.pem client.cert.pem ## Certificate Authority cakey.pem: $(IPSEC) pki --gen $(KEYTYPE) $(KEYSIZE) --outform pem > $@ $(IPSEC) pki --print $(KEYTYPE) --in $@ cacert.pem: cakey.pem $(IPSEC) pki --self --in $< \ --dn "C=UK, OU=LAN, O=Mail, CN=Mail CA" \ --ca --outform pem > $@ $(IPSEC) pki --print --in $@ ## Client key .SECONDARY: $(%.key.pem) %.key.pem: $(IPSEC) pki --gen $(KEYTYPE) $(KEYSIZE) --outform pem > $@ %.key.pub: %.key.pem $(IPSEC) pki --pub --in $< --outform pem > $@ ## Client cert client.cert.pem: client.key.pub $(IPSEC) pki --issue --cacert cacert.pem --cakey cakey.pem \ --dn "C=UK, OU=LAN, O=Mail, CN=Mail Client" \ --in $< --outform pem > $@
If you are unsure whether a private key and certificate go together, the following script will check this by comparing the public key extracted from them:
#!/bin/bash if [[ $# -ne 2 ]]; then echo "USAGE: ${0} <cert.pem> <key.pem>" exit fi if ! test -f ${1}; then echo "Certificate ${1} does not exist" exit fi if ! test -f ${2}; then echo "Private key ${2} does not exist" exit fi diff -q <(openssl x509 -in "${1}" -pubkey | sed -n -e '/-----BEGIN CERT/,$!p') \ <(openssl ec -in "${2}" -pubout 2>/dev/null) >/dev/null if [[ $? -eq 0 ]]; then echo "Certificate and key pair match" else echo "Certificate and key do not go together" fi
Setting up the internal mail-server
The internal server is installed on a dedicated machine within my server cabinet and the SMTP port is forwarded to it by my RaspberryPi gateway, which only accepts connections from the external relays. Since the LAN is behind a NAT gateway the configuration used previously for the master needs modification and these changes are all within the first batch of five lines, and the second batch of four lines configure the tightened security with the enforced use of client certificates.myhostname = mailsrv.lan mydomain = example.com myorigin = $mydomain proxy_interfaces = smtp.example.com mydestination = $mydomain, $myhostname, localhost.$mydomain, localhost smtpd_tls_security_level = encrypt tls_append_default_CA = no smtpd_tls_req_ccert = yes smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem mynetworks_style = host local_recipient_maps = unix:passwd.byname $alias_maps virtual_alias_domains = example.com virtual_alias_maps = hash:/etc/postfix/virtual.cfg alias_maps = hash:/etc/aliases alias_database = $alias_maps smtpd_relay_restrictions = permit_mynetworks, defer_unauth_destination smtpd_recipient_restrictions = smtpd_tls_cert_file=/etc/postfix/ssl/fullchain.pem smtpd_tls_key_file=/etc/postfix/ssl/privkey.pem smtpd_tls_auth_only = yes compatibility_level = 3 unknown_local_recipient_reject_code = 550 soft_bounce = no mailbox_command = /usr/bin/procmail -a "$EXTENSION"
Details of the changed and new parameters in these first batches of lines are listed below, whereas details of existing and unchanged parameters have been omitted. Parameters that do not appear at all are assumed to have taken on their default values.
myhostname
mydomain
- The hostname and domain are the ones the machine has within the LAN and are registered with the internal DNS server, since the machine does not have any externally-accessible IP addresses.
myorigin
- Domain for local mail. In practice Postfix itself is likley going to be the only thing actually generating such messages.
proxy_interfaces
- This is the external IP address of the LAN gateway. Unsure of the technical details but Postfix needs to know about it.
mydestination
- Since
$domain
is the internal LAN suffix rather than something under the email domain,example.com
needs to be specified explicitly. smtpd_tls_security_level
- Enforce use of SSL. This should not be done with public-facing mail-servers but this one only takes connections from the public relays.
smtpd_tls_CAfile
- The CA (certificate-authority) certificate used to validate client certificates.
tls_append_default_CA
- Don't send clients the CA certificate as they should not need it.
smtpd_tls_req_ccert
- Enforce use of client certificates. In contrast
smtpd_tls_ask_ccert
gives clients the option of authenticating with one.
Setting up the MX relays
This configuration is much the same as the backup mail-server but with two important differences. Firstly whereas the backup mail-server used the domain's DNS record to find out the identity of the primary mail-server that it should relay mail to, the destination mail-server is instead explicitly listed in the configuration. Secondly a client certificate is needed to access the destination mail-server.myhostname = mx.example.com mydomain = example.com myorigin = $mydomain mynetworks_style = host mydestination = relay_domains = example.com virtual_alias_maps = hash:/etc/postfix/local_addrs.cfg relay_recipient_maps = hash:/etc/postfix/relay_addrs.cfg local_recipient_maps = local_transport = error:local mail delivery is disabled smtpd_relay_restrictions = defer_unauth_destination smtpd_recipient_restrictions = smtpd_tls_cert_file=/etc/postfix/ssl/fullchain.pem smtpd_tls_key_file=/etc/postfix/ssl/privkey.pem smtpd_tls_security_level = may smtpd_tls_auth_only = yes smtp_enforce_tls = yes smtp_tls_CAfile = /etc/postfix/ssl/cacert.pem smtp_tls_eckey_file = /etc/postfix/ssl/client.key.pem smtp_tls_eccert_file = /etc/postfix/ssl/client.cert.pem soft_bounce = no compatibility_level = 3 transport_maps = hash:/etc/postfix/destinations.cfg
See the previous article for details on relay_addrs.cfg
and local_addrs.cfg
.
Note that since this relay is acting as the client it has parameters starting smtp_
and not smtpd_
which was one of the things that caught me out.
The domains that the relay handles are listed in /etc/postfix/destinations.cfg
together with the address of the down-stream mail-servers, and the one used in this case is shown below.
example.com relay:[smtp.example.com]
The square brackets [
& ]
are required since they indicate to Postfix that an MX lookup should not be done and instead the address is used as-is.
This file along with the other .cfg
files may need reindexing by postmap
as for some reason this is not done automatically on daemon startup:
postmap hash:/etc/postfix/local_addrs.cfg postmap hash:/etc/postfix/relay_addrs.cfg postmap hash:/etc/postfix/destinations.cfg
Certificate gotchas
When revisiting the draft of this article the number one thing that caught me out was issues with certificates, with the clues being found by uppingsmtp_tls_loglevel
and looking at /var/log/maillog
log entries.
Once seeing the messages the root cause became very clear and the problem was certificate validity dates, specifically one of the servers I was using thinking the not before date was in the future.
This was due to it having not applied daylight savings adjustment and being an hour fast it was rejecting my newly generated certificate set.
Problems with incorrect dates has caught me out in the past.
Remarks
Although this scenario was tried and documented shortly after the previous one back in August 2024 a spate of interviews and subsequent relocation due to where the job offer was put off its production deployment, and this write-up like many other things fell by the way-side despite being practically complete. Before it gets forgotten once again the procedure was re-tested in a deployment not much different from the original intention and this how-to content finally published. Having said that the article was able to get a polishing that it would not have gotten if it had come out last year.
The relocation has forced a re-think of my whole computer infrastructure strategy, with relying on a server in my cabinet for email becoming a non-starter without drop-off-a-hat access of this time last year. In fact if there is one thing that should be outsourced it is email due to it being my one truly mission-critical thing. Co-location of a backup server in Manchester was thought about but ultimately the logistics were not considered worth-while, particularly at time of publication with the trend of moving stuff back down to London.