Optional — Block Spam

 Published on 16 Feb 2025 .  Filed in Projects .  1760 words

Reject Invalid EHLO/HELO Hostname

Some spammers don’t provide a valid HELO/EHLO hostname during SMTP communication. They can be non-fully qualified domain name, or a domain name doesn’t exist or only for an internal network. For example, a spammer using an Amazon EC2 instance to send spam is logged on my server as follows:

Aug 16 04:21:13 email postfix/smtpd[7070]: connect from ec2-54-237-201-103.compute-1.amazonaws.com[54.237.201.103]
Aug 16 04:21:13 email policyd-spf[7074]: prepend Received-SPF: None (mailfrom) identity=mailfrom; client-ip=54.237.201.103; helo=ip-172-30-0-149.ec2.internal; envelope-from=superdiem@carpaythe.tk; receiver=<UNKNOWN>

As you can see, the HELO hostname is ip-172-30-0-149.ec2.internal , which is only valid in AWS internal network. It has no valid A record nor MX record.

To enable HELO/EHLO hostname restriction, edit Postfix main configuration file:

  sudo nano /etc/postfix/main.cf

Add the following lines to reject SMTP client that does not provide valid HELO/EHLO hostname:

smtpd_helo_required = yes
smtpd_helo_restrictions =
    permit_mynetworks
    permit_sasl_authenticated
    check_helo_access hash:/etc/postfix/helo_access
    reject_invalid_helo_hostname
    reject_non_fqdn_helo_hostname
    reject_unknown_helo_hostname
  • permit_mynetworks: Allow IPs defined in this directive
  • permit_sasl_authenticated: Allow authenticated users.
  • check_helo_access: Allow hostnames defined in this file.
  • reject_invalid_helo_hostname: Reject clients who provide malformed HELO/EHLO hostname.
  • reject_non_fqdn_helo_hostname: Reject non-fully qualified HELO/EHLO hostname.
  • reject_unknown_helo_hostname: Reject email with HELO/EHLO hostname has neither DNS A record nor MX record.

Now create the file /etc/postfix/helo_access to whitelist domains that do not use a valid hostname:

optimus-webapi-prod-2.localdomain      OK
va-massmail-02.rakutenmarketing.com    OK

Then run the following command to create the /etc/postfix/helo_access.db file:

  sudo postmap /etc/postfix/helo_access

Save and close the file. Then reload Postfix.

  sudo systemctl reload postfix

Reject Emails from Non-Valid Domains

The MAIL FROM address is also known as envelope from address. Some spammers use a non-existent domain in the MAIL FROM address. If a domain name has no MX record, Postfix will find the A record of the main domain and send email to that host. If the sender domain has neither MX record nor A record, Postfix can’t send email to that domain. So reject those emails that you can’t reply to:

  sudo nano /etc/postfix/main.cf

Make the smtpd_sender_restrictions configuration directive look as follows:

smtpd_sender_restrictions =
   permit_mynetworks
   permit_sasl_authenticated
   reject_unknown_sender_domain
  • reject_unknown_sender_domain: Checks the sender's email domain in the MAIL FROM address to ensure it has a valid DNS configuration. It requires that the domain in the sender's email address has a valid MX or A record.

Then restart Postfix for the change to take effect:

  sudo systemctl restart postfix

Reject Emails if No Valid PTR or A Record for Hostname

A legitimate email server should have a PTR record and a valid A record for its hostname. The IP address returned from DNS A record should match the IP address of the contacting email server. So tell Postfix to perform these checks on SMTP client server:

  sudo nano /etc/postfix/main.cf

Make the smtpd_sender_restrictions configuration directive look as follows:

smtpd_sender_restrictions =
   permit_mynetworks
   permit_sasl_authenticated
   reject_unknown_sender_domain
   reject_unknown_reverse_client_hostname
   reject_unknown_client_hostname
  • reject_unknown_reverse_client_hostname: Checks the reverse DNS (PTR record) of the client’s IP address (the IP of the server connecting to your Postfix server) to ensures that the IP address of the sending server can be mapped back to a valid hostname.
  • reject_unknown_client_hostname: Performs a check that the hostname obtained from the PTR record has a valid forward DNS (A record) pointing back to the SMTP server that connecting to your Postfix server.

Save and close the file. Then restart Postfix for the change to take effect.

  sudo systemctl restart postfix

Greylisting

Introduction

