Postfix GeoIP Based Rejections

If you intend to be able to reject connections from remote IP addresses if they’re from certain countries. This is how you do it. This method will reject ip address that has been mapped in GeoIP at smtp conversation stage. However, This tutorial has never been tested. nothing more than a prototype that i created in leisure time.

Software required:

  • postfix (tcp_table)
  • Perl
  • Perl Geo::IP module

main.cf:

127.0.0.1:2528_time_limit = 3600s
smtpd_client_restrictions =
	check_client_access tcp:[127.0.0.1]:2528

master.cf

127.0.0.1:2528 inet  n       n       n       -       0      spawn
	user=nobody argv=/etc/postfix/geo-reject.pl


geo-reject.pl

#!/usr/bin/perl
use strict;
use warnings;
use Sys::Syslog qw(:DEFAULT setlogsock);
use Geo::IP;

my $gi = Geo::IP->new(GEOIP_STANDARD);

# maps country code to reject (this is just example)
# add more country code you would like to reject
my @geo_map = (
	'JP' ,
	'US' ,
	'CN' ,
	'HK'
);

#
# Initalize and open syslog.
#
openlog('postfix/geoIP-reject','pid','mail');

#
# Autoflush standard output.
#
select STDOUT; $|++;

while (<>) {
	chomp;
	if (/^get\s(.+)$/i) {
		my $client_ip = $1;
		my $country_code = $gi->country_code_by_name($client_ip);

		if (defined $country_code)
		{
			if ( grep /$country_code/, @geo_map )
			{
				print "200 REJECT you're from $country_code\n";
				syslog("info","Rejecting: %s", $country_code);
			} else {
				print "200 DUNNO\n";
			}
		} else {
			print "200 DUNNO\n";
		}
		next;
	}
	print "200 DUNNO\n";
}

Test from command line

# telnet xxx.xxx.xxx.xxx 25
Trying xxx.xxx.xxx.xxx...
Connected to mx.example.com (xxx.xxx.xxx.xxx).
Escape character is '^]'.
220-mx.example.com ESMTP Postfix
ehlo dude
554 5.7.1 <remote.example.com[xxx.xxx.xxx.xxx>: Client host rejected: you're from ID
503 5.7.0 Error: access denied for remote.example.com[xxx.xxx.xxx.xxx]

Logs

May 30 12:20:33 fire postfix/smtpd[7493]: NOQUEUE: reject: CONNECT from remote.example.com[xxx.xxx.xxx.xxx] 554 5.7.1 remote.example.com[xxx.xxx.xxx.xxx] Client host rejected: you're from ID; proto=SMTP

enjoy 🙂

7 Comments

  1. Mateusz

    Tested in production, works very well.

    It’s useful especially if you know, that you will never get e-mails sent from given countries. In my case I’m 100% sure that my company has no customers in China, India, Brasil or Russia so it’s good deal since 50% of spam is sent from that countries

    Thanks for this post!

  2. Tom

    Thank you for the awesome filter.

    If you have a country blocked, say Germany (DE), is there a way to exclude a specific range from that country from being blocked?

  3. hanji

    Hello.. I’m having some trouble with this. The geo-reject.pl script appears to be working fine (connecting with telnet and passing IP and I can get a 200 DUNNO or REJECT) but after trying to connect it to postfix, it appears to not be seeing anything passed to it.

    By adding some debug syslog handling, it appears the tcp_table is sending a blank or null value so country and ip are Unknown. I do have tcp_tables support in postfix, so I’m pretty stuck at this point.

    Thanks!
    hanji

    • try debug the script as follow

      #perl geo-reject.pl   
      get ip_which_related_to_countrycode_you_want_to_block
      

      you shou have a result like this:

      # perl geo-reject.pl   
      get 202.123.xxx.xxx
      200 REJECT you're from ID
      ^C
      

      or you get a result “DUNNO” if ip address isn’t in country code lists.

      next step debug by telneting to 127.0.0.1 port 2528

      #telnet 127.0.0.1 2528
      Trying 127.0.0.1...
      Connected to 127.0.0.1.
      Escape character is '^]'.
      get 202.123.xxx.xxx
      200 REJECT you're from ID
      
  4. Emanuel Gonzalez

    Hi, this plugin, works with a relay from exim to postfix?

Leave a Reply

Your email address will not be published. Required fields are marked *