Part 4: Set Up SPF and DKIM with Postfix on CentOS/RHEL Mail Server

After completing part 1 and part 2, we have a working Postfix SMTP server and Dovecot IMAP server. We can send and receive email using a desktop email client. Although I have created correct MX, A and PTR record, my emails were flagged as spam by Gmail and Outlook mail. So in this part, we are going to look at how to improve email delivery to recipient’s inbox by setting up SPF and DKIM on CentOS/RHEL server.

What are SPF and DKIM Records?

SPF and DKIM are two types of TXT records in DNS that can help with preventing email spoofing and making legitimate emails delivered into the recipient’s inbox instead of spam folder. If your domain is abused by email spoofing, then your emails are likely to landed in recipient’s spam folder if the recipient didn’t add you in address book.

SPF (Sender Policy Framework) record specifies which hosts or IP addresses are allowed to send emails on behalf of a domain. You should allow only your own email server or your ISP’s server to send emails for your domain.

DKIM (DomainKeys Identified Mail) uses a private key to add a signature to emails sent from your domain. Receiving SMTP servers verify the signature by using the corresponding public key, which is published in your domain’s DNS records.

Create an SPF Record in DNS

In your DNS management interface, create a new TXT record like below.

TXT  @   v=spf1 mx ~all

create spf record in DNS

Where:

  • TXT indicates this is a TXT record.
  • Enter @ in the name field to represent the apex domain name.
  • v=spf1 indicates this is a SPF record and the SPF record version is SPF1.
  • mx means all hosts listed in the MX records are allowed to send emails for your domain and all other hosts are disallowed.
  • ~all indicates that emails from your domain should only come from hosts specified in the SPF record. Emails sent from other hosts will be flagged as forged. Possible alternatives are +all, -all, ?all, but they are rarely used.

-all means that emails sent from not-allowed hosts should be rejected, never to land in the recipient’s inbox or spam folder. I have seen it used by facebook.com, but we generally don’t need such strict policy.

Note that some DNS managers require you to wrap the SPF record with double-quotes like below.

TXT  @   "v=spf1 mx ~all"

To check if your SPF record is propagated to the public Internet, you can use the dig utility on your Linux machine like below:

dig your-domain.com txt

The txt option tells dig that we only want to query TXT records.

use dig utility to query spf record

You can also use online SPF validator such as spf.myisp.ch to see which hosts are allowed to send emails for your domain and debug your SPF record if any error occurs. The dmarcian SPF surveyor can help test your SPF record syntax.

Configuring SPF Policy Agent

We also need to tell our Postfix SMTP server to check the SPF record of incoming emails to detect forged emails. First install required packages:

sudo dnf install python3-pip 
sudo -H pip3 install pyspf py3dns pypolicyd-spf

Then add a user for policyd-spf.

sudo adduser policyd-spf --user-group --no-create-home -s /bin/false

Edit the Postfix master process configuration file.

sudo nano /etc/postfix/master.cf

Add the following lines at the end of the file, which tells Postfix to start the SPF policy daemon when it’s starting itself. Policyd-spf will run as the policyd-spf user.

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

Save and close the file. Next, edit Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Append the following lines at the end of the file. The first line specifies the Postfix policy agent timeout setting (for querying DNS). The following lines will impose restriction on incoming emails by checking SPF record.

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

Save and close the file. Then 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).

Setting Up DKIM

Two common pieces of software that can do DKIM signing and verification on Linux are OpenDKIM and Amavis. OpenDKIM is not included in CentOS 8/RHEL 8 repository, so we will use Amavis. Amavis (A Mail Virus Scanner) is a high-performance interface between message transfer agent (MTA) and content filters. It’s commonly used for

  • virus-scanning by integrating with ClamAV
  • spam-checking by integrating with SpamAssassin
  • DKIM signing and verification

In this tutorial, we will focus on DKIM signing and verification. Virus-scanning and spam-checking will be discussed in a future tutorial. Note that Amavis requires a fair amount of RAM to run. If your server has only 1GB RAM, you probably need to upgrade to 2GB RAM.

