E-Mail Forward



Overview

The following diagrams are what this document will accomplish. The challenge is to obtain and keep a good reputation that other E-Mail handlers around the world will accept. Otherwise your messages will be dumped in the trash can (SPAM) or sent back to you (bounced).

It is a combination of:

  • IP Address Reputation
  • DNS assignment of an E-Mail name to that IP Address
  • Certificates for Authentication and Encryption
  • Login protection against Open Spam Relays

A VPS (Virtual Private Server) is a way to obtain a good IP address reputation, and it will stay the same number over time. There are many to choose from, some already have bad reputations and constant streams of hackers knocking on the network, while others have a good reputation and just a few random hackers on your doorstep. Choose wisely!

  • MUA - Mail User Agent; Thunderbird, Evolution - Read, send, user interface
  • MDA - Mail Delivery Agent; Dovecot - File and organize mail, authorize user accounts
  • MTA - Mail Transport Agent; Postfix - Move messsages from one network stop to another

Outgoing: Relay from Inside Home

graph TD;
        Home_E-Mail.MUA-->Home_Postfix.MTA;
        Home_Postfix.MTA-->VPS_Postfix.MTA;
        VPS_Postfix.MTA-->Internet;

Outgoing: Relay from Outside Home

graph TD;
        Mobile_E-Mail.MUA-->VPS_Postfix.MTA;
        VPS_Postfix.MTA<-->VPS_Dovecot.MDA;
        VPS_Dovecot.MDA<-->VPS_Linux;
        VPS_Postfix.MTA-->Internet;

Incoming: Transport from Internet

graph TD;
        Internet-->VPS_Postfix.MTA;
        VPS_Postfix.MTA-->Home_Postfix.MTA;
        Home_Postfix.MTA-->Home_Dovecot.MDA;
        Home_Dovecot.MDA<-->Home_Linux;
        Home_Dovecot.MDA-->Home_E-Mail.MUA;

This document will use:

  • your-domain.org : The E-Mail relay we are setting up in this document (VPS_Postfix)
  • your-domain.com : The E-Mail server set up in the prior document (Home_Postfix)

Recommendation:

  • Bring up a host on a VPS somewhere, and use the Setup Server instructions to secure it.
  • Get a domain name and certificate set up and working.
  • Install postfix/dovecot combo and relay to your main E-Mail host. Test sending out mail too.
  • Install and configure SPF Policy Agent. Test it and make sure all is well.
  • Install and configure OpenDKIM to sign your E-Mails. Ensure it works good.

VPS

Create a VPS Cloud Server, less than $10 month for 1GB RAM, 30GB Disk, 1 CPU, 2TB Bandwidth month.

Criteria:

  1. IP address reputation. May have to test drive to determine IP address. Lookup tools:
  2. Working control panel. Check online reviews, or test drive. Make sure every button works. Extra points if they allow firewall changes and creating a PTR record.
  3. Current OS release. Check references below.
  4. Support availability and response agreements. Test creating a support ticket. Check references below.
  5. Low cost, check references below.

Secure VPS on your-domain.org

Change your root password IMMEDIATELY. Hackers know the algorithm and expliot it from creation time to change time.

Change the ssh port from 22 (File: /etc/ssh/sshd_config). Hackers know that port and constantly attack it.

Check Network

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Update Package Repository

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Create New User(s)

The Linux mail user can be <name>@example.com, to keep things clear.

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Set Host and Domain

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Set Date and Time

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Monitor crack attempts

Install firewall and fail2ban on your-domain.org

Be sure the system firewall is installed and also the "Block Bad Actors" firewall.sh script.

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Add rules for E-Mail smtp and smtps.

  • Debian:
$ sudo apt-get install fail2ban mailutils
  • RedHat:
$ sudo dnf install fail2ban mailx

Make sure firewall rules open port smtp(25) and smtps(465):

  • Debian
$ sudo ufw allow 25
$ sudo ufw allow 465
$ sudo ufw status numbered

     To                         Action      From
     --                         ------      ----
[ 1] 22                         ALLOW IN    Anywhere                  
[ 2] 25                         ALLOW IN    Anywhere                  
[ 3] 465                        ALLOW IN    Anywhere                  
[ 4] 22 (v6)                    ALLOW IN    Anywhere (v6)             
[ 5] 25 (v6)                    ALLOW IN    Anywhere (v6)  

  • RedHat
$ sudo firewall-cmd --add-service=smtp
 success
$ sudo firewall-cmd --add-service=smtps
 success
$ sudo firewall-cmd --list-services
  smtp smtps ssh
$ firewall-cmd --runtime-to-permanent
 success
$ firewall-cmd --reload
 success

Configure fail2ban

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Secure postfix

File: /etc/fail2ban/jail.d/postfix.local

[postfix]
enabled  = true
maxretry = 3
bantime = 1d
port     = smtp,465,submission

[postfix-sasl]
enabled  = true
maxretry = 3
bantime = 1d
port     = smtp,465,submission,imap,imaps,pop3,pop3s

File: /etc/fail2ban/jail.d/dovecot.local

[dovecot]
enabled = true
port    = pop3,pop3s,imap,imaps,submission,465,sieve
logpath = /var/log/maillog
findtime = 1200
bantime = 1d
Secure ssh

File: /etc/fail2ban/jail.d/sshd.local

[sshd]
port = XXXX
enabled = true
maxretry = 3
bantime = 1d

Log Monitors on your-domain.org

Install Logwatch

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Install Logcheck:

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

LogWatcher

Create

Put this script in the /root directory, and install the dependencies listed.

#!/bin/bash
##############################################################################
#
# File: logwatcher.sh
#
# Purpose: Watch for interesting new things in log files and e-mail them
#
# Dependencies: 
#  * Debian:
#   apt-get install git clang zlib1g-dev
#  * RedHat:
#   dnf install git clang zlib-devel 
#
#   git clone https://github.com/mbucc/retail
#
# Author     Date     Description
# ---------- -------- --------------------------------------------------------
# D. Cohoon  Feb-2023 Created
##############################################################################
MAILTO=root
DIR=/root
OS=$(/usr/bin/hostnamectl|/usr/bin/grep 'Operating System'|/usr/bin/cut -d: -f2|/usr/bin/awk '{print $1}')
cd ${DIR}
#--------------
log_check () {
  OFFSET=${1}
  LOGFILE=${2}
  FILTER=${3}
  OUTPUT=$(/usr/bin/mktemp)
  /root/retail/retail -o ${OFFSET} ${LOGFILE} | ${FILTER} >${OUTPUT}
  if [ -s ${OUTPUT} ]; then
    /bin/cat ${OUTPUT} | /usr/bin/mail -s "Logwatcher.sh: ${OFFSET}" ${MAILTO} 2>/dev/null
  fi
  rm -rf ${OUTPUT}
}
#
#--------------
case ${OS} in
    AlmaLinux) 
        FAIL2BAN_LOG=/var/log/fail2ban.log
        POSTFIX_LOG=/var/log/maillog
        AUTH_LOG=/var/log/secure 
        ;;
    Ubuntu|Debian) 
        FAIL2BAN_LOG=/var/log/fail2ban.log
        POSTFIX_LOG=/var/log/syslog
        AUTH_LOG=/var/log/auth.log
        ;;
esac
#           Offset  Log File               Filter
#          -------- ---------------------  -------------------------
log_check .fail2ban "${FAIL2BAN_LOG}"      ${DIR}/filter_fail2ban.sh
#
log_check .postfix  "${POSTFIX_LOG}"       ${DIR}/filter_postfix.sh
#
log_check .sshd     "${AUTH_LOG}"          ${DIR}/filter_ssh.sh
#
log_check .dovecot  "${AUTH_LOG}"          ${DIR}/filter_dovecot.sh

Schedule

# cat /etc/cron.d/logwatcher 
# /etc/cron.d/logwatcher: crontab entries for the logwatcher.sh script

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

