UFW & GeoIP…
Hi Cudzinec,
We all need a solution once that super SuperMicro beefy server decide’s to request a F1 key strike at POST time following your reboot command don’t we ? …
To cut a long story short, I needed a solution to get on an IPMI interface from a SuperMicro box, no matter what. At the given location, the main Internet uplink is provided by a NAT router which provides what I’d refer to as a DMZ network where all the setup’ed gear would interconnect their WAN leg’s so to speak. A kind of a problematic situation if a given system behind that DMZ (system which might provides further NGFW’s, IPsec tunnelling, DB services, appz etc.) doesn’t boot anymore… You just do not have access to anything at all…
This happened far to many times to me and I decided to give this a rest. Hence, I stacked a tiny Linux physical host within that DMZ network onto which I installed ThinLinc thus enabling Web Based Remote Access.. Great, wonderful, I can get on that damn IPMI thingy from wherever and possibly get an answer as to why that pesky box refuses to boot…
Well, if I can, so does the whole wide world… My first move once ACK’ing that fact was to enable 2FA on the given ThinLinc setup, you can find more information’s about this here – pretty straight forward and easy to setup. Good or well, already much better shall I say but not a complete satisfaction yet..
Digging a bit deeper onto what could have been done in order to secure that OOB box a notch more, I thought about enabling UFW on the given host. After a few researches, I could find and enable a working solution providing:
- UFW allowing a single port inbound
- UFW GeoIP allowing a single country while dropping anything else on that open port (DMZ NAT router would send any requests no matter what)
Here is what I did (on a Debian based distro) to get there#
Upgrading our system and installing what is needed for the task:#
# updating our system
sudo apt update -y
sudo apt upgrade -y
# adding our need packages
sudo apt install curl perl unzip xtables-addons-common libtext-csv-xs-perl libmoosex-types-netaddr-ip-perl
# adding the xtables xt_geoip module
sudo modprobe xt_geoip
# getting the xt_geoip module loaded at boot time
sudoedit /etc/modules
# And just add this line at the end of the file
xt_geoip
# Creating a directory for our needed data's
sudo mkdir -p /usr/share/xt_geoip
Creating our GeoIP populating script:#
sudo /bin/bash -c '/bin/cat > /usr/local/bin/update-geoip.sh' << EOF
#!/bin/bash -e
# Create temporary directory
mkdir -p /usr/share/xt_geoip/tmp/
mkdir -p /usr/share/xt_geoip/tmp/ip2loc/
# Download latest from db-ip.com
cd /usr/share/xt_geoip/tmp/
/usr/libexec/xtables-addons/xt_geoip_dl
# Download maxmind legacy csv and process
wget https://mailfud.org/geoip-legacy/GeoIP-legacy.csv.gz -O /usr/share/xt_geoip/tmp/GeoIP-legacy.csv.gz
gunzip /usr/share/xt_geoip/tmp/GeoIP-legacy.csv.gz
cat /usr/share/xt_geoip/tmp/GeoIP-legacy.csv | tr -d '"' | cut -d, -f1,2,5 > /usr/share/xt_geoip/tmp/GeoIP-legacy-processed.csv
rm -rf /usr/share/xt_geoip/tmp/GeoIP-legacy.csv
rm -rf /usr/share/xt_geoip/tmp/GeoIP-legacy.csv.gz
# Download latest from https://github.com/sapics/ip-location-db
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/geo-whois-asn-country/geo-whois-asn-country-ipv4.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/geo-whois-asn-country/geo-whois-asn-country-ipv6.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/iptoasn-country/iptoasn-country-ipv4.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/iptoasn-country/iptoasn-country-ipv6.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/dbip-country/dbip-country-ipv4.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/dbip-country/dbip-country-ipv6.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/geolite2-country/geolite2-country-ipv4.csv
wget -P /usr/share/xt_geoip/tmp/ https://cdn.jsdelivr.net/npm/@ip-location-db/geolite2-country/geolite2-country-ipv6.csv
# Combine all csv and remove duplicates
cd /usr/share/xt_geoip/tmp/
cat *.csv > geoip.csv
sort -u geoip.csv -o /usr/share/xt_geoip/dbip-country-lite.csv
# Remove temp directory and update geoip xtables
rm -rf /usr/share/xt_geoip/tmp/
/usr/libexec/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip/ -i /usr/share/xt_geoip/dbip-country-lite.csv
rm -rf /usr/share/xt_geoip/dbip-country-lite.csv
# reload ufw
ufw reload
EOF
Making the script executable and run the script for the first time to download thus populating all the databases:#
sudo chmod +x /usr/local/bin/update-geoip.sh
sudo /usr/local/bin/update-geoip.sh
Once the script has ran, you’ll see all the sorted databases per country within the /usr/share/xt_geoip directory. A cron job regularly running that script wouldn’t be a bad idea…
Adding custom firewall rules in order to use the geoip module thus allowing a single country while blocking anything else. For this, we need to modify this 2 file, standing for ipv4 and ipv6:#
/etc/ufw/before.rules
/etc/ufw/before6.rules
Here is what I’ve setup within the IPv4 file on my setup, just before the COMMIT directive:#
# Allow a single countries on TCP:3000 -- while blocking all the rest:
-A ufw-before-input -p tcp --dport 3000 -m geoip --src-cc CR -j ACCEPT
-A ufw-before-input -p tcp --dport 3000 -j DROP
Finally, we can setup our UFW and enable it:#
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw logging on
sudo ufw allow log-all 3000/tcp
sudo ufw show added
sudo ufw enable
Great, now only a single country can reach that host:service and 2FA has been enabled on the service. I did test none access through VPN from different countries and yes, it just works, DROP… Meanwhile, I couldn’t yet figure how to log the dropped packets in UFW from the /etc/ufw/before.rules directives. I’ll check this further and follow up here…
UPDATE – Hence, to log the denied connections, here is what I’ve modified within the /etc/ufw/before.rules file:#
# Allow a single countries on TCP:3000 -- while blocking all the rest:
-A ufw-before-input -p tcp --dport 3000 -m geoip --src-cc CR -j ACCEPT
-A ufw-before-input -p tcp --dport 3000 -j LOG --log-prefix "[BLOCKED COUNTRIES] "
-A ufw-before-input -p tcp --dport 3000 -j DROP
And you can grab these logs within the /var/log/syslogfile, a few samples:
root@oobox:~# tail -f /var/log/syslog | grep BLOCKED
Aug 19 00:00:00 oobox kernel: [ 6155.418429] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=5.180.181.217 DST=x.x.x.x LEN=40 TOS=0x00 PREC=0x00 TTL=245 ID=54321 PROTO=TCP SPT=55442 DPT=3000 WINDOW=65535 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8722.943953] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=206.168.34.63 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=50 ID=23292 DF PROTO=TCP SPT=39042 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8723.944856] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=206.168.34.63 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=50 ID=23293 DF PROTO=TCP SPT=39042 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8725.994153] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=206.168.34.63 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=50 ID=23294 DF PROTO=TCP SPT=39042 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8730.024910] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=206.168.34.63 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=50 ID=23295 DF PROTO=TCP SPT=39042 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8738.707720] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=162.142.125.47 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=56466 DF PROTO=TCP SPT=34344 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8739.724075] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=162.142.125.47 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=56467 DF PROTO=TCP SPT=34344 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8741.772958] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=162.142.125.47 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=56468 DF PROTO=TCP SPT=34344 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8745.804028] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=162.142.125.47 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=56469 DF PROTO=TCP SPT=34344 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8754.980984] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.138.42 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=28456 DF PROTO=TCP SPT=50204 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8756.003341] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.138.42 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=28457 DF PROTO=TCP SPT=50204 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8758.050335] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.138.42 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=28458 DF PROTO=TCP SPT=50204 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 8762.082638] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.138.42 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=51 ID=28459 DF PROTO=TCP SPT=50204 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 9928.077827] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.145.103 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=55260 DF PROTO=TCP SPT=49192 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 9929.114957] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.145.103 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=596 DF PROTO=TCP SPT=49262 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 9930.142662] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.145.103 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=597 DF PROTO=TCP SPT=49262 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
Aug 19 00:00:00 oobox kernel: [ 9930.319570] [BLOCKED COUNTRIES] IN=eno1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:00:00 SRC=167.94.145.103 DST=x.x.x.x LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=24175 DF PROTO=TCP SPT=49298 DPT=3000 WINDOW=21900 RES=0x00 SYN URGP=0
...
Source IP’s intentionally left intact as these attempts aren’t part of my own drills =) Finally, this works all fine with the UFW default logging settings, that is: Logging: on (low)
Hope this might help some of you..
So longue,
Obuno
PS: original post helping a lot, thanks: https://www.seenlyst.com/blog/geo-blocking-ufw-iptables/