This morning, when I took my daughter to school, I got the idea to experiment with postfix and GeoIP location. the idea is, if mx emails are in a geo targeted a specific location, mail delivery will be done with a certain ip address.
Ie:
- Every emails with the mx hosts that have IP addresses/host mapped to the US country code, will be bind to ip 1.2.3.4.
- Every emails with the mx hosts that have IP addresses/host mapped to the HK country code, will be bind to ip 5.6.7.8.
or
- Every emails with the mx hosts that have IP addresses/host mapped to the CN country code, will be relay to our smtp nexthop in china.
And so on..
what is geolocation?
Geolocation is used to deduce the geolocation (geographic location) of another party. For example, on the Internet, one geolocation approach is to identify the subject party’s IP address, then determine what country (including down to the city and post/ZIP code level), organization, or user the IP address has been assigned to, and finally, determine that party’s location. Other methods include examination of a MAC address, image metadata, or credit card information.
But, in this experiment we just need ip/host to country code map and perl script.
Perl module required:
Net::DNS Geo::IP Sys::Syslog
Basic Usage perl geoip
#!/usr/bin/perl use Geo::IP; my $gi = Geo::IP->new(GEOIP_STANDARD); print $gi->country_name_by_name("amazon.com");
I would still be using transport_maps and tcp_table to interact with Perl scripts. so here’s the prototype.
In Postfix part, we have custom transport like this in master.cf:
smtp-JP unix - - n - - smtp -o syslog_name=postfix-smtp-JP -o smtp_helo_name=smtp-JP.example.com -o smtp_bind_address=1.2.3.1 smtp-US unix - - n - - smtp -o syslog_name=postfix-smtp-US -o smtp_helo_name=smtp-US.example.com -o smtp_bind_address=1.2.3.2 smtp-ID unix - - n - - smtp -o syslog_name=postfix-smtp-ID -o smtp_helo_name=smtp-ID.example.com -o smtp_bind_address=1.2.3.3 smtp-CN unix - - n - - smtp -o syslog_name=postfix-smtp-CN -o smtp_helo_name=smtp-CN.example.com -o smtp_bind_address=1.2.3.4 smtp-HK unix - - n - - smtp -o syslog_name=postfix-smtp-HK -o smtp_helo_name=smtp-HK.example.com -o smtp_bind_address=1.2.3.5
We’ll run perl script using spawn daemon service
127.0.0.1:2527 inet n n n - 0 spawn user=nobody argv=/etc/postfix/geo.pl
In Main.cf:
transport_maps = tcp:[127.0.0.1]:2527 127.0.0.1:2527_time_limit = 3600s
Now, Perl Script part:
#!/usr/bin/perl use strict; use warnings; use Net::DNS; use Sys::Syslog qw(:DEFAULT setlogsock); use Geo::IP; my $gi = Geo::IP->new(GEOIP_STANDARD); # maps my %smtp_geo_map = ( "JP" => "smtp-JP:", "US" => "smtp-US:", "ID" => "smtp-ID:", "CN" => "smtp-CN:100.2.3.4", "HK" => "smtp-HK:" ); # # Create our resolver object. # my $dns = Net::DNS::Resolver->new( nameservers => [qw(10.11.12.13)], udp_timeout => 2, retry => 2, ); # # Initalize and open syslog. # openlog('postfix/geoIP','pid','mail'); # # Autoflush standard output. # select STDOUT; $|++; while (<>) { chomp; if (/^get\s(.+)$/i) { # extract domain from email my @email = split(/@/, $1); my $domain = $email[1]; # initiate default result my $geo_smtp = "smtp:"; # resolve MXs my @mx = mx($dns, $domain); my @mx_list; foreach my $record (@mx) { push(@mx_list, $record->exchange); } # find country code base on random mx my $country_code = $gi->country_code_by_name($mx_list[rand]); if (defined $country_code) { $geo_smtp = $smtp_geo_map{"$country_code"}; if (defined $geo_smtp) { print "200 $geo_smtp\n"; syslog("info","We are using: %s", $geo_smtp); } else { print "200 smtp:\n"; } } else { print "200 smtp:\n"; } next; } print "200 smtp:\n"; }
Take a note on this:
"CN" => "smtp-CN:100.2.3.4",
If Perl script geoip lookup result is CN country code, mail will be relay to nexthop at 100.2.3.4 (our smtp relay in china for example). Well, this is just my prototype. i’ve never really try these experiment on real smtp system with traffic. But The script and postfix configuration is tested.
now I have to pick up my daughter at school.
cheers 🙂
Excelent article, I also tested and it helps me as described.
Do you even know a way to use sender outgoing IP address based on listen IP address? I mean, if my postfix is listening on 1.2.3.4, 1.2.3.5, 1.2.3.6, when I connect on 1.2.3.5, remote server receive my mails at this IP and just this IP address.
i think you should be using multiple instances for those purpose 🙂
Hello.
Excellent post.
somebody know how extract domain name from From field,
because if I understand this part
# extract domain from email
my @email = split(/@/, $1);
my $domain = $email[1];
extract from “To” field