@reboot         root   /root/logwatcher.sh 
2 * * * *       root   /root/logwatcher.sh 

# EOF

Filters

File: filter_fail2ban.sh

/usr/bin/grep "Ban"

File: filter_postfix.sh

/usr/bin/grep "postfix"|/usr/bin/grep 'disconnect from unknown'

File: filter_ssh.sh

/usr/bin/grep "ssh" | /usr/bin/egrep "Failed|invalid format"

File: filter_dovecot.sh

/usr/bin/grep "dovecot" | /usr/bin/grep "authentication failure"

Make the Scripts Executable

$ chmod 755 *.sh

Reference:

Apache install (enable) on your-domain.org

Click on this heading, then refer to these instructions, then come back here with the Back Arrow on the Browser:

Test it on http://<domain.name>

Add SSL

  • Debian
$ sudo ufw allow 443/tcp
  • RedHat
$ sudo firewall-cmd --add-service=https
  success
$ sudo firewall-cmd --list-services
  http https smtp smtps ssh
$ sudo firewall-cmd --runtime-to-permanent
  success
$ sudo firewall-cmd --reload
  success

Change the default landing page to blank

File: /var/www/html/index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  </head>
  <body>
    <div class="main_page">
    </div>
  </body>
</html>

DNS Registrar (your-domain.org)

Register name.

https://godaddy.com https://www.namecheap.com/ https://www.domain.com/

DNS Updates

Create Advanced DNS records, using IP Address of VPS server:

HOST

Type       Host        Value               TTL
---------- ----------- ------------------- ----
A Record   @           <IP Address>        Auto
A Record   mail        <IP Address>        Auto
TXT Record @           v=spf1 mx -all      Auto

MAIL

Type       Host        Value               Priority TTL
---------- ----------- ------------------- -------- ----
MX Record  @           <Domain Name>       10       Auto

Lookup status via dig -t <Type> <Domain>

$ dig -t a +short <Domain Name>
123.123.123.123
$ dig -t mx +short <Domain Name>
10 <Domain Name>.

To install dig: $ sudo apt-get install dnsutils $ sudo dnf install bind-utils

Create PTR Record

Using the Control Panel on the VPS, update the PTR record. If they do not allow this, create a support ticket, they will do it then, just trying to limit spammers.

Test your PTR record. Using the IP Address, it should show your domain, proving you have control over the IP Address.

dig -x 1.2.3.4
;; ANSWER SECTION:
1.2.3.4.in-addr.arpa. 86400 IN	PTR	mail.<Domain Name>.

certbot - Certificate Management for Let's Encrypt

To create and maintain LetsEncrypt certificates using Apache web server

Install

$ sudo systemctl unmask apache2
$ sudo systemctl enable apache2
$ sudo systemctl start apache2
$ sudo apt-get install certbot
$ sudo apt-get install python3-certbot-apache
$ sudo systemctl unmask httpd
$ sudo systemctl enable httpd
$ sudo systemctl start httpd
$ sudo dnf install certbot
$ sudo dnf install python3-certbot-apache
$ sudo certbot  plugins

Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* apache
Description: Apache Web Server plugin
Interfaces: IAuthenticator, IInstaller, IPlugin
Entry point: apache = certbot_apache._internal.entrypoint:ENTRYPOINT

* standalone
Description: Spin up a temporary webserver
Interfaces: IAuthenticator, IPlugin
Entry point: standalone = certbot._internal.plugins.standalone:Authenticator

* webroot
Description: Place files in webroot directory
Interfaces: IAuthenticator, IPlugin
Entry point: webroot = certbot._internal.plugins.webroot:Authenticator
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Configure

Be sure to get certificates for the domain and all subdomains (hosts)

$ certbot  --apache -d your-domain.org -d mail.your-domain.org
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator apache, Installer apache
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): you@your-domain.org

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Account registered.
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated)  (Enter 'c' to cancel): your-domain.org
Requesting a certificate for your-domain.org
Performing the following challenges:
http-01 challenge for your-domain.org
Enabled Apache rewrite module
Waiting for verification...
Cleaning up challenges
Created an SSL vhost at /etc/apache2/sites-available/000-default-le-ssl.conf
Enabled Apache socache_shmcb module
Enabled Apache ssl module
Deploying Certificate to VirtualHost /etc/apache2/sites-available/000-default-le-ssl.conf
Enabling available site: /etc/apache2/sites-available/000-default-le-ssl.conf
Enabled Apache rewrite module
Redirecting vhost in /etc/apache2/sites-enabled/000-default.conf to ssl vhost in /etc/apache2/sites-available/000-default-le-ssl.conf

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://your-domain.org
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Subscribe to the EFF mailing list (email: you@your-domain.org).

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/your-domain.org/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/your-domain.org/privkey.pem
   Your certificate will expire on 2023-05-08. To obtain a new or
   tweaked version of this certificate in the future, simply run
   certbot again with the "certonly" option. To non-interactively
   renew *all* of your certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Apache (disable)

Apache is not needed, so disable it to reduce threat footprint.

$ sudo systemctl stop apache2
$ sudo systemctl disable apache2
$ sudo systemctl mask apache2
$ sudo ufw status numbered
# -> ufw delete <n> for ports 80 & 443
$ sudo systemctl stop httpd
$ sudo systemctl disable httpd
$ sudo systemctl mask httpd
$ sudo  firewall-cmd --remove-service=https
  success
$ sudo firewall-cmd --list-services
  http smtp smtps ssh
$ sudo  firewall-cmd --remove-service=http
  success
$ sudo firewall-cmd --list-services
  smtp smtps ssh
$ sudo firewall-cmd --runtime-to-permanent
  success

When the Let's Encrypt Certificate expires, you will need to enable it again and open up ports.

postfix - E-Mail Transport Agent

Install postfix MTA and remove exim4, if it is installed. Postfix is more mature and full-featured.

TLS http://www.postfix.org/TLS_README.html

Install on your-domain.org

$ sudo apt-get remove exim4-base
$ sudo apt-get install postfix
$ sudo dnf remove exim
$ sudo dnf install postfix

Configure on your-domain.org

  • smtp -> outgoing mail
  • smtpd <- incoming mail (daemon)

Complete main.cf file contents

~
mydomain = your-domain.org
myorigin = $mydomain
#myorigin = /etc/mailname
~
Also in RedHat: Log is in /var/log/maillog

File main.cf

#.........................................................................
#
# * smtpd <- incoming mail (daemon)
#
smtpd_tls_cert_file = /etc/letsencrypt/live/your-domain.org/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/your-domain.org/privkey.pem
smtpd_use_tls = yes
smtpd_tls_security_level=may
smtpd_tls_auth_only = yes
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#
# Enforce TLSv1.3 or TLSv1.2
#  smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
#  smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
#  smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
#  smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
#
# Sending relays allowed only if:
# -> you are on mynetworks
# -> you are logged in
#  all others are deferred (temp error 4.x.x), 
#   like a permanent failure that won't go away
#   until 1) added to mynetworks, or 2) sasl authenticated
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

# Sending sasl auth from Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

# I am a relay to the world
relayhost = 

#.........................................................................
#
# * smtp  -> outgoing mail 
#
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# Receiving transport table specifies a mapping from email addresses to 
#  message delivery  transports  and  next-hop  destinations. 
# (relay forwarding back out)
transport_maps = hash:/etc/postfix/transport
relay_domains = your-domain.com

# .org host
myhostname = mail.your-domain.org
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname

# Accept final destination mail for mail.your-domain.org, your-domain.org, or locals
mydestination = $myhostname, your-domain.org, localhost.your-domain.org, localhost
#.........................................................................

# Allow my host and the .com host to send (relay out)
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128  5.6.7.8
inet_interfaces = all
inet_protocols = ipv4

