GeoIP database has the records of Geographical location-based on IP address. Using this database we can search any IP belongs to which country using the Linux command line. This article will help you to allow SSH or FTP (vsftpd) access based on the user’s country. This example uses TCP wrappers to secure your services.
Install GeoIP and GeoIP Database
First install GeoIP binary for Linux and their database based on your operating system. For CentOS and RedHat users GeoIP binary and database are combined in single package.
On CentOS and RedHat:
sudo yum install GeoIP
On Ubuntu and Debian:
sudo apt-get install geoip-bin geoip-database
Create the SSH/FTP Filter Script
Now create a shell script which checks for all incoming connection IP addresses and search their corresponding country using GeoIP database and allowed only those countries which code is defined in ALLOW_COUNTRIES variable in the script.
vim /usr/local/bin/ipfilter.sh
#!/bin/bash # License: WTFPL # UPPERCASE space-separated country codes to ACCEPT ALLOW_COUNTRIES="IN US" LOGDENY_FACILITY="authpriv.notice" if [ $# -ne 1 ]; then echo "Usage: `basename $0`" 1>&2 exit 0 # return true in case of config issue fi if [[ "`echo $1 | grep ':'`" != "" ]] ; then COUNTRY=`/usr/bin/geoiplookup6 "$1" | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1` else COUNTRY=`/usr/bin/geoiplookup "$1" | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1` fi [[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY" if [[ "$RESPONSE" == "ALLOW" ]] ; then logger -p $LOGDENY_FACILITY "$RESPONSE sshd connection from $1 ($COUNTRY)" exit 0 else logger -p $LOGDENY_FACILITY "$RESPONSE sshd connection from $1 ($COUNTRY)" exit 1 fi
Script srouce: https://gist.github.com/jokey2k/a74f56955124880749e7
Make this script executable
chmod +x /usr/local/bin/ipfilter.sh
Restrict SSH/FTP Connections
Now apply SSH and FTP restrictions using TCP wrappers. First we need to deny everyone by adding below line in /etc/hosts.deny
.
/etc/hosts.deny:
sshd: ALL vsftpd: ALL
Now edit /etc/hosts.allow
and allow only those ips which are allowed by your IP filter script.
/etc/hosts.allow:
sshd: ALL: spawn /usr/local/bin/ipfilter.sh %a vsftp: ALL: spawn /usr/local/bin/ipfilter.sh %a
Above FTP restrictions are for vsftpd only. Also, make sure you have enabled (tcp_wrappers=YES) in your vsftpd configuration. You can also create similar rules for any other services supported by TCP wrapper.
Testing
Finally, test your server by login using SSH or FTP from different-2 locations and analyze the access log files. Below are some demo logs created by ipfilter.sh.
Feb 27 13:03:29 TecAdmin root: DENY sshd connection from 212.191.246.202 (PL) Feb 27 13:34:28 TecAdmin root: DENY sshd connection from 212.181.246.202 (SE) Feb 27 13:34:36 TecAdmin root: DENY sshd connection from 211.181.246.203 (KR) Feb 27 13:35:00 TecAdmin root: DENY sshd connection from 221.191.146.204 (JP) Feb 27 15:11:04 TecAdmin root: ALLOW sshd connection from 49.15.212.12 (IN) Feb 27 15:11:09 TecAdmin root: ALLOW sshd connection from 149.15.212.12 (US) Feb 27 15:11:22 TecAdmin root: ALLOW sshd connection from 49.15.156.123 (IN) Feb 27 15:11:32 TecAdmin root: ALLOW sshd connection from 231.15.156.123 (IP Address not found) Feb 27 15:14:04 TecAdmin root: DENY sshd connection from 111.15.15.123 (CN) Feb 27 15:14:56 TecAdmin root: ALLOW sshd connection from 49.15.110.123 (IN)
In logs you can say that all ips belong to the US (United States) and IN (India) are allowed. Also if any ip do not matches in GeoIP database will be allowed by default. The rest of the matching other countries’ ips are denied.
geoip-database is outdated since long time ago.
geoip-database is already the newest version (20181108-1).
I use Centos 8, as the TCP wrapper (hosts.allow/deny)been removed here, how can I use GEOIP in centos 8 still?
I tried this on Debian, but it was allowing connections even when the script returned ‘DENY’ instead of ‘ALLOW’! The solution is to use ‘aclexec’ instead of ‘spawn’ in the /etc/hosts.allow file. That way, the return status of the script is taken into account.
Hello,
I have followed the instruction to install the script, geoip and all thestuff.
I get in my logs the results but ssh access is still allowed for users having an Ip not allowed.
Centos 6
I get this in the logs:
Jul 1 13:27:51 db Olivier: DENY sshd connection from 43.226.150.122 (CN)
Jul 1 13:27:54 db sshd[27318]: Invalid user ajeet from 43.226.150.122
Jul 1 13:27:54 db sshd[27329]: input_userauth_request: invalid user ajeet
Jul 1 13:27:54 db sshd[27318]: pam_unix(sshd:auth): check pass; user unknown
Jul 1 13:27:54 db sshd[27318]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=43.226.150.122
Jul 1 13:27:54 db sshd[27318]: pam_succeed_if(sshd:auth): error retrieving information about user ajeet
Jul 1 13:27:56 db sshd[27318]: Failed password for invalid user ajeet from 43.226.150.122 port 35876 ssh2
This works perfectly in CentOS / RHEL 6.x & 7.x but in CentOS 8 this method will not work because
the TCP Wrapper been removed.
Any solution to work on RHEL / CentOS 8.x
Thanks for the script but please put
ALLOW_COUNTRIES=”IN US”
as
ALLOW_COUNTRIES=”EU US”
something other than India just because I was confused by the word “IN”. Methought it was like the word “INput” .
thanks.
Hi to every body, it’s my first pay a visit of this webpage; this web site contains
amazing and truly fine information designed for visitors.
i have red all your comments all that stuff doesn’t work properly with my centos 7
Hello.
how can I use it for smtp? smtpd .. allow connection to send mail from a defined country.
thanks, Andres.
I’m close to having a centos solution by using FUSE and python to return a rule in a virtual text file created by ‘opening’ /mnt/myfusefolder/8.8.8.8 (any ip address) where the rule is either accept or deny based on geoiplookup.
For all CentOS users, spawn or aclexec does not work, the hint is already given by using iptables to block the user.
The iptables command given appends (-A) so the connection might still go through, to really block the IP you have to insert (-I) the block rule at rule #1.
You can use my altered script for a working CentOS/RHEL version: https://github.com/chiel1980/scripts/blob/master/ipfilter.sh
There is a simple workaroud for this : using a vpn in the allowed country. Is it possible to detect the source of the IP, and refuse VPN connections ?
I know this might be old, but I just can’t get it to work on CentOS …
I’m using spawn but it seems like that the hosts.allow file does not get the response back for the actual block and just allows everything…
Anyone got this working on CentOS ?
Thanks for this, I have modified the script slightly and it works great =) Within half an hour I have already blocked a bunch of attempts to login to ssh.
Yaysen, I found spawn did not work as it doesn’t’ return so everyone gets though still.
The lack of aclexec on RHEL is an issue here.. I got around it by putting IPTABLES drop rules in the DENY block of the script. ( I had posted the code but then I couldn’t submit as it said I’d done something dodgy.) I’ll try encoding it and see if that gets it though.
/sbin/iptables -A INPUT -i eth0 -p tcp –destination-port 22 -s $1 -j DROP
Works, secure log is full of entries from logger, and iptables -L shows all of those IP’s being blocked.
For added measure I put this too:
echo “ALL: $1” >> /etc/hosts.deny
cheers
Frank
same here.. still can access when hit on enter
Hi
may i know where i can find the log file?
Try in /var/log/secure
The manual is good. In the log file everything is also displayed correctly (DENY / ALLOW). But I can still sign me even with DENY. I have a system of CentOS 7 installed. Installation was carried out as described. Any idea why it does not work? Thanks, Bruno
You ever figure out your problem? I have the same issue. Geoip correctly determines the source of the ip logging in, but no connections are ever denied. Thanks!
Still same issue here 🙂
Not sure how people get this to work. Nothing I can find indicates the tcp wrappers spawn command returns any sort of conditional allow/deny value. I did have to disable selinux to even get the spawn command to function but then it simply allowed every connection regardless of the return value of the spawn command.
Excellent tutorial. Only change I had to make for CentOS 6 was that “aclexec” did not work in the hosts.allow file. I changed it to “spawn” and it works great.
sshd: ALL: spawn /usr/local/bin/ipfilter.sh %a
vsftp: ALL: spawn /usr/local/bin/ipfilter.sh %a
Thanks,
Jaysen Johnson
Hi Jaysen,
Thanks, I have updated article with spawn.