GeoIP aware SMTP service – 3

Building Central Hub

Component list:
Postfix (build from rpm source);
QMail (mini version);
MySQL for SASL authentication;
PAM_MySQL PAM module support mysql password db;
GeoIP database;
GeoIP perl module;
perl scripting;

Build Postfix

Postfix 2.6 support tcp_table, the feature I needed in this project, but it does not build into standard rpm package. So, what I need to do is download source rpm and build it. (Source RPM: http://vault.centos.org/6.3/os/Source/SPackages/postfix-2.6.6-2.2.el6_1.src.rpm)

You need to install gcc compiler and some development packages in order to build the package.

zlib-devel-1.2.3-27.el6.x86_64
pcre-devel-7.8-4.el6.x86_64
cyrus-sasl-devel-2.1.23-13.el6_3.1.x86_64
openssl-devel-1.0.0-25.el6_3.1.x86_64
openldap-devel-2.4.23-26.el6_3.2.x86_64
db4-devel-4.7.25-17.el6.x86_64


# rpm -ivh http://vault.centos.org/6.3/os/Source/SPackages/postfix-2.6.6-2.2.el6_1.src.rpm
# cd ~rpmbuild/SPEC
# vi postfix.spec
164 popd
165 %endif
166 %patch10 -p1 -b .CVE-2011-0411
167 %patch11 -p1 -b .CVE-2011-1720
168
169 for f in README_FILES/TLS_{LEGACY_,}README; do
170 iconv -f iso8859-1 -t utf8 -o ${f}{_,} &&
171 touch -r ${f}{,_} && mv -f ${f}{_,}
172 done
173
174 %build
175 CCARGS="-fPIC -DSNAPSHOT"
176 AUXLIBS=
177
178 %ifarch s390 s390x ppc
179 CCARGS="${CCARGS} -fsigned-char"
180 %endif
181
182 %if %{LDAP}

find CCARGS at around line 172, add -DSNAPSHOT, this will enable the tcp_table module.


#rpmbuild -bb postfix.spec

After compile success, install the postfix rpm

# rpm -ivh ~/rpmbuild/RPMS/x86_64/postfix-2.6.6-2.2.el6_2.x86_64.rpm

Build QMail (mini-version)

Mentioned before, postfix didn’t implement QMQP client, so what we have to do is build QMQP client from qmail, we need only part of it.

# wget http://www.qmail.org/netqmail-1.06.tar.gz
# tar zxf netqmail-1.06.tar.gz
# cp -a netqmail-1.06 netqmail-cn
# cp -a netqmail-1.06 netqmail-other

And now we create the users to own qmail-cn.

# vi ~/netqmail-cn/conf-users
alias
chinad
chinal
root
chinap
chinaq
chinar
chinas


# vi ~/netqmail-cn/conf-groups
qmail
qmaild


# vi ~/netqmail-cn/conf-qmail
/var/qmail-cn


# groupadd qmaild
# useradd -g qmaild -d /var/qmail-cn alias
# useradd -g qmaild -d /var/qmail-cn chinad
# useradd -g qmaild -d /var/qmail-cn chinal
# useradd -g qmaild -d /var/qmail-cn chinap
# groupadd qmail
# useradd -g qmail -d /var/qmail-cn chinaq
# useradd -g qmail -d /var/qmail-cn chinar
# useradd -g qmail -d /var/qmail-cn chinas


# make setup check

and then build ~/netqmail-other/

# vi ~/netqmail-other/conf-users
alias
otherd
otherl
root
otherp
otherq
otherr
others


# vi ~/netqmail-other/conf-groups
qmail
qmaild


# vi ~/netqmail-other/conf-qmail
/var/qmail


# groupadd qmaild
# useradd -g qmaild -d /var/qmail otherd
# useradd -g qmaild -d /var/qmail otherl
# useradd -g qmaild -d /var/qmail otherp
# groupadd qmail
# useradd -g qmail -d /var/qmail otherq
# useradd -g qmail -d /var/qmail otherr
# useradd -g qmail -d /var/qmail others


# make setup check

Install PAM_MySQL

SASL authentication, we install epel repository of pam_mysql.

# rpm -ivh http://ftp.cuhk.edu.hk/pub/linux/fedora-epel/6/i386/epel-release-6-7.noarch.rpm
# yum install cyrus-sasl-2.1.23-13.el6_3.1.x86_64 \
cyrus-sasl-plain-2.1.23-13.el6_3.1.x86_64 \
cyrus-sasl-lib-2.1.23-13.el6_3.1.x86_64 \
pam_mysql-0.7-0.12.rc1.el6.x86_64 \
mysql-server-5.1.66-1.el6_3.x86_64 \
mysql-libs-5.1.66-1.el6_3.x86_64 \
mysql-5.1.66-1.el6_3.x86_64 \


# service mysqld start
# mysql_secure_installation
# mysql < insert into relaydb values ('[email protected]',password('fakepass'),1);
Query OK, 1 row affected (0.00 sec)


# vi /etc/pam.d/smtp.mysql
auth required pam_mysql.so user=postfix passwd=mypassw host=localhost db=relaydb table=mailname usercolumn=username passwdcolumn=password where=active=1 crypt=2 verbose=1
account sufficient pam_mysql.so user=postfix passwd=mypassw host=localhost db=relaydb table=mailname usercolumn=username passwdcolumn=password where=active=1 crypt=2 verbose=1

# cd /etc/pam.d;rm -f smtp; ln -s smtp.mysql smtp

Setup QMail(s)

We first setup the Qmail QMQP Client, we need defaultdomain,plusdomain,idhost,qmqpservers and me, total 5 files. idhost is the hostname that qmail-inject generates

Message-ID, must be different from other host.

{qmail-dir}/control/defaultdomain
{qmail-dir}/control/plusdomain
{qmail-dir}/control/idhost
{qmail-dir}/control/me
{qmail-dir}/control/qmqpservers

details may refer to http://cr.yp.to/qmail/mini.html

Setup Postfix

I’ve copied the main.cf below, you can found one strange line “transport_maps=tcp:[127.0.0.1]:6123”, this is tcp_table re-enabled during my rpm build.

local_recipient_maps =
mynetworks = hash:/etc/postfix/network_table
smtpd_sender_restrictions = reject_unknown_sender_domain
unknown_address_reject_code = 550
unknown_hostname_reject_code = 550
smtpd_sasl_path = smtpd
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
disable_vrfy_command = yes
myhostname = qmqpc001.mydomain.com
transport_maps=tcp:[127.0.0.1]:6123
home_mailbox = Maildir/
disable_vrfy_command = yes
smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,reject_unknown_helo_hostname
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
default_destination_recipient_limit = 60
append_at_myorigin = no

and I add following line to master.cf

cnmail unix - n n - - pipe
flags=R user=cnmailq argv=/var/qmail-cn/bin/sendmail ${recipient}


qmail unix - n n - - pipe
flags=R user=qmailq argv=/var/qmail/bin/sendmail ${recipient}


127.0.0.1:6123 inet n n n - 0 spawn
user=nobody argv=/usr/bin/perl /usr/local/bin/geoip_router.pl

cnmail is a transport for sending email to server in Mainland China, qmail is a transport to send email to mail server with dedicated international bandwidth. last line is the perl script to service tcp_table at tcp port 6123 by identify the geographical location of the email domain.

Setup GeoIP script

My logic is also simple, the script I was grabbed from here . Just change a bit.

#!/usr/bin/perl
use strict;
use warnings;
use Net::DNS;
use Geo::IP;


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


# create a maps, here I just add
my %smtp_geo_map = (
"CN" => "cnmail:",
"HK" => "smtp:"
);
#
# Create our resolver object.
#
my $dns = Net::DNS::Resolver->new(
nameservers => [qw(8.8.8.8)],
udp_timeout => 2,
retry => 2,
);
#
# 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 = "qmail:";


# 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";
} else {
print "200 qmail:\n";
}
} else {
print "200 qmail:\n";
}
next;
}
print "200 qmail:\n";

If you can read code, you already know that I have 3 route:-
1. China goes to transport called cnmail, which send mail to our China SMTP server, we have private link with the server, through QMQP protocol.
2. HongKong goes to normal transport called SMTP, i.e. send out email by this server using SMTP protocol
3. Other than 1 or 2, email will send to International SMTP server which we have purchase international bandwidth.

geoip-aware-smtp-service-1
geoip-aware-smtp-service-2

Leave a Reply

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