# Message restrictions
mailbox_size_limit = 0
message_size_limit = 52428800
header_size_limit = 4096000
recipient_delimiter = +

#compatibility_level = 2

Complete relay contents of master.cf

No spaces between = sign

File: master.cf

~
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
smtp      inet  n       -       y       -       -       smtpd
smtps     inet  n       -       y       -       -       smtpd
    -o syslog_name=postfix/smtps
    -o smtpd_tls_wrappermode=yes
    -o smtpd_sasl_auth_enable=yes
    -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
# The preceeding 4 lines require SSL login for relay to internet (.com -> .org -> internet)
#  need cert for mail.your-domain.org, your-domain.org; 
#  and sasl_passwd for mail.your-domain.org unix login on www.your-domain.com
~
:wq

Reload Config

$ sudo postfix reload

Definitions of master fields

  • service

    This refers to the name in /etc/services and matches a name to a port number.

  • type

    Internet or pipe. Internet is a network communication, while pipe is a special file on disk that echos input as output used for local communication only.

  • private

    Access to some components is restricted to the Postfix system itself. This column is marked with a y for private access (the default) or an n for public access. inet components must be marked n for public access, since network sockets are necessarily available to other processes.

  • unpriv

    Postfix components run with the least amount of privilege required to accomplish their tasks. They set their identity to that of the unprivileged account specified by the mail_owner parameter. The default installation uses postfix. The default value of y for this column indicates that the service runs under the normal unprivileged account. Services that require root privileges are marked with n.

  • chroot

    Many components can be chrooted for additional security. The chroot location is specified in the queue_directory parameter in main.cf. The default is for a service to run in a chroot environment; however, the normal installation marks all components with an n so they are not chrooted when they run. Chrooting a service adds a level of complexity that you should thoroughly understand before taking advantage of the added security. See Section 4.8 later in the chapter for more information on running Postfix services in a chroot environment.

  • wakeup

    Some components require a wake-up timer to kick them into action at the specified interval. The pickup daemon is one example. At its default setting of 60 seconds, the master daemon wakes it up every minute to see if any new messages have arrived in the maildrop queue. The other services that require a wake-up are the qmgr and flush daemons. A question mark character (?) can be added at the end of the time to indicate that a wake-up event should be sent only if the component is being used. A 0 for the time interval indicates that no wake-up is required. The default is 0, since only the three components mentioned require a wake-up. The values as they are set in the Postfix distribution should work for almost all situations. Other services should not have wakeup enabled.

  • maxproc

    Limits the number of processes that can be invoked simultaneously. If unspecified here, the value comes from the parameter default_process_limit in main.cf, which is set to 100 by default. A setting of 0 means no process limit. You may want to adjust maxproc settings if you run Postfix on a system with limited resources or you want to optimize different aspects of the system.

  • command

    The actual command used to execute a service is listed in the final column. The command is specified with no path information, because it is expected to be in the Postfix daemon directory specified by the daemon_directory parameter in main.cf. By default the directory is /usr/libexec/postfix. All of the Postfix commands can be specified with one or more -v options to turn on increasingly more verbose logging information, which can be helpful if you must troubleshoot a problem. You can also enable information for a debugging program with the -D option. See the DEBUG_README file that comes with the Postfix distribution for more information on debugging if necessary.

Proxy Postfix using Transport Maps for Incoming Mails on your-domain.org

A postfix transport(5) table allows one domain to transfer incoming SMTP messages to another domain. For instance the .org domain will transfer all .com messages to the .com domain automatically.

Repeated postfix file contents from above to clarify this is for transporting from .org to .com

Configure

File: /etc/postfix/main.cf:

~
    transport_maps = hash:/etc/postfix/transport
~

Set postfix to accept mails for the .com addresse.

Repeated postfix file contents from above to clarify this is for transporting from .org to .com

File: /etc/postfix/main.cf:

~
    relay_domains = example.com
~

Transport Definitions

Create a transport table to redirect all mail for one domain as well as mail for "user@mydomain.org" to another domain. You can also specify another port, to bypass port 25 restrictions.

File: /etc/postfix/transport

    example.com          smtp:[example.com]:10025
    user@mydomain.org   smtp:[example.com]:10025

Make it a database

Create a postmap database from the flat ascii file.

$ sudo postmap /etc/postfix/transport

Reload Config

Finally, reload the postfix configuration files.

$ sudo postfix reload

Reference:

Proxy Postfix Relay for SMTP Outgoing Mails

To send mails from a non-standard port, use a .com domain to relay to a .org domain, with the .org relay set to = (basically null) and the .com relay = ...org.

Change MX records in no-ip.com from mail1.no-ip.com to mail.your-domain.org

Log into noip.com, go to DNS and select *.your-domain.com. At the bottom of the page you can change the mail1.no-ip.com record to mail.your-domain.org.

Don't forget to save and lookup the new value using dig -t MX your-domain.com.

Change TXT spf record in no-ip.com from noip, to:

For your-domain.com domain:

TypeHostValuePriorityTTL
MX Record@your-domain.org10Auto
TXT Record@v=spf1 mx a include:your-domain.org ~allAuto

SPF tools: https://www.dynu.com/en-US/NetworkTools/SPFGenerator# https://mxtoolbox.com/spf.aspx

  • Try not to add more than required, you might create a potential infinite loop. 8

On the DNS for .com, create a TXT record like this

"v=spf1 mx a include:<Domain of .org> ~all"

Basically is says "In the DNS TXT record for email destination you@your-domain.com, validate the MX IP Address of DNS record for domain your-domain.com" for ~all; When an SPF record includes ~all (softfail qualifier), receiving servers typically accept messages from senders that aren't in your SPF record, but mark them as suspicious.

Reference: http://www.open-spf.org/FAQ/Common_mistakes/#list-domains

Test e-mail results from google:

~
ARC-Authentication-Results: i=1; mx.google.com;
       spf=pass (google.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=you@your-domain.com
Received: from mail.your-domain.org (mail.your-domain.org. [1.2.3.4])
        by mx.google.com with ESMTPS id o6-20020a0dcc06000000b005363cf948basi2222119ywd.61.2023.02.25.05.45.43
        for <you.name@gmail.com>
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Sat, 25 Feb 2023 05:45:44 -0800 (PST)
Received-SPF: pass (google.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) client-ip=1.2.3.4;
Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=you@your-domain.com
Received: from www.your-domain.com (unknown [5.6.7.8])
	by mail.your-domain.org (Postfix) with ESMTPSA id 9071FD3C93
	for <you.name@gmail.com>; Sat, 25 Feb 2023 13:45:43 +0000 (UTC)
~

Change your-domain.com postfix/main.cf relay to your-domain.org

File: /etc/postfix/main.cf

relayhost = [mail.your-domain.org]:465

Add your-domain.org unix login on host your-domain.com

File: /etc/postfix/sasl/sasl_passwd

[mail.your-domain.org]:465 you:********

Create a postfix DB from flat file

$ sudo postmap /etc/postfix/sasl/sasl_passwd

Enable TLS on your-domain.org

TLS requires certificate(s) and SASL login verification.

The logon verification is done by Docevot using the userdb setting pam[1], while the postfix SASL verification uses a unix pipe /var/spool/postfix/private/auth to talk to Dovecot.

The encryption is enabled by Let's Encrypt certificates (one for the domain, another for each subdomain), over the smtps (post 465) network socket to the client. The master.cf file -o settings override the main.cf settings, ensuring a connection is only accepted if:

  • TLS certificate is working and accepted by both parties, on port 465
  • SASL logon passes, using postfix -> Dovecot -> pam login.
OR
  • The incoming IP address is part of mynetwork, on port 25

Plugable Authentication Module (PAM) is the Linux login method for users

Two Steps:

  1. install dovecot
$ sudo apt-get install dovecot-core
$ sudo dnf install dovecot

Add login to dovecot auth

File: /etc/dovecot/conf.d/10-auth.conf

~
     auth_mechanisms = plain login
~
:wq

Set the certificates to letsencrypt, require ssl and prefer server of the ciphers

File: /etc/dovecot/conf.d/10-ssl.conf

~
    ssl = required
~
    ssl_cert = </etc/letsencrypt/live/your-domain.org/fullchain.pem
    ssl_key = </etc/letsencrypt/live/your-domain.org/privkey.pem
~
    # Comment out default certs
~
    ssl_prefer_server_ciphers = yes
~
:wq

Set up a local unix pipe for dovecot and postfix to communicate this authorization data, under the service_auth set of brackets.

File: /etc/dovecot/conf.d/10-master.conf

~
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0660
    user = postfix
  }
