This is a plugin to postfix firewall postfwd (also located on github) intended to block international spam botnets. International spam botnets are logging into hacked mail addresses via sasl login from multiple IP addresses based in usually more than 30 unique countries. After successful login, the hackers send spam from huge amount of unique IP addresses which circumvents traditional rate limits per IP address.
If you are interested in theory about how botnet spam works and motivation for creating this plugin, look at the blog on Medium.
If you are interested in how your users got their mail accounts hacked, check out bsdly blog about slow distributed brute force attack on SSH passwords, which also applies to pop3/imap logins Hail Mary Cloud.
|Plugin Version||Postfwd Version||GeoIP Version||IP version|
|v2.0.0||postfwd3 v2.xx||GeoIP 2||IPv4, IPv6|
|v1.50.0||postfwd3 v2.xx||GeoIP 1, 2||IPv4|
|v1.40||postfwd3 v2.xx||GeoIP 1||IPv4|
|v1.30||postfwd3 v2.xx||GeoIP 1||IPv4|
|v1.21||postfwd1, postfwd2 v1.xx||GeoIP 1||IPv4|
To list changed between versions check release notes or look into the Changelog.
Pre-built ready-to-use Docker image is located on DockerHub and can be simply pulled by command:
# postfwd3 tags docker pull lirt/postfwd-anti-geoip-spam-plugin:v2.0.0 # postfwd1, postfwd2 tags docker pull lirt/postfwd-anti-geoip-spam-plugin:v1.21
To run postfwd with geoip-plugin, run docker with configuration files mounted as volumes:
docker run \ -v </absolute/path/to/anti-spam.conf>:/etc/postfwd/anti-spam.conf \ -v </absolute/path/to/postfwd.cf>:/etc/postfwd/postfwd.cf \ lirt/postfwd-anti-geoip-spam-plugin:v2.0.0
This will run
postfwd3 (based on docker tag) with default arguments, reading postfwd rules file from your mounted volume file
postfwd.cf and using anti-spam configuration from your file
To install this plugin follow next steps:
install.shto install plugin into
--plugins <PATH TO PLUGIN>to postfwd command (or update it in
CREATE TABLE IF NOT EXISTS postfwd_logins ( sasl_username varchar(100), ip_address varchar(45), state_code varchar(4), login_count int, last_login timestamp ); CREATE INDEX postfwd_sasl_client_state_index ON postfwd_logins (sasl_username, ip_address, state_code); CREATE INDEX postfwd_sasl_username ON postfwd_logins (sasl_username);
2.0.0or GeoIP version 2 since release
You can install all dependencies using cpanm with single command
cpanm --installdeps .
Install GeoIP, Time, Config, DBI and database modules with following command:
yum install -y 'perl(Geo::IP)' \ 'perl(Time::Piece)' \ 'perl(Config::Any)' \ 'perl(DBI)' \ 'perl(DBD::mysql)' \ 'perl(DBD::Pg)' \ 'perl(Net::Subnet)' \ 'perl(GeoIP2::Database::Reader)' \ 'perl(Net::SSLeay)' \ 'perl(IO::Socket::SSL)' \ 'perl(LWP::Protocol::https)' \ 'perl(Class::XSAccessor)' \ 'perl(MaxMind::DB::Reader::XS)' \ 'perl(Readonly)' \ 'perl(Data::Validate::IP)'
Install GeoIP, Time, Config, DBI and database modules with following command:
apt-get install -y libgeo-ip-perl \ libtime-piece-perl \ libconfig-any-perl \ libdbi-perl \ libdbd-mysql-perl \ libdbd-pg-perl \ libnet-subnet-perl \ geoip-database \ libnet-ssleay-perl \ libio-socket-ssl-perl \ liblwp-protocol-https-perl \ libclass-xsaccessor-perl \ libmaxmind-db-reader-xs-perl \ libgeoip2-perl \ libreadonly-perl \ libdata-validate-ip-perl
Plugin configuration file
anti-spam.conf is INI style configuration file, in which values must NOT be quoted!
Add following rules to postfwd configuration file
postfwd.cf. You can use your own message and value of parameters:
client_uniq_country_login_count: Sets maximum number of unique countries to allow user to log in using sasl.
client_uniq_ip_login_count: Sets maximum number of unique IP addresses to allow user to log in using sasl.
# Anti spam botnet rule: # This example shows how to limit e-mail address defined by `sasl_username` # to be able to login from max. 5 different countries or 20 different IP # addresses, otherwise it will be blocked from sending messages. id=BAN_BOTNET_COUNTRY ; sasl_username=~^(.+)$ ; client_uniq_country_login_count > 5 ; action=rate(sasl_username/1/3600/554 Your mail account ($$sasl_username) was compromised. Please change your password immediately after next login.) ; id=BAN_BOTNET_IP ; sasl_username=~^(.+)$ ; client_uniq_ip_login_count > 20 ; action=rate(sasl_username/1/3600/554 Your mail account ($$sasl_username) was compromised. Please change your password immediately after next login.) ;
Update configuration file
/etc/postfix/anti-spam.conf with your credentials to selected database backend. Don't forget to use proper driver and port.
In case you use different path such as
/etc/postfix/anti-spam-sql-st.conf to main configuration file, export environment variables
POSTFWD_ANTISPAM_SQL_STATEMENTS_CONFIG_PATH with your custom path.
[database] # driver = Pg driver = mysql database = test host = localhost # port = 5432 port = 3306 userid = testuser password = password
The plugin is by default configured to remove records for users with last login date older than 24 hours. This interval can be changed in configuration
Plugin looks by default for GeoIP database file in path
/usr/local/share/GeoIP/GeoLite2-Country.mmdb. You can override this path in configuration
You can whitelist set of IP addresses or subnets in CIDR format by using configuration setting
app.ip_whitelist. Whitelisting means, that if client logs into email account from IP address, which IS in whitelist, it will NOT increment login count for this pair of
[app] # flush database records with last login older than 1 day db_flush_interval = 86400 geoip_db_path = /usr/local/share/GeoIP/GeoLite2-Country.mmdb # IP whitelist must be valid comma separated strings in CIDR format without whitespaces. # it specifies IP addresses which will NOT be counted into user logins database. ip_whitelist = 198.51.100.0/24,203.0.113.123/32 # ip_whitelist_path = /etc/postfwd/ip_whitelist.txt
Plugin is by default logging into standard output. This can be changed in configuration file by setting value for
You can disable logging completely by updating value of statement
[logging] # enable(1) or disable(0) logging enable = 1 # remove statement `logfile`, or set it to empty `logfile = ` to log into STDOUT logfile = /var/log/postfwd_plugin.log autoflush = 0 # make log after exceeding unique country count limit country_limit = 5 # make log after exceeding unique ip count limit ip_limit = 20
If you use
logrotate to rotate anti-spam logs, use option
copytruncate which prevents logging errors when log file is rotated.
Plugin stores interesting statistical information in the database. To query those statistics you can use predefined SELECTs located in separate file DB-Queries.md.
Complete development environment with postfwd, anti-spam plugin and mysql/postgresql database configured together can be run with single command from directory
export POSTFWD_ANTISPAM_MAIN_CONFIG_PATH=/etc/postfwd/01-dev-anti-spam-postgres-geoip2.conf; docker-compose -f compose-dev-postgresql.yml up
export POSTFWD_ANTISPAM_MAIN_CONFIG_PATH=/etc/postfwd/02-dev-anti-spam-mysql-geoip2.conf; docker-compose -f compose-dev-mysql.yml up
Note for overriding postfwd arguments:
postfwdin Docker are
--nodaemon. These arguments configure postfwd to log into standard output and stay in foreground.
--plugins <path-to-plugin>to correct location of plugin.
MaxMind test database
tests/GeoLite2-Country-Test.mmdb was downloaded from MaxMind-DB repository.
Check for proper linting with
Run plugin as mentioned in Prototyping with Docker and send SMTP requests to postfwd policy server, or use testing script to check functionality:
# manually send postfwd request export CLIENT_ADDRESS='126.96.36.199' export SASL_USERNAME='[email protected]' nc 127.0.0.1 10040 < <(envsubst < dev-request)