To control SSH access based on location, I will use the GeoIP free database to determine the source IP address. Each time an external (non-local network) connection is initiated, the script will be executed by the SSH daemon to identify the region. If the region is not in the allowed list the connection will be dropped.
Install the geoiplookup
utility
1 | $ sudo apt-get install geoip-bin geoip-database |
Verify it is working by pinging the Google public DNS server
1 2 | $ geoiplookup 8.8.8.8 GeoIP Country Edition: US, United States |
Create the following script in /usr/local/bin/ssh_geoip.sh
as root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/bin/bash # UPPERCASE list of space-separated country codes to allow ALLOW_COUNTRIES="US" if [ $# -ne 1 ]; then echo "Usage: `basename $0` <ip_address>" 1>&2 exit 0 fi COUNTRY=`/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1` [[ ${COUNTRY} = "IP Address not found" || ${ALLOW_COUNTRIES} =~ ${COUNTRY} ]] && RESPONSE="ALLOW" || RESPONSE="DENY" logger "${RESPONSE} sshd connection from $1 (${COUNTRY})" if [ ${RESPONSE} = "ALLOW" ]; then exit 0 fi exit 1 |
Make the script executable
1 | sudo chmod 755 /usr/local/bin/ssh_geoip.sh |
Add the following to /etc/hosts.deny
as root
(create the file if needed)
1 | sshd: ALL |
Add the following to /etc/hosts.allow
as root
(create the file if needed)
1 | sshd: ALL: aclexec /usr/local/bin/ssh_geoip.sh %a |
Create an update script in /usr/local/bin/geoip_update.sh
as root
1 2 3 4 5 6 7 8 9 10 11 | #!/bin/bash cd /tmp wget -q https://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz if [ -f GeoIP.dat.gz ]; then gzip -d GeoIP.dat.gz mv -f GeoIP.dat /usr/share/GeoIP/GeoIP.dat else echo "Error downloading or updating the GeoIP library" fi |
Make the script executable. If this is the first time installing the GeoIP database, run the script manually and check that the database is fairly recent.
1 2 3 4 | sudo chmod 755 /usr/local/bin/geoip_update.sh sudo /usr/local/bin/geoip_update.sh ls -lrt /usr/share/GeoIP/ |
Add a cron entry for root to run the script monthly (at 2:05am on the first day of each month in this example).
1 2 | # m h dom mon dow command 5 2 1 * * /usr/local/bin/geoip_update.sh >/dev/null 2>&1 |