~
:wq
  1. Set the receiving certificates in Postfix, require TLS using Dovecot.

Repeated postfix file contents from above to clarify this is for relaying TLS on .org to the Internet

File: /etc/postfix/main.cf

~
#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_cert_file = /etc/letsencrypt/live/your-domain.org/fullchain.pem
smtpd_tls_key_file  = /etc/letsencrypt/live/your-domain.org/privkey.pem
smtpd_use_tls = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_security_level=may

~

# sasl auth from Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

~
:wq

Repeated postfix file contents from above to clarify this is for relaying TLS on .org to the Internet

File: /etc/postfix/master.cf

~
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
smtp      inet  n       -       y       -       -       smtpd
smtps     inet  n       -       y       -       -       smtpd
    -o syslog_name=postfix/smtps
    -o smtpd_tls_wrappermode=yes
    -o smtpd_sasl_auth_enable=yes
    -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
# The preceeding 4 lines require SSL login for relay to internet (.com -> .org -> internet)
#  need cert for mail.your-domain.org, your-domain.org; 
#  and sasl_passwd for mail.your-domain.org unix login on www.your-domain.com
~
:wq
  • Make sure port 465 (smtps) is opened by Postfix

Open on localhost

# nmap -sT -O localhost
Starting Nmap 7.70 ( https://nmap.org ) at 2023-02-19 16:46 EST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00019s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 992 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh   <-
25/tcp  open  smtp  <-
80/tcp  open  http  <-
110/tcp open  pop3
143/tcp open  imap
443/tcp open  https <-
465/tcp open  smtps <-
993/tcp open  imaps
995/tcp open  pop3s

RedHat 9 /etc/services shows port 465 as urd, so I switched the urd and alias smtps.

# grep smtps /etc/services
smtps           465/tcp           urd   # URL Rendesvous Directory for SSM / SMTP over SSL (TLS)

Now ss looks better:

# ss -ltap
State        Recv-Q    Send-Q        Local Address:Port            Peer Address:Port     Process                                                   
LISTEN       0         128                 0.0.0.0:ssh                  0.0.0.0:*         users:(("sshd",pid=1314,fd=3))                           
LISTEN       0         100                 0.0.0.0:smtp                 0.0.0.0:*         users:(("master",pid=23736,fd=13))                       
LISTEN       0         100                 0.0.0.0:imaps                0.0.0.0:*         users:(("dovecot",pid=23599,fd=41))                      
LISTEN       0         100                 0.0.0.0:pop3s                0.0.0.0:*         users:(("dovecot",pid=23599,fd=23))                      
LISTEN       0         100                 0.0.0.0:pop3                 0.0.0.0:*         users:(("dovecot",pid=23599,fd=21))                      
LISTEN       0         100                 0.0.0.0:imap                 0.0.0.0:*         users:(("dovecot",pid=23599,fd=39))                      
LISTEN       0         100                 0.0.0.0:smtps                0.0.0.0:*         users:(("master",pid=23736,fd=17))                       
ESTAB        0         52            1.2.3.4:ssh                   5.6.7.8:44958     users:(("sshd",pid=2402,fd=4),("sshd",pid=2278,fd=4))    
LISTEN       0         128                    [::]:ssh                     [::]:*         users:(("sshd",pid=1314,fd=4))                           
LISTEN       0         100                    [::]:imaps                   [::]:*         users:(("dovecot",pid=23599,fd=42))                      
LISTEN       0         100                    [::]:pop3s                   [::]:*         users:(("dovecot",pid=23599,fd=24))                      
LISTEN       0         100                    [::]:pop3                    [::]:*         users:(("dovecot",pid=23599,fd=22))                      
LISTEN       0         100                    [::]:imap                    [::]:*         users:(("dovecot",pid=23599,fd=40))   

Open to internet

$ sudo firewall-cmd --list-services
  http https smtp smtps ssh

Clients Mail User Agents (MUA)

These are the programs where a human interface resides, and where everybody reads, sends, deletes, and manages their E-Mail. Names such as: Thunderbird, Evolution, Mutt, Gmail, Outlook, etc.

  • Mail Transport Agents (MTA) only connect here. Do not connect your MUA to this port!

    • SMTP (port 25): This is an restricted relay port, defined on host example.org and controlled by the /etc/postfix/master.cf smtp definition. Only addresses in $mydestination will be accepted, and nothing else will be relayed in. The transport definition will forward to it's list of addresses/domains. After a client is authenticated, it will be allowed to relay out on port 25. Process master, forked from postfix, performs this responsibility.

      • -o smtpd_relay_restrictions=permit_sasl_authenticated,reject: means that only authenticated connections can relay out
  • Client (MUA) connect here to SEND mail only

    • SMTPS (port 465): This is defined on host example.org, and is always an encrypted port, controlled by the /etc/postfix/master.cf smtps definition. All authentications occur here. Process master, forked from postfix, performs this responsibility.

      • -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject: means that only $mynetworks or authenticated connections can submit messages to be sent. MUA will connect using this rule.

      • Authentication is done using /etc/dovecot/conf.d/10-auth.conf auth_mechanism=login definition, over unix pipe unix_listener /var/spool/postfix/private/auth declared in file /etc/dovecot/conf.d/10-master.conf. Linux PAM verification occurs using file /etc/pam.d/dovecot and passdb{driver=pam} in file /etc/dovecot/conf.d/auth-system.conf.ext.

Unix pipe for postfix to dovecot authenication of clients.

$ sudo ls -l /var/spool/postfix/private/auth
srw-rw---- 1 postfix postfix 0 Feb 10 10:21 /var/spool/postfix/private/auth
  • Client (MUA) connect here to READ/MANAGE mail only

    • IMAPS (port 993): This is defined on the example.com host Dovecot installation, file /etc/dovecot/conf.d/10-master.conf, section service imap_listener imaps. Process imap, forked from dovecot, performs this responsibility.

    • Authentication is done using /etc/dovecot/conf.d/10-auth.conf auth_mechanism=plain definition, over unix_listener auth_userdb declared in file /etc/dovecot/conf.d/10-master.conf. Linux PAM verification occurs using file /etc/pam.d/dovecot and passdb{driver=pam} in file /etc/dovecot/conf.d/auth-system.conf.ext.

Port 587, aka: submission, is purposely ommited because it is a partial encryption called STARTTLS. This is deemed un-secure and not recommended.

MUA Connection Definitions

Read Mail

  • Server: mail.example.com
  • User: <linux user defined on the E-Mail .com host>
  • Password: <linux user password defined on the E-Mail .com host>
  • SSL: ON
  • Port: 993

Send Mail

  • Server: smtp.example.org
  • User: <linux user defined above on the E-Mail .org host>
  • Password: <linux user password defined above on the E-Mail .org host>
  • SSL: ON
  • Port: 465

Debug

If mail queues up to send/resend, you can check and clear to queue

List mail queue
mailq
Look at message
postcat -vq DCE2182DEA
Flush queue
postsuper -d ALL deferred
or
postqueue -f

Cache files (send/receive) are in data_directory (/var/lib/postfix) as Berkley Databasees

Install a Mail User Agent for ssh

mutt is a nice local mail reader, for times when the network mail may not work, at least the local mail will.

$ sudo apt-get install mutt
$ sudo dnf install mutt

Install a nice ssh capable log reader

I like lnav. Just run lnav and by default it will read the syslog. Or run lnav /var/log/mail.log.

$ sudo apt-get install lnav
$ sudo dnf install lnav

Install a package marking tool

Debian only: apt-clone will create a nice bundle of all your current packages. Save this off in case you have to re-build your server.

$ sudo apt-get install apt-clone

Certificate Expiration Check

Run this every week/day in cron /root/cert_expire.sh 2 to E-Mail you reminders before it expires.

File: ~/cert_expire.sh

#!/bin/bash
# ----------------------------------------------------------------------
#
# File: cert_expire.sh
#
# Purpose: See what the expiration date is for Let's Encrypt Certificate
#
#
#  s_client : The s_client command implements a generic SSL/TLS client
#              which connects to a remote host using SSL/TLS.
#  -servername $DOM : Set the TLS SNI (Server Name Indication) extension
#                      in the ClientHello message to the given value.
#  -connect $DOM:$PORT : This specifies the host ($DOM) and optional
#                         port ($PORT) to connect to.
#  x509 : Run certificate display and signing utility.
#  -noout : Prevents output of the encoded version of the certificate.
#  -dates : Prints out the start and expiry dates of a TLS or SSL certificate.
#
# Don Cohoon - Jan 2023
# ----------------------------------------------------------------------
#
#
if [ $# -gt 0 ]; then
  A=${1}
else
  /usr/bin/echo "1) E-Mail"
  /usr/bin/echo "2) File"
  /usr/bin/echo "3) Web"
  /usr/bin/echo "4) Local"
  read A
fi
case ${A}
 in
   1)
	/usr/bin/echo "REMINDER: Restart postfix and dovecot to enable new certs"
	/usr/bin/echo "=> E-Mail Certificate: CTRL-C to exit"
	#/usr/bin/openssl s_client -connect mail.your-domain.org:25 -starttls smtp 2>/dev/null|/usr/bin/openssl x509 -noout -dates
	/usr/bin/openssl s_client -connect mail.your-domain.org:465  2>/dev/null|/usr/bin/openssl x509 -noout -dates
	;;
   2)
	/usr/bin/echo "=> File Certificate"
	sudo /usr/bin/openssl x509 -enddate -noout -in /etc/letsencrypt/live/your-domain.org/fullchain.pem
	;;
   3)
	/usr/bin/echo "REMINDER: Restart apache2 and nginx to enable new certs"
	/usr/bin/echo "=> www.your-domain.org Certificate: CTRL-C to exit"
	/usr/bin/openssl s_client -servername your-domain.org -connect www.your-domain.org:443 2>/dev/null | /usr/bin/openssl x509 -noout -dates
	;;
   4)
	/usr/bin/echo "REMINDER: Restart apache2 and nginx to enable new certs"
	/usr/bin/echo "=> Local Web Certificate: CTRL-C to exit"
	/usr/bin/openssl s_client -connect localhost:443 | /usr/bin/openssl x509 -noout -dates
	;;