Greylisting is a spam prevention technique that can be implemented in Postfix to reduce unwanted email by temporarily rejecting new email senders and requiring them to retry. Because most legitimate mail servers will retry delivery after a short delay, while many spam servers or bots will not attempt to resend.

How Greylisting Works ?

  1. Initial Rejection: When an email is received from an unfamiliar sender IP, sender address, and recipient address combination, Postfix temporarily rejects it with a 451 (Temporary failure) response. This tells the sending server to try again later.
  2. Retry Attempt: If the sending server retries after a certain period, Postfix recognizes the retry as legitimate and accepts the email.
  3. Temporary Listing: After accepting the email, Postfix remembers this sender-recipient combination for a configurable period, so subsequent emails from this source are accepted without delay.

Configuration

Postgrey is a greylisting policy server for Postfix. So first install install postgrey:

  sudo apt install postgrey

Start and enable it with systemctl:

  sudo systemctl start postgrey
  sudo systemctl enable postgrey

On Debian and Ubuntu, it listens on TCP port 10023 on localhost:

  sudo netstat -lnpt | grep postgrey

Next, we need to edit Postfix main configuration file to make it use the greylisting policy server.

  sudo nano /etc/postfix/main.cf

Add the line check_policy_service inet:127.0.0.1:10023 to smtpd_recipient_restrictions configuration directive:

smtpd_recipient_restrictions =
   reject_unauth_destination,
   check_policy_service unix:private/quota-status,
   check_policy_service unix:private/policyd-spf,
   check_policy_service inet:127.0.0.1:10023

Then restart Postfix.

  sudo systemctl restart postfix

From now on, Postgrey will reject an email if the sender triplet (sender IP address, sender email address, recipient email address) is new. The following log message in /var/log/mail.log (or sudo journalctl -u postgrey) shows a new sender triplet. The action greylist means this email message was rejected:

postgrey[1016]: action=greylist, reason=new, client_name=unknown, client_address=117.90.24.148/32, sender=pnccepjeu@rhknqj.net, recipient=xiao@linuxbabe.com

Whitelist Domains

Greylisting can result in bad experience for the end user, as the user has to wait another several minute for the email to arrive. To minimize this bad experience, you can create a whitelist, and use a second MX record that points to the same host. Whitelist

Postgrey ships with two whitelist files (/etc/postgrey/whitelist_clients and /etc/postgrey/whitelist_recipients). The former contains a list of hostnames and the latter contains a list of recipient addresses.

By default, Google’s mail servers are whitelisted. No matter the sender is using a @gmail.com address or other address, as long as the sender is using Google’s mail server, Postgrey won’t reject the email. The following line in my /var/log/mail.log file shows this:

postgrey[1032]: action=pass, reason=client whitelist, client_name=mail-yb0-f190.google.com

You can add other hostnames in /etc/postgrey/whitelist_clients file, like:

facebook.com
bounce.twitter.com
blogger.com
email.medium.com

Now let's create a secondary MX record in DNS with the same IP address pointing to your same Potfix server to allow the SMTP client to retry immediately after the first mail server rejects the mail. For example:

Record Type    Name      Mail Server            Priority

MX             @         mail1.example1.com     0
MX             @         mail2.example1.com     5

The sender will try the first mail server (with priority 0). If mail1.example1.com rejects email by greylisting, then the sender would immediately try the second mail server (with priority 5).

Since two mail server hostnames have the same IP address, when the sender tries the second mail server hostname, the email will be accepted immediately (if all other checks pass) and end users will not notice the email delay caused by greylisting.

Note that this requires you to set a very small delay time like 1 second in /etc/default/postgrey. The delay time tells the SMTP client how many seconds to wait before sending again. If the delay time is not small enough, then the second email delivery would still be rejected:

POSTGREY_OPTS="--inet=127.0.0.1:10023 --delay=1"

Finally restart Postgrey:

  sudo systemctl restart postgrey

Troubleshooting

If you see the following error in mail log (/var/log/mail.log)

warning: connect to 127.0.0.1:10023: Connection refused
warning: problem talking to server 127.0.0.1:10023: Connection refused

The problem is that postgrey is not running. You need to specify 127.0.0.1 as the listening address in /etc/default/postgrey file. So change the following line:

POSTGREY_OPTS="--inet=10023"

to

POSTGREY_OPTS="--inet=127.0.0.1:10023"

Then restart postgrey:

  sudo systemctl restart postgrey

