You are here: start » plugins » spam » geo_blacklist_whitelist
You are currently not logged in! Enter your authentication credentials below to log in. You need to have cookies enabled to log in.
Plug-in Summary
Plug-in name: | geo_blacklist_whitelist |
Info: | block connections based on geo-lookups |
Author: | Marc Sebastian Pelzer |
Email: | not shown |
Compatibility: | 3.x |
Download: | http://search.cpan.org/~mpelzer/ |
=head1 NAME geo_blacklist_whitelist Version 1.2 by Marc Sebastian Pelzer http://search.cpan.org/~mpelzer/ =head1 DESCRIPTION This plugin allowes you to block incoming connections from specific countries and/or cities. You can define a blacklist and a whitelist file in the config/ directory of your qpsmtpd installation. These file must be called 'geo_blacklist' and 'geo_whitelist'. Entries in the blacklist will be overwritten by entries in the whitelist. So, for example, to just receive emails from senders within your country, put a *.* entry into the blacklist file and a *.IT (for Italy) or *.US (for the USA) into the whitelist file. City names must be a wildcard (*) if you mean 'every city' or the concrete city name, like 'Seattle' or 'seattle' or 'SEATTLE' (city names are case insensitive). The country must be specified as a 2 letter ISO 3166 code or as wildcard (*) if you mean 'every country'. See http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html (or google for 'ISO 3166') for a list of all ISO 3166 codes (the column 'A 2' is what we want). This plugin is using the Geo-IP database from mindmax.com (http://www.maxmind.com/app/ip-location). They offer a licensed (more accurate) version and also a free (lite) version of their GEO country & city database. You can get the free version from here (download the binary database and put it somewhere where your qpsmtpd user can access & read it): http://www.maxmind.com/app/geolitecity The database is updated every month; so you maybe want to set-up a cronjob which updates the database automatically every month, like: 0 4 3 * * smtp wget -T 60 -t 3 -q -O - http://www.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz | gunzip -c > /home/smtpd/GeoIPCity.dat You need the Geo-IP C libraries installed. Get them here: http://www.maxmind.com/app/c Or, if you have a Debian Linux running, by simpliy typing: apt-get install libgeoip-dev libgeoip1 You also need the Perl Module Geo::IP installed. Get it via: perl -MCPAN -e shell install Geo::IP or as HTTP download from CPAN: http://search.cpan.org/~tjmather/Geo-IP-1.27/ Put a geo_blacklist_whitelist entry into your config/plugins file - somewhere in the upper section. If you're using the "nodialup" plug-in (which I highly recommend), then you should load this plug-in after the nodialup because it's less "expensive" (just using RegExp instead of doing IO and database look-ups). Example "config/plugins" entry: geo_blacklist_whitelist /home/smtpd/GeoCity.dat active This plugin should be called with two parameters. The first parameter has to be the full path + filename of your GeoCity.dat database file (downloaded from mindmax.com). Make sure that your qpsmtpd user can access and read the file! As second parameter you can add a "active", which means that this plugin should become into production and really deny connection bases on your black/whitelist defitions. I recommend that you run this plugin like this for the start to see what happens and to fine-tune your black/whitelists files: geo_blacklist_whitelist /home/smtpd/GeoCity.dat and then do a grep 'geo_blacklist_whitelist' on your qpsmtpd debug logfile in order to see it working. If you dont specify the "active" parameter, the plugin will run in a "dry-mode" - which means that is does not DENY any connection but just send some debug output instead. If you want to reject connections from un-locatable peers, you could add an entry *.UNKNOWN into the geo_blacklist file. By default, we let this connections pass through. Example config/geo_blacklist config file: *.* # no mails from anyone - will be overwritten by entries in geo_whitelist *.CN # no mails from China *.KR # no mails from Korea *.TW # no mails from Taiwan Texas.US # no mails from Texas Berlin.* # no mails from any city in the world called Berlin *.UNKNOWN # no mails from un-locatable connections; by default we let this connections pass through Example config/geo_whitelist config file: *.IT # anyone from Italy New York.US # anyone from New York Moscow.RU # people from Moscow can mail me Beijing.CN # i want mails from Beijing in China Take a look at the top 10 most spamming countries; maybe it helps you to define your blacklisting :) http://www.spamhaus.org/statistics/countries.lasso =cut use Geo::IP; sub register { my ($self, $qp, @args) = @_; # check if we've been called with a path to the database file # if (-r $args[0] && $args[0] =~ m!\.dat$!i) { $self->{_GEOCITY_DATABASE} = $args[0]; $self->log(LOGINFO, "OK. Using Geo-City-Database '". $args[0] . "'"); } else { die "Bad argument - need a full path and filename to your Geo-City-Database .dat file!"; } # check if we should run in production mode or not # if (grep (/active/, @args)) { $self->log(LOGINFO, "Running in ACTIVE mode. This means that we reject connections based on the defined rules!"); $self->{_MODE} = 'active'; } else { $self->log(LOGINFO, "Running in DRY-RUN mode. This means that we do not reject any connections but instead print some debug messages what would happen."); $self->{_MODE} = 'dry-run'; } } sub hook_connect { my ($self, $transaction) = @_; my ($g, $r, $element, $status, $match, $rule, $ci, $co); $status = ''; my $remote_ip = $self->qp->connection->remote_ip; my $remote_host = $self->qp->connection->remote_host; my @geo_whitelist = $self->qp->config("geo_whitelist"); my @geo_blacklist = $self->qp->config("geo_blacklist"); $g = Geo::IP->open($self->{_GEOCITY_DATABASE}, GEOIP_STANDARD); $r = $g->record_by_addr($remote_ip); # check wether we can resolve the city name and country code or set some default values # my $city = '*'; my $country_code = 'UNKNOWN'; my $country_name = 'Unknown'; if ($remote_ip eq '127.0.0.1') { $city = '*'; $country_code = 'LOCAL'; $country_name = 'Local'; } else { eval { $city = $r->city || '*' }; eval { $country_code = $r->country_code || 'UNKNOWN' }; eval { $country_name = $r->country_name || 'Unknown' }; } # first, check if the current city + country is blacklisted # my $counter = 0; foreach $element (@geo_blacklist) { ++$counter; ($ci, $co) = ($element =~ m!^\s*([^\.]+)\.([^\s\#]+)!); if ((lc($ci) eq lc($city) || $ci eq '*') && (lc($co) eq lc($country_code) || $co eq '*')) { $self->log(LOGDEBUG, "geo_blacklist: given city '$city' and country_code '$country_code' ($country_name) matching rule in line $counter '$element'"); $match = $counter; $status = 'forbidden'; last; } } # then check if it's whitelisted somehow - this overwites a former # blacklist entry # $counter = 0; foreach $element (@geo_whitelist) { ++$counter; ($ci, $co) = ($element =~ m!^\s*([^\.]+)\.([^\s\#]+)!); if ((lc($ci) eq lc($city) || $ci eq '*') && (lc($co) eq lc($country_code) || $co eq '*')) { $self->log(LOGDEBUG, "geo_whitelist: given city '$city' and country_code '$country_code' ($country_name) matching rule in line $counter '$element'"); $status = ''; last; } } if ($status eq 'forbidden') { $self->log(LOGINFO, "Remote peers ($remote_ip / $remote_host) city '$city' and country_code '$country_code' ($country_name) is blacklisted (matched line $match). We DENY this connection."); if ($self->{_MODE} eq "active") { return (DENY, "Sorry, not allowed."); } } else { $self->log(LOGINFO, "Remote peers ($remote_ip / $remote_host) city '$city' and country_code '$country_code' ($country_name) is allowed to pass through."); } return DECLINED; } 1;