esac

Configuring SPF Policy Agent

We also need to tell our Postfix SMTP server to check for SPF record of incoming emails. This doesn’t help ensure outgoing email delivery but help with detecting forged incoming emails.

Install required packages:

  • Debian
sudo apt install postfix-policyd-spf-python
  • RedHat
sudo dnf install pypolicyd-spf

Test SPF

If you know the sender, recipient, and client_address, you can test them before turning SPF on in postfix.

Must have blank line as last input for policyd-spf

# Python Site Packages = /usr/lib/python3.9/site-packages/
# Source = /usr/lib/python3.9/site-packages/spf_engine/*
/usr/libexec/postfix/policyd-spf <<EOF
 request=smtpd_access_policy
 protocol_state=RCPT
 protocol_name=SMTP
 helo_name=ccpub6
 queue_id=hv8rp02v1sso
 instance=12345.6789
 sender=foo
 recipient=bar
 client_address=1.2.3.4
 client_name=bubba

EOF

Configure SPF

Add the following lines at the end of the file, which tells Postfix to start the SPF policy daemon when it’s starting itself.

  • Debian

File: /etc/postfix/master.cf

policyd-spf  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/bin/policyd-spf
  • Redhat

File: /etc/postfix/master.cf

policyd-spf  unix  -       n       n       -       0       spawn
    user=nobody argv=/usr/libexec/postfix/policyd-spf

Append the following lines at the end of the file. The first line specifies the Postfix policy agent timeout setting. The following lines will impose a restriction on incoming emails by rejecting unauthorized email and checking SPF record.

File: /etc/postfix/main.cf

policyd-spf_time_limit = 3600
smtpd_recipient_restrictions =
   permit_mynetworks,
   permit_sasl_authenticated,
   reject_unauth_destination,
   check_policy_service unix:private/policyd-spf

Restart Postfix.

sudo systemctl restart postfix

Next time, when you receive an email from a domain that has an SPF record, you can see the SPF check results in the raw email header. The following header indicates the sender sent the email from an authorized host.

Received-SPF: Pass (sender SPF authorized).

Debug SPF

Config file, debug setting:

File: /etc/python-policyd-spf/policyd-spf.conf

~
# level 1 is default
debugLevel = 5
~
:wq
man policyd-spf.conf

check_policy_service Unix pipe:

File: /var/spool/postfix/private/policyd-spf

srw-rw-rw-. 1 postfix postfix 0 Feb 25 14:37 /var/spool/postfix/private/policyd-spf

Block Domains and E-Mail Addresses using Postfix access

The following example uses an indexed file, so that the order of table entries does not matter.

File: /etc/postfix/main.cf:

 smtpd_client_restrictions =
   check_client_access hash:/etc/postfix/access

The example permits access by the client at address 1.2.3.4 but rejects all other clients in 1.2.3.0/24.

File: /etc/postfix/access:

1.2.3   REJECT
1.2.3.4 OK
aol.com REJECT

Create hash map of ascii file, and restart postfix

$ sudo postmap  /etc/postfix/access
$ sudo systemctl restart postfix

Reference:

Setting up DKIM

OpenDKIM is an open source implementation of the DKIM (Domain Keys Identified Mail) sender authentication system proposed by the E-mail Signing Technology Group (ESTG), now standardized by the IETF (RFC6376). It also includes implementations of the RFC5617) Vouch By Reference (VBR, RFC5518) proposed standard and the experimental Authorized Third Party Signatures protocol (ATPS, RFC6541).

Install OpenDKIM

  • Debian
sudo apt install opendkim opendkim-tools
  • RedHat
# enable the CodeReady Linux Builder repository. You already have access to it; you just need to enable it.
dnf config-manager --set-enabled crb
# install the EPEL RPM
dnf install epel-release epel-next-release
# install
sudo dnf install opendkim opendkim-tools

Configure OpenDKIM

Add user postfix to group opendkim.

sudo gpasswd -a postfix opendkim

Check OpenDKIM main configuration file for Syslog, Logwhy.

Logwhy will generate more detailed logs for debugging.

File: /etc/opendkim.conf

Syslog               yes
Logwhy               yes

Set Canonicalization used when signing messages. The recognized values are relaxed and simple as defined by the DKIM specification. The default is simple. The value may include two different canonicalizations separated by a slash ("/") character, in which case the first will be applied to the header and the second to the body.

Set operating Modes. The string is a concatenation of characters that indicate which mode(s) of operation are desired. Valid modes are s (signer) and v (verifier). The default is sv except in test mode (see the opendkim(8) man page) in which case the default is v. When signing mode is enabled, one of the following combinations must also be set: (a) Domain, KeyFile, Selector, no KeyTable, no SigningTable; (b) KeyTable, SigningTable, no Domain, no KeyFile, no Selector; (c) KeyTable, SetupPolicyScript, no Domain, no KeyFile, no Selector.