Check if it’s listening:

  sudo netstat -lnpt | grep 10023

Use Public Realtime Blacklists

There are spam emails that are sent from servers that has a valid hostname, valid PTR record and can pass all previous checks. In this case, you can use blacklisting to reject spam. There are many public realtime blacklists (RBL), also known as DNSBLs (DNS based lists). By realtime it means that the list is always changing. An IP address or domain name could be on the list today and off the list tomorrow, so you could get different result depending on when you query the list.

You can use multiple blacklists to block spam. Go to www.debouncer.com and mxtoolbox.com, enter the spammer’s domain and/or IP address to see which blacklists are blocking them, then you can use those blacklists. For example, I found that spammers are blacklisted by one of the following blacklists:

  • dbl.spamhaus.org
  • zen.spamhaus.org
  • multi.uribl.com
  • ivmURI
  • InvaluementURI

So I can add the following configurations in /etc/postfix/main.cf file. Some public blacklisting service requires monthly fee. For now, I’m using the free service of spamhaus.org:

smtpd_recipient_restrictions =
   permit_mynetworks,
   permit_sasl_authenticated,
   check_policy_service unix:private/policyd-spf,
   check_policy_service inet:127.0.0.1:10023,
   check_client_access hash:/etc/postfix/rbl_override,
   reject_rhsbl_helo dbl.spamhaus.org,
   reject_rhsbl_reverse_client dbl.spamhaus.org,
   reject_rhsbl_sender dbl.spamhaus.org,
   reject_rbl_client zen.spamhaus.org
  • check_client_access: File containing a list of domains you want to whitelist.
  • reject_rhsbl_helo: Postfix reject email when the client HELO/EHLO hostname is blacklisted.
  • reject_rhsbl_reverse_client: Checks the PTR (reverse DNS) record of the SMTP client’s IP address against Spamhaus's DBL. If the hostname is blacklisted, reject the email.
  • reject_rhsbl_sender Postfix reject email when the domain in MAIL FROM header field is blacklisted.
  • reject_rbl_client: This is an IP-based blacklist. When the client IP address is backlisted, reject the email.

NOTE: Some spammers use Google’s mail server, so reject_rhsbl_helo is ineffective, but most of them use their own domain names in the MAIL FROM header field, so reject_rhsbl_sender will be effective.

Sometimes legitimate mail servers are blacklisted. In this case, we ask the Postfix server to whitelist these domains by listing them in a file:

  sudo nano /etc/postfix/rbl_override

Add the domains you want to whitelist:

dripemail2.com  OK           //This domain belongs to drip.com
mlsend.com      OK           //This domain belongs to mailerlite email marketing service

Then run the following command to create the rbl_override.db file.

  sudo postmap /etc/postfix/rbl_override

Reload Postfix for the changes to take effect.

  sudo systemctl reload postfix

Use Public Whitelist to Reduce False Positive

Maintaining a private whitelist is necessary sometimes, but you can also use public whitelists, the most famous of which is dnswl.org. Currently, there is only a whitelist for IP address. Domain name whitelist is in beta. To use it, put the following line in smtpd_recipient_restrictions.

permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3],

Like below. It should be placed above the reject_rbl_client check.

smtpd_recipient_restrictions =
   permit_mynetworks,
   permit_sasl_authenticated,
   check_policy_service unix:private/policyd-spf,
   check_policy_service inet:127.0.0.1:10023,
   check_client_access hash:/etc/postfix/rbl_override,
   reject_rhsbl_helo dbl.spamhaus.org,
   reject_rhsbl_reverse_client dbl.spamhaus.org,
   reject_rhsbl_sender dbl.spamhaus.org,
   permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3],
   reject_rbl_client zen.spamhaus.org

Another well-known whitelist is swl.spamhaus.org, so you can also add it below the permit_dnswl_client list.dnswl.org=127.0.[0..255].[1..3], line:

permit_dnswl_client swl.spamhaus.org,

It’s impossible for an IP address to be listed in Spamhaus whitelist and blacklist at the same time, so if you only use Spamhaus blacklist in Postfix, then it’s not necessary to check against Spamhaus whitelist.

TODO Anti-spam — SpamAssassin

  • State "TODO" from [2024-11-02 Sat 17:45]

Run the following command to install SpamAssassin from the default Debian/Ubuntu software repository. Spamc is the client for SpamAssassin spam filtering daemon.

sudo apt install spamassassin spamc