Enhancing SSH Security with fail2ban and GeoIP Filtering

Discussion in 'HOWTO-Related Questions' started by pannet1, Apr 19, 2024.

  1. pannet1

    pannet1 Member

    If you manage a publicly accessible server, you're likely familiar with the plethora of unsuccessful authentication attempts from bots worldwide. While using public keys for authentication mitigates some risks, disabling password-based authentication and switching to SSH keys is crucial. You can achieve this by setting PasswordAuthentication no in /etc/ssh/sshd_config and reloading the SSH daemon.

    Another tactic to thwart unauthorized access is to utilize a non-standard TCP port for your SSH server by specifying Port <custom_port> in your sshd_config.

    However, for an extra layer of security, consider implementing fail2ban—an intrusion prevention system that scans log files for suspicious activity and bans IPs accordingly. Customize fail2ban's configuration in /etc/fail2ban/jail.local to adjust ban time and other parameters according to your needs. Additionally, restricting SSH access based on geolocation can significantly reduce unauthorized login attempts. Start by installing the GeoIP database:
    Code:
    sudo apt install geoip-bin
    
    Utilize a filtering script like the one below to allow/deny connections based on country codes:
    Code:
    #!/bin/bash
    # License: WTFPL
    
    ALLOW_COUNTRIES="DE IN"  # UPPERCASE space-separated country codes to ACCEPT
    LOGDENY_FACILITY="authpriv.notice"
    
    if [ $# -ne 1 ]; then
      echo "Usage:  `basename $0` " 1>&2
      exit 0
    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
    
    
    Integrate the script into the SSH authentication process using TCP wrappers. Deny all requests to sshd in /etc/hosts.deny, then allowlist specific countries in /etc/hosts.allow by calling the filter script:
    Code:
    # /etc/hosts.deny
    sshd: ALL
    
    # /etc/hosts.allow
    sshd: ALL: aclexec /usr/local/bin/ssh-filter.sh %a
    
    These rules take immediate effect without requiring a restart. Ensure to use aclexec instead of spawn for the filter script to accurately block unauthorized connections based on country codes. By implementing fail2ban and GeoIP filtering, you significantly enhance your server's security against unauthorized access attempts.

    check if the changes are taking effect.
    Code:
    $ sudo cat /var/log/auth.log | tail
    sudo: unable to resolve host server1: Name or service not known
    2024-04-20T01:39:01.861838+05:30 server1 CRON[7851]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
    2024-04-20T01:39:01.874314+05:30 server1 CRON[7849]: pam_unix(cron:session): session closed for user root
    2024-04-20T01:39:02.157874+05:30 server1 CRON[7850]: pam_unix(cron:session): session closed for user root
    2024-04-20T01:39:02.246266+05:30 server1 CRON[7851]: pam_unix(cron:session): session closed for user root
    2024-04-20T01:39:04.084897+05:30 server1 root: DENY sshd connection from 180.101.88.205 (CN)
    2024-04-20T01:39:04.087873+05:30 server1 sshd[7871]: aclexec returned 1
    2024-04-20T01:39:04.088094+05:30 server1 sshd[7871]: refused connect from 180.101.88.205 (180.101.88.205)
    
    Note: adapted from reinhard.codes blog. I was having this pending for a long time. posting this after being successful in my perfect debian server.
     
    ahrasis likes this.
  2. ahrasis

    ahrasis Well-Known Member HowtoForge Supporter

    Just an add up, in multiple servers setup you could also limit ssh access to one main ssh server only, so only from this server you could access other servers in that setup. The down side is this main ssh server must always be working or you will not be able to access any servers at all. To avoid that risk, use two servers for that i.e. one as backup in case the main one stop working.

    Also, if you use proxmox, you may disable all ssh access to all vm except via proxmox only.
     
  3. pannet1

    pannet1 Member

    Thanks ahrasis.
    Right now, i am very small. over the years, many of my sites have died down. Trying to sell ssh to my algo customers (i code for indian stock market, mainly in python). so ispconfig ssh shell had doubled up as a python virtualenv. The above setup is very useful because i dont expect customers SSH from another country apart from mine.
     
    ahrasis likes this.
  4. ahrasis

    ahrasis Well-Known Member HowtoForge Supporter

    No worries @pannet1. Growth is very much possible and easy with ISPConfig. ;)
     
  5. Hostking

    Hostking New Member

    I dont see this spoken about much but we use the following in our ssh config file, note set PermitRootLogin to no above in the ssh file and restart for it to work. Ensure you put your IP address next to match address. We add our VPN Server IP so its the only server that can access it. Hope this helps someone.

    # Okay allow root login with public ssh key for Office and HK IP ##
    Match Address IPaddresshere
    PermitRootLogin yes
     

Share This Page