File: /etc/opendkim.conf

Canonicalization   relaxed/simple
Mode               sv
  • Do not set Domain or SubDomains, they are not required because we will use SigningTable.
  • Do not set Selector, it is not required because we will use SigningTable.
  • Do not set KeyFile, it is not required because we will use KeyTable.

Add restart definitions to the end of the file.

  • AutoRestart (Boolean): Automatically re-start on failures. Use with caution; if the filter fails instantly after it starts, this can cause a tight fork(2) loop.

  • AutoRestartCount (integer): Sets the maximum automatic restart count. After this number of automatic restarts, the filter will give up and terminate. A value of 0 implies no limit; this is the default.

  • AutoRestartRate (string): Sets the maximum automatic restart rate. If the filter begins restarting faster than the rate defined here, it will give up and terminate. This is a string of the form n/t[u] where n is an integer limiting the count of restarts in the given interval and t[u] defines the time interval through which the rate is calculated; t is an integer and u defines the units thus represented ("s" or "S" for seconds, the default; "m" or "M" for minutes; "h" or "H" for hours; "d" or "D" for days). For example, a value of "10/1h" limits the restarts to 10 in one hour. There is no default, meaning restart rate is not limited.

File: /etc/opendkim.conf

AutoRestart       yes
AutoRestartCount  10
AutoRestartRate   10/1H

Reference: http://www.opendkim.org/opendkim.conf.5.html

Map Domains to Keys

The next two configuration items will create maps for E-Mail Domains in the From: header, to keys used to sign messages.

  • KeyTable (dataset): Gives the location of a file mapping key names to signing keys. If present, overrides any KeyFile setting in the configuration file. The data set named here maps each key name to three values: (a) the name of the domain to use in the signature’s "d=" value; (b) the name of the selector to use in the signature’s "s=" value; and (c) either a private key or a path to a file containing a private key. If the first value consists solely of a percent sign ("%") character, it will be replaced by the apparent domain of the sender when generating a signature. If the third value starts with a slash ("/") character, or "./" or "../", then it is presumed to refer to a file from which the private key should be read, otherwise it is itself a PEM-encoded private key or a base64-encoded DER private key; a "%" in the third value in this case will be replaced by the apparent domain name of the sender. The SigningTable (see below) is used to select records from this table to be used to add signatures based on the message sender.

  • SigningTable (dataset): Defines a table used to select one or more signatures to apply to a message based on the address found in the From: header field. Keys in this table vary depending on the type of table used; values in this data set should include one field that contains a name found in the KeyTable (see above) that identifies which key should be used in generating the signature, and an optional second field naming the signer of the message that will be included in the "i=" tag in the generated signature. Note that the "i=" value will not be included in the signature if it conflicts with the signing domain (the "d=" value).

    • If the first field contains only a "%" character, it will be replaced by the domain found in the From: header field. Similarly, within the optional second field, any "%" character will be replaced by the domain found in the From: header field.

    • If this table specifies a regular expression file ("refile"), then the keys are wildcard patterns that are matched against the address found in the From: header field. Entries are checked in the order in which they appear in the file.

    • For all other database types, the full user@host is checked first, then simply host, then user@.domain (with all superdomains checked in sequence, so "foo.example.com" would first check "user@foo.example.com", then "user@.example.com", then "user@.com"), then .domain, then user@*, and finally *.

    • In any case, only the first match is applied, unless MultipleSignatures is enabled in which case all matches are applied.

File: /etc/opendkim.conf

KeyTable        /etc/opendkim/KeyTable
SigningTable    refile:/etc/opendkim/SigningTable

KeyTable need "refile:"? (wildcards not allowed)

Hosts to ignore when verifying signatures

Identifies a set of "external" hosts that may send mail through the server as one of the signing domains without credentials as such. This has the effect of suppressing the "external host (hostname) tried to send mail as (domain)" log messages. Entries in the data set should be of the same form as those of the PeerList option below. The set is empty by default.

File: /etc/opendkim.conf

ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts

A set of internal hosts whose mail should be signed

Identifies a set internal hosts whose mail should be signed rather than verified. Entries in this data set follow the same form as those of the PeerList option below. If not specified, the default of "127.0.0.1" is applied. Naturally, providing a value here overrides the default, so if mail from 127.0.0.1 should be signed, the list provided here should include that address explicitly.

File: /etc/opendkim.conf

InternalHosts   refile:/etc/opendkim/TrustedHosts

Save and close the file.

Create Signing Table, Key Table and Trusted Hosts File

Check directory structure for OpenDKIM

$ sudo ls -lR /etc/opendkim*
-rw-r--r-- 1 root     root     5346 Mar  2 11:15 /etc/opendkim.conf

/etc/opendkim:
total 12
drwx--x--- 2 opendkim opendkim    6 Feb 24  2022 keys
-rw-r----- 1 opendkim opendkim  339 Feb 24  2022 KeyTable
-rw-r----- 1 opendkim opendkim 1221 Feb 24  2022 SigningTable
-rw-r----- 1 opendkim opendkim  378 Feb 24  2022 TrustedHosts

/etc/opendkim/keys:
total 0

If not the same, change the owner from root to opendkim and make sure only opendkim user can read and write to the keys directory.

sudo chown -R opendkim:opendkim /etc/opendkim

sudo chmod go-rw /etc/opendkim/keys

Create the signing table

Add two lines to the file.

The first line tells OpenDKIM that if a sender on your server is using a @your-domain.com address, then it should be signed with the private key identified by default._domainkey.your-domain.com.

The second line tells that your sub-domains will be signed by the private key also.

File: /etc/opendkim/SigningTable

*@your-domain.com    default._domainkey.your-domain.com
*@*.your-domain.com    default._domainkey.your-domain.com

Save and close the file.

Create the key table

Add the following line, defining the location of the private key.

File: /etc/opendkim/KeyTable

default._domainkey.your-domain.com     your-domain.com:default:/etc/opendkim/keys/your-domain.com/default.private

Save and close the file.

Create the trusted hosts file

Tell OpenDKIM if an email is from localhost or the same domain, then only sign the email, and not perform DKIM verification.

File: /etc/opendkim/TrustedHosts

127.0.0.1
localhost

.your-domain.com

Save and close the file.

Do not add an asterisk to the domain name like this: *.your-domain.com. Put only a dot before the domain name.

Generate Private/Public Keypair

DKIM is used to sign outgoing messages and verify incoming messages, so we need to generate a private key for signing and a public key for remote verifier. Only the public key will be published in DNS.

Create a separate folder for the domain.

sudo mkdir /etc/opendkim/keys/your-domain.com

Generate keys using opendkim-genkey tool.

You should rotate in new keys every once and a while for protection against private key leaks. Allow seven days before deleting the old keys for existing E-Mails to be delivered.

sudo opendkim-genkey -b 2048 -d your-domain.com -D /etc/opendkim/keys/your-domain.com -s default -v
opendkim-genkey: generating private key
opendkim-genkey: private key written to default.private
opendkim-genkey: extracting public key
opendkim-genkey: DNS TXT record written to default.txt

The above command will create 2048 bits keys. -d (domain) specifies the domain. -D (directory) specifies the directory where the keys will be stored and we use default as the selector (-s), also known as the name. Once the command is executed, the private key will be written to default.private file and the public key will be written to default.txt file.

Ensure opendkim is the owner of the private key.

sudo chown opendkim:opendkim /etc/opendkim/keys/your-domain.com/default.private

Also change the permission, so only the opendkim user has read and write access to the file.

sudo chmod 600 /etc/opendkim/keys/your-domain.com/default.private

Publish Your Public Key in DNS Records

Display the public key

