Files
server/opt/psa/admin/sbin/modules/firewall/geoip/maxmind-lite
cutemeli 0bfc6c8425 Initial
2025-12-22 10:32:59 +00:00

107 lines
3.5 KiB
Bash
Executable File

#!/bin/bash -e
# Free but less accurate GeoLite2 data created by MaxMind: https://www.maxmind.com . Requires license key.
# Updated twice weekly, every Tuesday and Friday.
# Each account (license key) is limited to 2000 total direct downloads in a 24 hour period.
: ${VAR_D:="/usr/local/psa/var/modules/firewall/geoip/`basename "$0"`.d"}
: ${EDITION_TYPE:="GeoLite2"}
EDITION_ID="$EDITION_TYPE-Country-CSV"
die()
{
echo "`basename "$0"`: $*" >&2
exit 1
}
load_settings()
{
[ ! -s "$VAR_D/settings.sh" ] || . "$VAR_D/settings.sh"
}
save_settings()
{
cat > "./settings.sh" <<-EOT
: \${DOWNLOAD_TIMEOUT:=$DOWNLOAD_TIMEOUT}
: \${LICENSE_KEY:=$LICENSE_KEY}
EOT
}
exists()
{
[ -s "$VAR_D/db.sqlite3" ]
}
fetch()
{
load_settings
[ "$EDITION_TYPE" = "GeoLite2" -o "$EDITION_TYPE" = "GeoIP2" ] ||
die "Unsupported MaxMind EDITION_TYPE='$EDITION_TYPE'"
[ -n "$LICENSE_KEY" ] ||
die "Missing MaxMind LICENSE_KEY"
rm -rf "$VAR_D".*
mkdir -p "`dirname "$VAR_D"`"
local tgt_d=
tgt_d="`mktemp -d "$VAR_D.XXXXXX"`"
chmod 700 "$tgt_d"
cd "$tgt_d"
# Database structure: https://dev.maxmind.com/geoip/docs/databases/city-and-country
local url="https://download.maxmind.com/app/geoip_download?edition_id=$EDITION_ID&license_key=$LICENSE_KEY&suffix=zip"
date --utc --rfc-3339=seconds > updated-at
save_settings
curl ${DOWNLOAD_TIMEOUT:+-m "$DOWNLOAD_TIMEOUT"} -fsSL "$url" -o dbip-csv.zip
unzip -jq dbip-csv.zip
sqlite3 db.sqlite3 <<-EOT
-- CentOS 7 has SQLite 3.7.17, which doesn't auto-create tables based on CSV header
CREATE TABLE blocks_ipv4 (network TEXT, geoname_id TEXT, registered_country_geoname_id TEXT,
represented_country_geoname_id TEXT, is_anonymous_proxy TEXT, is_satellite_provider TEXT);
CREATE TABLE blocks_ipv6 (network TEXT, geoname_id TEXT, registered_country_geoname_id TEXT,
represented_country_geoname_id TEXT, is_anonymous_proxy TEXT, is_satellite_provider TEXT);
CREATE TABLE locations_en(geoname_id TEXT, locale_code TEXT, continent_code TEXT,
continent_name TEXT, country_iso_code TEXT, country_name TEXT, is_in_european_union TEXT);
CREATE INDEX blocks_ipv4_geoname_id ON blocks_ipv4 (geoname_id);
CREATE INDEX blocks_ipv6_geoname_id ON blocks_ipv6 (geoname_id);
CREATE INDEX locations_en_code_geoname_id ON locations_en (country_iso_code, geoname_id);
.mode csv
.import $EDITION_TYPE-Country-Blocks-IPv4.csv blocks_ipv4
.import $EDITION_TYPE-Country-Blocks-IPv6.csv blocks_ipv6
.import $EDITION_TYPE-Country-Locations-en.csv locations_en
-- Since the tables already existed on import, header is also imported as data, remove it
DELETE FROM blocks_ipv4 WHERE geoname_id = 'geoname_id';
DELETE FROM blocks_ipv6 WHERE geoname_id = 'geoname_id';
DELETE FROM locations_en WHERE geoname_id = 'geoname_id';
EOT
for table in blocks_ipv4 blocks_ipv6 locations_en; do
[ "`sqlite3 db.sqlite3 "SELECT count(*) FROM $table;"`" -gt 0 ] ||
die "Table '$table' in the downloaded DB is empty"
done
rm -f dbip-csv.zip *.csv
rm -rf "$VAR_D"
mv -fT "$tgt_d" "$VAR_D"
}
list()
{
local country="$1"
[ -n "$country" ] || die "--list requires a single 2-letter ISO country code argument"
sqlite3 "$VAR_D/db.sqlite3" "
SELECT network FROM blocks_ipv4 b INNER JOIN locations_en l ON b.geoname_id = l.geoname_id
WHERE l.country_iso_code = '$country';
SELECT network FROM blocks_ipv6 b INNER JOIN locations_en l ON b.geoname_id = l.geoname_id
WHERE l.country_iso_code = '$country';
"
}
case "$1" in
--exists) exists ;;
--fetch) fetch ;;
--list) list "$2" ;;
*) die "Unknown command: '$1'" ;;
esac