Facebook Twitter Instagram
    TecAdmin
    • Home
    • FeedBack
    • Submit Article
    • About Us
    Facebook Twitter Instagram
    TecAdmin
    You are at:Home»Security»How to Allow SSH/FTP Access Based on Country using GeoIP

    How to Allow SSH/FTP Access Based on Country using GeoIP

    By RahulApril 21, 20223 Mins Read

    GeoIP database has records of Geographical location based on IP address. Using this database we can search for any IP belonging 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.

    Advertisement

    country based ssh access

    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 a 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 that checks for all incoming connection IP addresses and searches their corresponding country using the GeoIP database and allowed only those countries whose 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 a TCP wrapper.

    Testing

    Finally, test your server by login in 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 belonging to the US (United States) and IN (India) are allowed. Also if any IP does not match in the GeoIP database will be allowed by default. The rest of the matching other countries’ ips are denied.

    geoip security ssh security
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email WhatsApp

    Related Posts

    How to Open Port in Linux

    How to Install and Configure Fail2ban on Debian 11

    How To Set Up SSH Keys in Linux

    How To Set Up SSH Keys in Linux

    View 26 Comments

    26 Comments

    1. frollic on January 22, 2021 1:46 pm

      Thank you for posting this.

      I tried the method you posted, but the tcp wrappers functionality is deprecated in Fedora 🙁

      I’m already using the (new) Maxmind GeoIP DB in shorewall (= iptables), but I’ve noticed it isn’t very exact, even though I’ve blocked China, there’s still a ton of requests coming in from over there :/

      So I ended up signing up for ipinfo.io, which allows you to do 50k API calls per month, for free.
      There are other services offering the same, but I found their API easy to work with – you just do
      a curl call.

      I used the script above as base, when I adapted it for ipinfo.

      I had to modify the shorewall rules file to log the SSH requests, so they could be cought by my script,
      geo looked up at ipinfo, and banned.

      rules, the notice param was added:
      ACCEPT:notice net fw tcp 22
      I’m sure it can be done in raw iptables too, but I don’t know how :/

      Here’s the script, it ain’t pretty, but it works 🙂

      #!/bin/bash
      # License: WTFPL
      #
      # UPPERCASE space-separated country codes to ACCEPT
      ALLOW_COUNTRIES=”ZZ”

      tail -f -n 0 /var/log/messages | grep –line-buffered -o ‘SRC=[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}’ | while read IPADDR
      do
      IPADDR=${IPADDR:4}

      DATE=`date +”%Y-%m-%d %H:%M:%S”`

      if [[ “`echo $IPADDR | grep ‘:’`” != “” ]] ; then
      COUNTRY=`/bin/curl -s http://ipinfo.io/$IPADDR/country?token=ipinfo.token`
      else
      COUNTRY=`/bin/curl -s http://ipinfo.io/$IPADDR/country?token=ipinfo.token`
      fi

      if ! echo $COUNTRY | grep -q -v ‘”error”‘; then
      COUNTRY=”error”
      fi

      [[ $COUNTRY = “error” || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE=”ALLOW” || RESPONSE=”DENY”

      if [[ “$RESPONSE” == “ALLOW” ]] ; then
      echo “$DATE $RESPONSE sshd connection from $IPADDR ($COUNTRY)”
      else
      echo “$DATE $RESPONSE sshd connection from $IPADDR ($COUNTRY)”
      shorewall drop $IPADDR
      fi
      done

      It might be a good idea to replace shorewall drop $IPADDR, with (if installed)
      fail2ban-client set sshd banip $IPADDR, since shorewall bans won’t survive a
      system reboot/restart of shorewall, while fail2ban will.

      Reply
      • frollic on January 24, 2021 7:39 am

        FYI,

        just noticed it doesn’t handle logrotate’s log handling.

        It’ll freeze on the log is rotated 😉

        Reply
    2. aokromes on January 13, 2021 8:55 am

      geoip-database is outdated since long time ago.

      geoip-database is already the newest version (20181108-1).

      Reply
    3. Ansari on October 27, 2020 8:06 pm

      I use Centos 8, as the TCP wrapper (hosts.allow/deny)been removed here, how can I use GEOIP in centos 8 still?

      Reply
    4. Anon on September 23, 2020 6:29 pm

      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.

      Reply
    5. Olivier on July 1, 2020 11:30 am

      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

      Reply
    6. Keerthiraja on June 12, 2020 5:42 pm

      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

      Reply
    7. Rani Ahmad on May 11, 2020 6:47 am

      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.

      Reply
    8. blog on June 20, 2019 11:22 pm

      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.

      Reply
    9. zouhair kasmi on March 16, 2019 9:00 pm

      i have red all your comments all that stuff doesn’t work properly with my centos 7

      Reply
    10. A_marucevich on February 22, 2019 9:28 pm

      Hello.
      how can I use it for smtp? smtpd .. allow connection to send mail from a defined country.
      thanks, Andres.

      Reply
    11. steve martins on November 7, 2018 7:10 pm

      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.

      Reply
    12. Michiel van Es on August 20, 2017 2:17 pm

      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

      Reply
    13. dd on July 9, 2017 7:20 pm

      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 ?

      Reply
    14. dns on March 9, 2017 7:07 am

      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 ?

      Reply
    15. Michael on January 30, 2017 8:11 am

      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.

      Reply
    16. Frank on July 15, 2016 6:26 am

      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

      Reply
    17. agus on October 27, 2015 2:10 pm

      same here.. still can access when hit on enter

      Reply
    18. Agus on October 27, 2015 12:16 pm

      Hi
      may i know where i can find the log file?

      Reply
      • cope on May 11, 2017 9:13 am

        Try in /var/log/secure

        Reply
    19. Bruno on May 27, 2015 11:44 am

      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

      Reply
      • Andrew on September 2, 2015 8:20 pm

        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!

        Reply
        • dns on March 9, 2017 8:02 am

          Still same issue here 🙂

          Reply
      • andrew on November 20, 2018 3:56 pm

        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.

        Reply
    20. Jaysen Johnson on March 27, 2015 10:53 pm

      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

      Reply
      • Rahul on March 30, 2015 3:10 am

        Hi Jaysen,

        Thanks, I have updated article with spawn.

        Reply

    Leave A Reply Cancel Reply

    Advertisement
    Recent Posts
    • How to Install PHP 8.2-7.4 on RHEL & CentOS Stream 9
    • How to Install MySQL 8.0 on RHEL & CentOS Stream 9
    • How to Split Large Archives in Linux using the Command Line
    • System.out.println() Method in Java: A Beginner’s Guide
    • Split Command in Linux With Examples (Split Large Files)
    Facebook Twitter Instagram Pinterest
    © 2023 Tecadmin.net. All Rights Reserved | Terms  | Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.