sudo cat /etc/opendkim/keys/your-domain.com/default.txt
default._domainkey	IN	TXT	( "v=DKIM1; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAutisB7xnL1B1j88Er2VsEd6WuwifqSThKEcrnlkhnsVhs/UkCd2lHL+dZwivjbfH+4RXIP0LK9shGokPwaA2MHNH3GgAuWZ/Wb6ZrZwqDlmHy+H6Q0/cLsB2Py2HFthq1JUhHW31ZOIqa4qOn2suBntQdizGExHsuMMb1nJpu0lgFJLU848qPQO76QMTcC/TyssiCjLXXSQEsS"
	  "Kx0UmeODJ43NKAAS0OqkGBD2UE7/SW54bVpESK32lTIfzk91OdW+zDMzX6myToJtEE9WgOkgD2evSTp02dhKBBRkQvGJ0SF7el34e/smeS+XvodjjOvP2f3qW5cLvrCRByIkFzRwIDAQAB" )  ; ----- DKIM key default for your-domain.com

The string after the p parameter is the public key, it spans two lines in the cat output because the limit per line is 256 characters. It really should be one big long string with no quotes.

In your DNS manager,

  • create a TXT record,
  • enter default._domainkey in the name field.
  • Copy everything between the parentheses and paste it into the value field of the DNS record. Delete all double quotes and white spaces in the value field. Join all the lines into one line.

For Example; look at a recent google e-mail:

X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20210112; t=1677769129;
        h=to:list-unsubscribe:user-agent:mime-version:subject:message-id:from
         :date:dkim-signature:dkim-signature:delivered-to:x-gm-message-state
         :from:to:cc:subject:date:message-id:reply-to;
        bh=V22zSr/CKU90o7uszazuWVoXgsouLolWaiMhbqvqG8Y=;
        b=FUH9YP2CWhupcc6eU3hVYigxSWJ2fVJHF1F3DrkJoMb1K3hf9O7vrTWDxqNIOvGmPS
         6sCsgvynVtQ+cccVgzc6vZLBYDg3XBdQF6u2hxiMIAAkyPGVUUrYpj/OreMj1WkGDkG0
         9yVxpp0UuIK8uyrfswX9zBWT/QORjQ4Lfh3KCbzaLX8DbfWoc3P907Ebc8cfvVGDu2wX
         oNBeYjEXb4sywHcVmuUNdg//O78sAY5CnSVZ3Gc/41/pFtNHdCCjQAWSQ/W/Czfsy2TY
         Ovuebwb71h3VlUpqDIqkaIMZ5rF9pWxqeQgHkIs8Ktgd8CnAhqnk77ZXWk0SR9Q8hkuQ
         +BQw==

The s and d tags are used to look up the DKIM record. s is the selector while d is the domain.

s=20210112 d=1e100.net

Together they form the DNS lookup string: 20210112._domainkey.1e100.net

$ dig +short txt 20210112._domainkey.1e100.net
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8Sabq+yC2d91PkWTEkjdH2AsaH31YzVJ8PKlQy2GZ9u8vtqfQM88nACYwWAbCQvLwUfCz7hbF/PyM3K/SPlDSk0HqKq89AQjv60br0pK90vWFmzt04ioNcf4QoiJjnnTWD6h5gOM" "ATz4WfdwCrQ9MRF0SjDHteEVeHCK4WKsWKdPshaSLiVfZxiGLv4SZkWye7Zh5iM66MUvYAr3x151AyCQroTNfJY9RN9RK2ZqLdcoulg7S/XMbnzY7EW0P8nPj2jqvMp0bcr13tOzBRnysYiQIu3cjrtyLNfAZobK6tlmy737vkVH27D0rsUrcABrFqvVox61h61JssaRYcwRpwIDAQAB"

Your key lookup should look like:

$ dig +short txt default._domainkey.your-domain.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAutisB7xnL1B1j88Er2VsEd6WuwifqSThKEcrnlkhnsVhs/UkCd2lHL+dZwivjbfH+4RXIP0LK9shGokPwaA2MHNH3GgAuWZ/Wb6ZrZwqDlmHy+H6Q0/cLsB2Py2HFthq1JUhHW31ZOIqa4qOn2suBntQdizGExHsuMMb1nJpu0lgFJLU848qPQO76QMTcC/TyssiCjLXXSQEsS" "Kx0UmeODJ43NKAAS0OqkGBD2UE7/SW54bVpESK32lTIfzk91OdW+zDMzX6myToJtEE9WgOkgD2evSTp02dhKBBRkQvGJ0SF7el34e/smeS+XvodjjOvP2f3qW5cLvrCRByIkFzRwIDAQAB"

Reference: https://www.cloudflare.com/learning/dns/dns-records/dns-dkim-record/

Test DKIM Key

Test your key.

sudo opendkim-testkey -d your-domain.com -s default -vvv

You should see Key OK in the output.

opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key 'default._domainkey.your-domain.com'
opendkim-testkey: key secure
opendkim-testkey: key OK

Your DKIM record will take some time to propagate to the Internet.

To check, go to https://www.dmarcanalyzer.com/dkim/dkim-check/, use default as the selector your domain name to check DKIM record propagation.

Connect Postfix to OpenDKIM

Postfix can talk to OpenDKIM via a Unix socket file. It is a good idea to put it where all the other postfix socket files are.

Create the directory OpenDKIM for a socket file and limit is to the opendkim user and postfix group.

$sudo mkdir                  /var/spool/postfix/opendkim
$sudo chown opendkim:postfix /var/spool/postfix/opendkim

Set Socket to local

File: /etc/opendkim.conf

Socket    local:/var/spool/postfix/opendkim/opendkim.sock

Change the default file as well (if it exists):

Add openDKIM to Postfix main.cf

milter is a modifier filter

File: /etc/postfix/main.cf

# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

Restart opendkim and postfix service

$ sudo systemctl restart opendkim postfix

SPF and DKIM Check

Send a test email to another domain's account, and see if SPF and DKIM checks are passed in the message source.

Example:

Authentication-Results: dkim-verifier.icloud.com;
	dkim=pass (2048-bit key) header.d=your-domain.com header.i=@your-domain.com header.b=yn+tGP2N