March 08, 2020 Update: As of now, OpenDKIM is included in the EPEL repository. If you prefer to use OpenDKIM to do DKIM singing and verification, read the article linked below.

If you prefer to use Amavis, continue reading the following content.

Install Amavisd-new on CentOS 8/RHEL 8

Amavisd-new is written in Perl. We need to enable the EPEL (Extra Packages for Enterprise Linux) and CodeReady Linux Builder repository on RHEL 8 to install some Perl dependencies for Amavisd-new.

sudo dnf install epel-release

sudo subscription-manager repos --enable=codeready-builder-for-rhel-8-x86_64-rpms

On CentOS 8, enable the EPEL (Extra Packages for Enterprise Linux) and PowerTools repository to install Perl dedpendencies for Amavisd-new.

sudo dnf install epel-release
sudo dnf config-manager --set-enabled PowerTools

Then install the amavisd-new package.

sudo dnf install amavisd-new

Start Amavis.

sudo systemctl start amavisd

Enable auto-start at boot time.

sudo systemctl enable amavisd

Check its status:

systemctl status amavisd

Sample output:

 amavisd.service - Amavisd-new is an interface between MTA and content checkers.
   Loaded: loaded (/usr/lib/systemd/system/amavisd.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-01-19 06:18:14 EST; 40s ago
     Docs: http://www.ijs.si/software/amavisd/#doc
 Main PID: 10827 (/usr/sbin/amavi)
    Tasks: 3 (limit: 5047)
   Memory: 8.8M
   CGroup: /system.slice/amavisd.service
           ├─10827 /usr/sbin/amavisd (master)                                                                                                                                                              
           ├─10831 /usr/sbin/amavisd (virgin child)                                                                                                                                                        
           └─10832 /usr/sbin/amavisd (virgin child)

Amavisd listens on 127.0.0.1:10024, as can be seen with:

sudo netstat -lnpt | grep amavisd

centos 8 amavisd-new

And it runs as the amavis user.

Configuring Amavis

The main configuration file is /etc/amavisd/amavisd.conf.  Open it with:

sudo nano /etc/amavisd/amavisd.conf

First, find the following line. (In Nano text editor, you can press Ctrl+W to search a string.)

$mydomain = 'example.com';

Change example.com to your apex domain name.

$mydomain = 'yourdomain.com';

Then find the following line.

# $myhostname = 'host.example.com';

Remove the comment character (#) and change the hostname to your real hostname like below. $myhostname is used by Amavisd-new for node identification, and it is important to get it right for ESMTP EHLO, loop detection, and so on.

$myhostname = 'mail.yourdomain.com';

By default, DKIM signing and verification are both enabled, as indicated by the following two lines, which is fine.

$enable_dkim_verification = 1;  # enable DKIM signatures verification
$enable_dkim_signing = 1;       # load DKIM signing code, keys defined by dkim_key

Next, we can add the following line, which defines the DKIM selecor and DKIM key for this particular domain name.

dkim_key('yourdomain.com', '20200119', '/var/spool/amavisd/dkim/yourdomain.com.pem');

You can use whatever name for the DKIM selector, but I found it’s convienent to use the current date (January 19, 2020) as the DKIM selector. The private key is located at /var/spool/amavised/dkim/yourdomain.com.pem. Then we also need to add the following lines.

@dkim_signature_options_bysender_maps = ( {
    # 'd' defaults to a domain of an author/sender address,
    # 's' defaults to whatever selector is offered by a matching key

    # explicit 'd' forces a third-party signature on foreign (hosted) domains
    "yourdomain.com"  => { d => "yourdomain.com", a => 'rsa-sha256', ttl => 10*24*3600 },

    # catchall defaults
    '.' => { a => 'rsa-sha256', c => 'relaxed/simple', ttl => 30*24*3600 },
} );

This tells Amavis that emails sent from yourdomain.com should be signed with the DKIM key belonging to yourdomain.com. If you have two domains, you can use one of the domains’ DKIM key to sign emails of both domains, but it’s a good practice to use a dedicated DKIM key for each domain. Save and close the file.

Then we need to generate a DKIM private key. Run the following command to create the /var/spool/amavisd/dkim/ directory to store the keys.

sudo mkdir /var/spool/amavisd/dkim/

Generate a private key for your domain.

sudo amavisd genrsa /var/spool/amavisd/dkim/yourdomain.com.pem 2048

Display the public key.

sudo amavisd -c /etc/amavisd/amavisd.conf showkeys

The string after the p parameter is the public key.

amavisd-show-dkim-key

In you DNS manager, create a TXT record, enter 20200119._domainkey in the name field. If you used a different DKIM selector, replace 20200119 with your real DKIM selector.

Then go back to the terminal window, copy everything in the parentheses and paste it into the value field of the DNS record. You need to delete all double quotes and line breaks in the value field. If you don’t delete them, then key test will probably fail.

adding DKIM record

After saving your changes. Check the TXT record with this command.

dig  TXT   20200119._domainkey.yourdomain.com

Now you can run the following command to test if your DKIM DNS record is correct.

sudo amavisd -c /etc/amavisd/amavisd.conf testkeys

If the DNS record is correct, the test will pass.

TESTING#1 linuxbabe.com: 20200119._domainkey.linuxbabe.com => pass

Using A Dedicated Port for Email Submissions

The default Amavis listening port 10024 is for scanning incoming email messages. It’s a good practice to use a different port such as 10026 for email submissions from authenticated users. Edit the Amavis main configuration file.

sudo nano /etc/amavisd/amavisd.conf

Find the following line.

$inet_socket_port = 10024;

Add a # character to comment it out.

#$inet_socket_port = 10024;

Then uncomment the following line, so Amavisd will also listen on port 10026.

$inet_socket_port = [10024,10026];

Scrolling down a little bit, you can find the following line, which sets the “ORIGINATING” policy for port 10026.

$interface_policy{'10026'} = 'ORIGINATING';

Then you can find the following lines, which defines the “ORIGINATING” policy.

$policy_bank{'ORIGINATING'} = {  # mail supposedly originating from our users
  originating => 1,  # declare that mail was submitted by our smtp client
  allow_disclaimers => 1,  # enables disclaimer insertion if available
  # notify administrator of locally originating malware
  virus_admin_maps => ["virusalert\@$mydomain"],
  spam_admin_maps  => ["virusalert\@$mydomain"],
  warnbadhsender   => 1,
  # forward to a smtpd service providing DKIM signing service
  forward_method => 'smtp:[127.0.0.1]:10027',
  # force MTA conversion to 7-bit (e.g. before DKIM signing)
  smtpd_discard_ehlo_keywords => ['8BITMIME'],
  bypass_banned_checks_maps => [1],  # allow sending any file names and types
  terminate_dsn_on_notify_success => 0,  # don't remove NOTIFY=SUCCESS option
};

In the above lines, you can see that by default Amavis would forward emails to a SMTPD service providing DKIM signing servie. Since we are going to use Amavis itself to sign emails submitted from authenticated users, we need to comment out the forward_method directive.

# forward_method => 'smtp:[127.0.0.1]:10027',

Save and close the file. Next, we need to tell SELinux to allow Amavis to use port 10026. Install the following package, which provides the semanage command.

sudo dnf install policycoreutils-python-utils

Then set the port type of 10026 to amavisd_recv_port_t, so Amavis will be able to listen on port 10026.

sudo semanage port -m -t amavisd_recv_port_t -p tcp 10026

Restart Amavis

sudo systemctl restart amavisd

Check its status to see if the restart is successful.

systemctl status amavisd

Integrating Postfix SMTP Server with Amavis

Amavisd-new works as a SMTP proxy. Email is fed to it through SMTP, processed, and fed back to the MTA through a new SMTP connection.

Edit the Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Add the following line at the end of the file. This tells Postfix to turn on content filtering by sending every incoming email messages to Amavis listening on 127.0.0.1:10024.

content_filter = smtp-amavis:[127.0.0.1]:10024

Save and close the file. Then edit the master.cf file.

sudo nano /etc/postfix/master.cf

Add the following lines at the end of the file. This instructs Postfix to use a special SMTP client component called smtp-amavis to deliver email messages to Amavis. Please allow at least one whitespace character (tab or spacebar) before each -o.  In postfix configurations, a preceding whitespace character means that this line is continuation of the previous line.

smtp-amavis   unix   -   -   n   -   2   smtp
    -o syslog_name=postfix/amavis
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
    -o max_use=20

Then add the following lines at the end of the file. This tells Postfix to run an additional smtpd daemon listening on 127.0.0.1:10025 to receive email messages back from Amavis.

127.0.0.1:10025   inet   n    -     n     -     -    smtpd
    -o syslog_name=postfix/10025
    -o content_filter=
    -o mynetworks_style=host
    -o mynetworks=127.0.0.0/8
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o strict_rfc821_envelopes=yes
    -o smtp_tls_security_level=none
    -o smtpd_tls_security_level=none
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_end_of_data_restrictions=
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_address_mappings

Next, add the following line to the submission service.

 -o content_filter=smtp-amavis:[127.0.0.1]:10026

Like this:

submission     inet     n    -    y    -    -    smtpd
    -o syslog_name=postfix/submission
    -o smtpd_tls_security_level=encrypt
    -o smtpd_tls_wrappermode=no
    -o smtpd_sasl_auth_enable=yes
    -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
    -o content_filter=smtp-amavis:[127.0.0.1]:10026

So emails submited from authenticated users will be forwarded to Amavis for DKIM signing. If you have enabled the smtps service for Microsoft Outlook users, then you also need to add this line to the smtps service.

smtps     inet  n       -       y       -       -       smtpd
    -o syslog_name=postfix/smtps
    -o smtpd_tls_wrappermode=yes
    -o smtpd_sasl_auth_enable=yes
    -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
    -o smtpd_sasl_type=dovecot
    -o smtpd_sasl_path=private/auth
    -o content_filter=smtp-amavis:[127.0.0.1]:10026

Save and close the file. Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

Check its status to see if the restart is successful.

systemctl status postfix

SPF and DKIM Check

You can now send a test email from your mail server to your Gmail account to see if SPF and DKIM checks are passed. On the right side of an opened email message in Gmail, if you click the show original button from the drop-down menu, you can see the authentication results.

Gmail SPF and DKIM check

If your message is not signed and DKIM check failed, you may want to check postfix log (/var/log/maillog) to see what’s wrong in your configuration. Your email server will also perform SPF and DKIM check on sender’s domain. You can see the results in the email headers. The following is SPF and DKIM check on a sender using Gmail.

Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::c2d; helo=mail-yw1-xc2d.google.com; [email protected]; receiver=<UNKNOWN> 
Authentication-Results: mail.linuxbabe.com;
	dkim=pass (2048-bit key; unprotected) header.d=gmail.com [email protected] header.b="XWMRd2co";
	dkim-atps=neutral

Testing Email Score and Placement

Now you can go to https://www.mail-tester.com. You will see a unique email address. Send an email from your domain to this address and then check your score. As you can see, I got a perfect score.

imporve email server reputation

Mail-tester.com can only show you a sender score. There’s another service called GlockApps that allow you to check if your email is placed in the recipient’s inbox or spam folder, or rejected outright. It supports many popular email providers like Gmail, Outlook, Hotmail, YahooMail, iCloud mail, etc

glockapps email placement test

Microsoft Mailboxes (Hotmail.com, Outlook.com)

Microsoft seems to be using an internal blacklist that block many legitimate IP addresses. If your emails are rejected by outlook or hotmail, you need to submit the sender information form. After that, your email will be accepted by outlook/hotmail, but may still be labeled as spam. In my test, the email landed in my Gmail inbox. However, it’s stilled labeled as spam in my outlook.com email although both SPF and DKIM are passed.

What if Your Emails Are Still Being Marked as Spam?

I have more tips for you in this article: How to stop your emails being marked as spam.

Next Step

In part 5, we will see how to create DMARC record to protect your domain from email spoofing. As always, if you found this post useful, then subscribe to our free newsletter to receive more useful articles, or follow us on Twitter or like our Facebook page.

Rate this tutorial
[Total: 1 Average: 5]

23 Responses to “Part 4: Set Up SPF and DKIM with Postfix on CentOS/RHEL Mail Server

  • Francois
    4 weeks ago

    I think opendkim is now added to epel.
    Might you consider adding it to the tutorial alongside Amavis?

  • I have gone all through all four parts and triple checked everything . I am getting the following error in the maillog. any ideas. I cannot seem to find anything on this one for centos. I am running centos 8. All I seem to be able to find are issues with the debian flavors related to this.

    Mar  4 19:57:21 mail postfix/smtpd[10124]: error: unsupported dictionary type: smtp-amavis

    I am getting a 9/10 on mail-tester. The only message is that I am Not Authenticated. Your message is not signed with DKIM

    • It seems you used smtp-amavis as format for a lookup table or map in /etc/postfix/main.cf file.

      Can you show your configurations with postconf -n?

  • alias_database = hash:/etc/aliases
    alias_maps = hash:/etc/aliases
    command_directory = /usr/sbin
    compatibility_level = 2
    daemon_directory = /usr/libexec/postfix
    data_directory = /var/lib/postfix
    debug_peer_level = 2
    debugger_command = PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin ddd $daemon_directory/$process_name $process_id & sleep 5
    html_directory = no
    inet_interfaces = all
    inet_protocols = ipv4
    mail_owner = postfix
    mailbox_size_limit = 0
    mailbox_transport = lmtp:unix:private/dovecot-lmtp
    mailq_path = /usr/bin/mailq.postfix
    manpage_directory = /usr/share/man
    message_size_limit = 0
    meta_directory = /etc/postfix
    mydestination = $myhostname, mail.ftnetworks.org, localhost.ftnetworks.org, localhost
    mydomain = ftnetworks.org
    myhostname = mail.ftnetworks.org
    mynetworks = 10.10.175.0/24, 66.119.60.64/28, 127.0.0.0/8
    myorigin = ftnetworks.org
    newaliases_path = /usr/bin/newaliases.postfix
    policyd-spf_time_limit = 3600
    queue_directory = /var/spool/postfix
    readme_directory = /usr/share/doc/postfix/README_FILES
    sample_directory = /usr/share/doc/postfix/samples
    sendmail_path = /usr/sbin/sendmail.postfix
    setgid_group = postdrop
    shlib_directory = /usr/lib64/postfix
    smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt
    smtp_tls_CApath = /etc/pki/tls/certs
    smtp_tls_loglevel = 1
    smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
    smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
    smtp_tls_security_level = may
    smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf content_filter = smtp-amavis:[127.0.0.1]:10024
    smtpd_tls_cert_file = /etc/letsencrypt/live/mail.ftnetworks.org/fullchain.pem
    smtpd_tls_key_file = /etc/letsencrypt/live/mail.ftnetworks.org/privkey.pem
    smtpd_tls_loglevel = 1
    smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
    smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
    smtpd_tls_security_level = may
    smtputf8_enable = no
    unknown_local_recipient_reject_code = 550
    virtual_alias_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
    virtual_gid_maps = static:2000
    virtual_mailbox_base = /var/vmail
    virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
    virtual_mailbox_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
    virtual_minimum_uid = 2000
    virtual_transport = lmtp:unix:private/dovecot-lmtp
    virtual_uid_maps = static:2000

    • This line is wrong.

      smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf content_filter = smtp-amavis:[127.0.0.1]:10024

      You should put

      content_filter = smtp-amavis:[127.0.0.1]:10024

      on it’s own line.

      • Changed that and now I have a new set of issues.
        Mar 4 22:37:41 mail postfix/error[4036]: E260A20548D6: to=, relay=none, delay=1.3, delays=0.07/1.2/0/0.04, dsn=4.3.0, status=deferred (mail transport unavailable)

    • Can you show me your /etc/postfix/master.cf file?

      cat /etc/postfix/master.cf
      • Ok I got that all working. I had something indented that should not be. I fixed that and everything appears to be working now. However; when I send an email to my gmail account I do not see anything that says DKIM. only SPF.
        Message ID
        Created at: Thu, Mar 5, 2020 at 7:07 PM (Delivered after 15 seconds)
        From: Trever Noggle
        To: [email protected]
        Subject: Test
        SPF: PASS with IP 66.119.60.68 Learn more

        The above testing of the keys show that it passes.
        TESTING#1 ftnetworks.org: 20200304._domainkey.ftnetworks.org => pass

      • [[email protected] ~]# cat /etc/postfix/master.cf
        #
        # Postfix master process configuration file. For details on the format
        # of the file, see the master(5) manual page (command: “man 5 master” or
        # on-line: http://www.postfix.org/master.5.html).
        #
        # Do not forget to execute “postfix reload” after editing this file.
        #
        # ==========================================================================
        # service type private unpriv chroot wakeup maxproc command + args
        # (yes) (yes) (no) (never) (100)
        # ==========================================================================
        smtp inet n – n – – smtpd
        #smtp inet n – n – 1 postscreen
        #smtpd pass – – n – – smtpd
        #dnsblog unix – – n – 0 dnsblog
        #tlsproxy unix – – n – 0 tlsproxy
        #submission inet n – n – – smtpd
        # -o syslog_name=postfix/submission
        # -o smtpd_tls_security_level=encrypt
        # -o smtpd_sasl_auth_enable=yes
        # -o smtpd_tls_auth_only=yes
        # -o smtpd_reject_unlisted_recipient=no
        # -o smtpd_client_restrictions=$mua_client_restrictions
        # -o smtpd_helo_restrictions=$mua_helo_restrictions
        # -o smtpd_sender_restrictions=$mua_sender_restrictions
        # -o smtpd_recipient_restrictions=
        # -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
        # -o milter_macro_daemon_name=ORIGINATING
        submission inet n – y – – smtpd
        -o syslog_name=postfix/submission
        -o smtpd_tls_security_level=encrypt
        -o smtpd_tls_wrappermode=yes
        -o smtpd_sasl_auth_enable=yes
        -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
        -o content_filter=smtp-amavis:[127.0.0.1]:10026
        smtps inet n – y – – smtpd
        -o syslog_name=postfix/smtps
        -o content_filter=smtp-amavis:[127.0.0.1]:10026
        #smtps inet n – n – – smtpd
        # -o syslog_name=postfix/smtps
        # -o smtpd_tls_wrappermode=yes
        # -o smtpd_sasl_auth_enable=yes
        # -o smtpd_reject_unlisted_recipient=no
        # -o smtpd_client_restrictions=$mua_client_restrictions
        # -o smtpd_helo_restrictions=$mua_helo_restrictions
        # -o smtpd_sender_restrictions=$mua_sender_restrictions
        # -o smtpd_recipient_restrictions=
        # -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
        # -o milter_macro_daemon_name=ORIGINATING
        #628 inet n – n – – qmqpd
        pickup unix n – n 60 1 pickup
        cleanup unix n – n – 0 cleanup
        qmgr unix n – n 300 1 qmgr
        #qmgr unix n – n 300 1 oqmgr
        tlsmgr unix – – n 1000? 1 tlsmgr
        rewrite unix – – n – – trivial-rewrite
        bounce unix – – n – 0 bounce
        defer unix – – n – 0 bounce
        trace unix – – n – 0 bounce
        verify unix – – n – 1 verify
        flush unix n – n 1000? 0 flush
        proxymap unix – – n – – proxymap
        proxywrite unix – – n – 1 proxymap
        smtp unix – – n – – smtp
        relay unix – – n – – smtp
        -o syslog_name=postfix/$service_name
        # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
        showq unix n – n – – showq
        error unix – – n – – error
        retry unix – – n – – error
        discard unix – – n – – discard
        local unix – n n – – local
        virtual unix – n n – – virtual
        lmtp unix – – n – – lmtp
        anvil unix – – n – 1 anvil
        scache unix – – n – 1 scache
        #
        # ====================================================================
        # Interfaces to non-Postfix software. Be sure to examine the manual
        # pages of the non-Postfix software to find out what options it wants.
        #
        # Many of the following services use the Postfix pipe(8) delivery
        # agent. See the pipe(8) man page for information about ${recipient}
        # and other message envelope options.
        # ====================================================================
        #
        # maildrop. See the Postfix MAILDROP_README file for details.
        # Also specify in main.cf: maildrop_destination_recipient_limit=1
        #
        #maildrop unix – n n – – pipe
        # flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
        #
        # ====================================================================
        #
        # Recent Cyrus versions can use the existing “lmtp” master.cf entry.
        #
        # Specify in cyrus.conf:
        # lmtp cmd=”lmtpd -a” listen=”localhost:lmtp” proto=tcp4
        #
        # Specify in main.cf one or more of the following:
        # mailbox_transport = lmtp:inet:localhost
        # virtual_transport = lmtp:inet:localhost
        #
        # ====================================================================
        #
        # Cyrus 2.1.5 (Amos Gouaux)
        # Also specify in main.cf: cyrus_destination_recipient_limit=1
        #
        #cyrus unix – n n – – pipe
        # user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -r ${sender} -m ${extension} ${user}
        #
        # ====================================================================
        #
        # Old example of delivery via Cyrus.
        #
        #old-cyrus unix – n n – – pipe
        # flags=R user=cyrus argv=/usr/lib/cyrus-imapd/deliver -e -m ${extension} ${user}
        #
        # ====================================================================
        #
        # See the Postfix UUCP_README file for configuration details.
        #
        #uucp unix – n n – – pipe
        # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender – $nexthop!rmail ($recipient)
        #
        # ====================================================================
        #
        # Other external delivery methods.
        #
        #ifmail unix – n n – – pipe
        # flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
        #
        #bsmtp unix – n n – – pipe
        # flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient
        #
        #scalemail-backend unix – n n – 2 pipe
        # flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store
        # ${nexthop} ${user} ${extension}
        #
        #mailman unix – n n – – pipe
        # flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
        # ${nexthop} ${user}
        policyd-spf unix – n n – 0 spawn
        user=policyd-spf argv=/usr/local/bin/policyd-spf
        smtp-amavis unix – – n – 2 smtp
        -o syslog_name=postfix/amavis
        -o smtp_data_done_timeout=1200
        -o smtp_send_xforward_command=yes
        -o disable_dns_lookups=yes
        -o max_use=20
        127.0.0.1:10025 inet n – n – – smtpd
        -o syslog_name=postfix/10025
        -o content_filter=
        -o mynetworks_style=host
        -o mynetworks=127.0.0.0/8
        -o local_recipient_maps=
        -o relay_recipient_maps=
        -o strict_rfc821_envelopes=yes
        -o smtp_tls_security_level=none
        -o smtpd_tls_security_level=none
        -o smtpd_restriction_classes=
        -o smtpd_delay_reject=no
        -o smtpd_client_restrictions=permit_mynetworks,reject
        -o smtpd_helo_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o smtpd_end_of_data_restrictions=
        -o smtpd_error_sleep_time=0
        -o smtpd_soft_error_limit=1001
        -o smtpd_hard_error_limit=1000
        -o smtpd_client_connection_count_limit=0
        -o smtpd_client_connection_rate_limit=0
        -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_address_mappings
        [[email protected] ~]#

    • smtps     inet    n     –     y     –     –    smtpd
         -o syslog_name=postfix/smtps
         -o content_filter=smtp-amavis:[127.0.0.1]:10026
      

      Your smtps definition is wrong. It should be

      smtps     inet  n       -       y       -       -       smtpd
          -o syslog_name=postfix/smtps
          -o smtpd_tls_wrappermode=yes
          -o smtpd_sasl_auth_enable=yes
          -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
          -o smtpd_sasl_type=dovecot
          -o smtpd_sasl_path=private/auth
          -o content_filter=smtp-amavis:[127.0.0.1]:10026
      
      • I change that and tried again. Still no go.

        Message ID
        Created at: Fri, Mar 6, 2020 at 8:38 PM (Delivered after 12 seconds)
        From: Trever Noggle
        To: [email protected]
        Subject: One more test
        SPF: PASS with IP 66.119.60.68 Learn more

        Delivered-To: [email protected]
        Received: by 2002:a05:6402:1ac1:0:0:0:0 with SMTP id ba1csp1812878edb;
        Fri, 6 Mar 2020 19:38:44 -0800 (PST)

        Here we go

    • Send an email, then check the mail log (/var/log/maillog) to see if you can find any clue. My Amavis DKIM is working, and it shows the following log in this file.

      amavis[8184]: (08184-11) Passed CLEAN {RelayedOutbound}, ORIGINATING LOCAL [23.254.225.226]:47236 [23.254.225.226] -> , Queue-ID: 59F362106291, Message-ID: <[email protected]>, mail_id: yeUidFP2flDI, Hits: -1, size: 559, queued_as: AF1E92106293, dkim_new=20200119:linuxbabe.com, 3128 ms
      Mar 7 03:00:15 mail postfix/amavis/smtp[16682]: 59F362106291: to=, relay=127.0.0.1[127.0.0.1]:10026, delay=3.5, delays=0.13/0.17/0.04/3.1, dsn=2.0.0, status=sent (250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as AF1E92106293)

  • I am not sure what I am missing. I even started completely over with a new VM and followed your documentation and ended up with the same result.

    Message ID
    Created at: Sat, Mar 7, 2020 at 7:18 PM (Delivered after 5 seconds)
    From: Trever Noggle
    To: [email protected]
    Subject: New Test
    SPF: PASS with IP 66.119.60.68 Learn more

    • Mar 7 19:27:27 mail amavis[27425]: (27425-02) Passed CLEAN {RelayedOpenRelay}, [66.119.60.66]:60077 [66.119.60.66] -> , Queue-ID: 973BC60400C5, Message-ID: , mail_id: RsB4bSK3sQXp, Hits: 1.859, size: 623, queued_as: B37E160A4962, 1118 ms
      Mar 7 19:27:27 mail postfix/amavis/smtp[32392]: 973BC60400C5: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=1.1, delays=0.02/0/0.01/1.1, dsn=2.0.0, status=sent (250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as B37E160A4962)
      Mar 7 19:27:27 mail postfix/qmgr[30197]: 973BC60400C5: removed

    • It seems you are using port 25 as the SMTP port. Configure your SMTP client/email client to use port 587 or 465. Don’t use port 25.

      • That was it. Thank you so much.

        Message ID
        Created at: Sat, Mar 7, 2020 at 7:32 PM (Delivered after 30 seconds)
        From: Trever Noggle
        To: [email protected]
        Subject: Testing again.
        SPF: PASS with IP 66.119.60.68 Learn more
        DKIM: ‘PASS’ with domain ftnetworks.org Learn more

    • You put your real email address in the comments? I will need to replace them with a bogus one, or spammers will harvest your email address.

  • Mai Truong
    3 days ago

    I followed the instructions for the SPF configuration section but on the header of the email there is no Received-SPF

    • Check your mail log (/var/log/maillog) to find what’s wrong. My log has the following line.

      policyd-spf[18724]: prepend Received-SPF:

      Which indicates policyd-spf has added the Received-SPF; header.

  • aleksandr
    4 seconds ago

    Good afternoon – a very competent guide – but I ran into a problem – my public key – amavis does not want to become valid
    Could you help in solving this problem?

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • Please use the community (https://community.linuxbabe.com) for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.