Monday, January 17, 2022

HowTo: GeoIP restrictions for Amazon Linux 2


pew pew pew

Background:

I have a few Amazon Linux 2 EC2 instances that I run for personal use.  Generally, I restrict "important" / remote access services to my home egress IPs, but for services that I expect to be generally available I cannot do that.

Anyone who has hosted a service on the public Internet knows that it's a matter of minutes after the service comes online before it starts being probed by all manner of scanners, exploit scripts, curious hackers, botnets, &etc.  No big deal, just build secure services, right?

Naturally.  But "defense in depth" dictates that if you can filter out a large percentage of these requests, you should.  As doing so not only reduces the load on your service, but also reduces the likelihood of your service being tagged by some unforeseen exploit.

Enter:  Maxmind's GeoIP database and the xtables-addons GeoIP filter.  With the geoip module you can easily use iptables to restrict traffic to only those countries you know your service will be accessed from.  Useful if you're building, say, a game server or similar for only a small group of friends and you know where they all are!

Note:  I'm mostly documenting this so that I can repeat it again later without having to rediscover the dependencies / steps.  No warranties, support, or promises.  You are responsible for your own systems.  But I thought someone else might find it useful.

Prerequisites:

- I'm using Amazon Linux 2 on EC2, but the steps are probably similar for any CentOS / RHEL based Linux distribution.  If using something Debian / Ubuntu based, your package management commands will differ (e.g. apt instead of yum) but you can probably muddle through it.

- A Maxmind account with which to download the "GeoIP Countries CSV" database.  For non-commercial use, the free account tier works fine for this.  Of course, you have options if you want to expand from there.

Steps:

(Note: since we're installing and configuring kernel modules, I did all this as root by running "sudo -i" first.  You can split it into download / install steps and use sudo on the individual commands if you wish.  YMMV)

1. Set up the build environment 

# yum install gcc gcc-c++ make automake unzip zip xz kernel-devel-`uname -r` iptables-devel cpan

(Yes, this requires CPAN.  Blast from the past!  The geoip build scripts are written in Perl.)

2. Set up CPAN.  I just took all the default options:

# cpan install

3. Install the required CPAN modules for the xt_geoip_build script:

# cpan Text::CSV Text::CSV_XS Net::CIDR::Lite

4. Download and extract the latest xtables-addons build (3.18 at the time of this writing.  Update the following commands for the version you download.)

# wget https://inai.de/files/xtables-addons/xtables-addons-3.18.tar.xz
# tar -xvf xtables-addons-3.18.tar.xz
# cd xtables-addons-3.18

5. Edit the "mconfig" file and comment out the modules you don't need.  I commented out everything except "build_geoip=m" to build only the geoip module.

6. Build and install the selected xtables-addon modules.

# ./configure && make && make install

7. If that went well, go into the "geoip" subdirectory under the xtables-addons build directory:

# cd geoip

8. Sign in to your Maxmind account, go to the "GeoIP Downloads" page, and find the link for the GeoLite2 Country: CSV Format database.  (Note, you want the CSV format in particular.)  I got this onto the server by copying the "Download ZIP Format" link and fetching it into the geoip directory with wget and extracting it:

# wget 'MAXMIND_URL' -O geoip_country_csv.zip
# unzip geoip_country_csv.zip

9. Now you should have a GeoLite2-Country-CSV_YYYYMMDD folder (name will vary based on the age of the database.  Substitute yours for YYYYMMDD.)  You need to run the "xt_geoip_build_maxmind" perl script from inside that folder, with the destination set for where the iptables module will look for the output:

# cd GeoLite2-Country-CSV_YYYYMMDD
# mkdir /usr/share/xt_geoip 
# ../xt_geoip_build_maxmind -D /usr/share/xt_geoip

10. If this went well, the script should build a series of files in /usr/share/xt_geoip and then show you a table of how many addresses are allocated to each country/region.  Congrats! 

Using this in IPTABLES: 

The iptables command to drop traffic from, say, non-US addresses is:

# iptables -A $CHAIN -m geoip ! --source-country US -j DROP

I maintain a "blocklist" chain specifically for things I want to drop, but put it AFTER the "RELATED,ESTABLISHED" state rules.  That way I can still initiate connections to foreign services, but clients outside the US can't initiate connections to my services.  An example configuration to do that looks like this:

# iptables -N blocklist
# iptables -A INPUT -m state --state INVALID -j DROP
# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# iptables -A INPUT -i lo -j ACCEPT
# iptables -A INPUT -j blocklist
# iptables -A blocklist -m geoip ! --source-country US -j DROP
[... your additional rules / policy here ...]


You can manage these with your own script, or do "iptables-save >/etc/sysconfig/iptables" to write the configuration to the sysconfig service that sets up iptables on boot. 

If this works, you should be able to do an "iptables -L -n -v" and see a count of packets that have been dropped from other countries.

# iptables -L -n -v
Chain blocklist (1 references)
 pkts bytes target     prot opt in     out     source               destination
  704 40777 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            -m geoip ! --source-country US

 LPT:  If you mess up iptables in such a way that you can't ssh into your host anymore, you can always log into the "serial console" via the AWS -> EC2 console -> Instance -> Actions -> Monitor & Troubleshoot -> Serial Console (It might make you enable it first, but that's a one step process.)

Addendum:

To UPDATE the Geo_IP tables, just repeat steps 8 and 9 above (without the mkdir /usr/share/xt_geoip).  The xt_geoip_build_maxmind script will replace the GEOIP databases.   Note you probably need to reboot (or remove / reload the geoip module?) to refresh the iptables data.  I haven't explored how persistent it is in memory.

IF your distro updates the kernel (or you install a new kernel for some reason) you have to rebuild and re-install the xtables-addon geoip module!  If you reboot one day, and your iptables rules don't load, you probably updated the kernel and didn't build the new module.

Just repeat yum install kernel-devel-`uname -r`and then repeat step 6 above to build and re-install the module for the new kernel.

Good luck and have fun!