Authentication-Results: spf.icloud.com; spf=pass (spf.icloud.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=you@your-domain.com

Your email server will also perform SPF and DKIM checks on other domains.

Example:

Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=1234:f8b0:5678:90::372; helo=mail-yw1-xc2d.google.com; envelope-from=someone@gmail.com; receiver=<UNKNOWN> 
Authentication-Results: you.your-domain.com;
	dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="IsoKGudn";
	dkim-atps=neutral

Postfix Can’t Connect to OpenDKIM

Check the logs

$ vi /var/log/mail*
$ sudo journalctl -eu opendkim

If you see this:

connect to Milter service local:opendkim/opendkim.sock: No such file or directory

check the opendkim systemd service.

$ sudo systemctl status opendkim

If opendkim is running, it means Postfix can’t connect to OpenDKIM via the Unix domain socket (local:opendkim/opendkim.sock).

Check the socket file:

$ ls -l /var/spool/postfix/opendkim/opendkim.sock 
srwxrwxr-x 1 opendkim opendkim 0 Mar  2 15:09 /var/spool/postfix/opendkim/opendkim.sock

And postfix user:

$ id postfix
uid=89(postfix) gid=89(postfix) groups=89(postfix),12(mail),988(opendkim)

Be sure the postfix user has group opendkim.

If all else fails, you can configure OpenDKIM to use a TCP/IP socket instead of Unix local socket.

File: /etc/opendkim.conf

Socket     inet:8892@localhost

File: /etc/postfix/main.cf

smtpd_milters = inet:127.0.0.1:8892

Restart OpenDKIM and Postfix.

sudo systemctl restart opendkim postfix

Now Postfix will connect to OpenDKIM via the TCP/IP socket.

Configuration Error in Email Client

DKIM signing could fail if you do not use STARTTLS or SSL/TLS.

TypePortEncryptionPassword
SMTP587STARTTLSNormal
IMAP143STARTTLSNormal
SMTP465SSL/TLSNormal
IMAP993SSL/TLSNormal

Wrong Settings

Port 25 as the SMTP port in mail clients to submit outgoing emails. No encryption method was selected.

Testing Email Score and Placement

Visit Mail-Tester https://www.mail-tester.com and send an E-Mail to the unique email address displayed on the home page. They will analyze it and return a sender score.

GlockApps https://glockapps.com/ will show you where your emails are being delivered at Gmail, Outlook, & all major ISPs.

What is DMARC?

DMARC stands for Domain-based message authentication, reporting and conformance. DMARC is a protcol for protecting your Internet Domain from abuse.

It extends SPF and DKIM using another DNS entry.

Originators of Internet Mail need to be able to associate reliable and authenticated domain identifiers with messages, communicate policies about messages that use those identifiers, and report about mail using those identifiers. These abilities have several benefits: Receivers can provide feedback to Domain Owners about the use of their domains; this feedback can provide valuable insight about the management of internal operations and the presence of external domain name abuse.

Reference: https://datatracker.ietf.org/doc/html/rfc7489

Create DMARC Record

DMARC policies are published as a TXT record in DNS.

  • Before creating a DMARC record, you must first create SPF and DKIM records.

  • Send a test email from your domain, and check the raw email headers at the recipient’s mailbox. Ensure the domain is the same in:

    • Return-path: you@domain
    • From: you@domain
    • d=domain in the DKIM signature

If the 3 domains are identical, then they are aligned.

If Return-Path: or DKIM d= uses a subdomain instead of the main domain name, then this is called relaxed alignment. If no subdomain is used and the main domain names are the same, it’s called strict alignment.

DMARC Record TXT

Domain Owner DMARC preferences are stored as DNS TXT records in subdomains named "_dmarc". For example, the Domain Owner of "example.com" would post DMARC preferences in a TXT record at "_dmarc.example.com".

In your Domain Registration DNS manager add a new TXT record.

Name field:

_dmarc

Value field:

v=DMARC1; p=none; pct=100; rua=mailto:dmarc-reports@your-domain.com

Definition:

  • v=DMARC1: Version (plain-text; REQUIRED). Identifies the record retrieved as a DMARC record. It MUST have the value of "DMARC1".
  • p=none: Requested Mail Receiver policy (plain-text; REQUIRED for policy records).
  • pct=100: (plain-text integer between 0 and 100, inclusive; OPTIONAL; default is 100). Percentage of messages from the Domain Owner's mail stream to which the DMARC policy is to be applied.
  • rua: Addresses to which aggregate feedback is to be sent (comma- separated plain-text list of DMARC URIs; OPTIONAL).

There are 3 policies you can choose from:

  • none: The Domain Owner requests no specific action be taken regarding delivery of messages.
  • quarantine: The Domain Owner wishes to have email that fails the DMARC mechanism check be treated by Mail Receivers as suspicious.
  • reject: The Domain Owner wishes for Mail Receivers to reject email that fails the DMARC mechanism check. Rejection SHOULD occur during the SMTP transaction.

Another option to consider is:

  • fo: Failure reporting options (plain-text; OPTIONAL; default is "0") Provides requested options for generation of failure reports. Report generators MAY choose to adhere to the requested options. This tag's content MUST be ignored if a "ruf" tag (below) is not also specified. The value of this tag is a colon-separated list of characters that indicate failure reporting options as follows:

    • 0: Generate a DMARC failure report if all underlying authentication mechanisms fail to produce an aligned "pass" result.

    • 1: Generate a DMARC failure report if any underlying authentication mechanism produced something other than an aligned "pass" result.

    • d: Generate a DKIM failure report if the message had a signature that failed evaluation, regardless of its alignment. DKIM- specific reporting is described in [AFRF-DKIM].

    • s: Generate an SPF failure report if the message failed SPF evaluation, regardless of its alignment. SPF-specific reporting is described in [AFRF-SPF].

  • ruf: Addresses to which message-specific failure information is to be reported (comma-separated plain-text list of DMARC URIs; OPTIONAL). If present, the Domain Owner is requesting Mail Receivers to send detailed failure reports about messages that fail the DMARC evaluation in specific ways (see the "fo" tag above).

Try fo=1 at first for detailed DMARC failure reports. When you change to a more restrictive policy, use fo=0.

v=DMARC1; p=none; pct=100; fo=1; ruf=mailto:dmarc-reports@your-domain.com

If you have a domain name that will not send emails, use p=reject policy.

v=DMARC1; p=reject; pct=100;

DMARC Record Check

You can check your DMARC record from Linux terminal with the following command:

$ dig txt +short _dmarc.example.com
"v=DMARC1; p=none; pct=100; rua=mailto:postmaster@your-domain.com"

You can also install opendmarc-check that to check a DMARC record.

Install opendmarc package

sudo apt install opendmarc

It checks DNS and translates the DMARC record to a human readable form.

$ opendmarc-check your-domain.com
DMARC record for your-domain.com:
	Sample percentage: 100
	DKIM alignment: relaxed
	SPF alignment: relaxed
	Domain policy: none
	Subdomain policy: unspecified
	Aggregate report URIs:
		mailto:postmaster@your-domain.com
	Failure report URIs:
		(none)

DMARC Test E-Mail

Send an email from your domain to another domain's account. If DMARC is configured correctly then you will see dmarc=pass in the Authentication-Results: header.

Authentication-Results: dmarc.icloud.com; dmarc=pass header.from=your-domain.com
X-DMARC-Info: pass=pass; dmarc-policy=none; s=r1; d=r1; pdomain=your-domain.com
X-DMARC-Policy: v=DMARC1; p=none; pct=100; rua=mailto:postmaster@your-domain.com
Authentication-Results: dkim-verifier.icloud.com;
	dkim=pass (2048-bit key) header.d=your-domain.com header.i=@your-domain.com header.b=wVFZ+19t
Authentication-Results: spf.icloud.com; spf=pass (spf.icloud.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=you@your-domain.com
Received-SPF: pass (spf.icloud.com: domain of you@your-domain.com designates 1.2.3.4 as permitted sender) receiver=spf.icloud.com; client-ip=1.2.3.4; helo=mail.your-domain.org; envelope-from=you@your-domain.com

Interpret a DMARC Report

There are two kinds of DMARC reports.

  • Daily XML-based [1] aggregate report generated by Gmail, Yahoo, Hotmail, etc.
  • Real-time forensic reports (copies of individual pieces of email that fail the DMARC check)

Normally you only want to receive the aggregate (rua) report. The data that DMARC produces is invaluable for understanding what is going on for any given email domain. However, raw DMARC report data is super hard to read and understand.

Postmark offers a free service to process these reports. The nice part about Postmark is that you can tell receiving email servers to send XML reports directly to Postmark for processing. So instead of entering your email address in the DMARC record, you enter an email address of postmarkapp.com that is unique to you.

v=DMARC1; p=none; pct=100; fo=1; rua=mailto:unique-to-you@dmarc.postmarkapp.com;

You can also specify multiple email addresses, separated by commas.

v=DMARC1; p=none; pct=100; fo=1; rua=mailto:unique-to-you@dmarc.postmarkapp.com,mailto:dmarc-report@your-domain.com;

After your DMARC record has been verified by Postmark, you will receive a DMARC report weekly every Monday in your email inbox. You don’t need to register an account at Postmark.

Many other firms exist to create DMARC reports. If you are into a lot of E-Mail marketing, you should check some more out.

  1. https://datatracker.ietf.org/doc/html/rfc7489#appendix-C

Reference:



E-Mail Relay - Linux in the House - https://linux-in-the-house.org Creative Commons License