Actualizado el 7/4/2020. / Actualizado el 11/6/2020.
Tabla de contenidos
Introducción
GeoIp es un programa destinado a la geolocalización de una dirección IP con licencia libre para su uso sin restricción alguna.
No solo permite la obtención de la Geolicalización, sino que también permite obtener el Sistema Autónomo (ASN) al que pertenece la IP y la organización que lo tiene registrado.
Se encuentra escrito en Python3, adjuntando también un script escrito en Bash para actualizar las bases de datos de MaxMind periódicamente desde el Cron del sistema.
Para ello se utilizan las siguientes herramientas:
MaxMind
Bases de datos de MaxMind, en concreto las bases de datos City y ASN de su producto gratuito GeoLite2.
ACTUALIZACIÓN
Tras los últimos cambios aplicados el 30 de Diciembre del 2019 es necesario disponer de una cuenta gratuita en esta plataforma, pudiendo registrarse en el siguiente enlace.
Una vez creada la cuenta hay que generar un código o token para poder descargarse las bases de datos. Este código lo generamos, una vez validados en el portal, en el siguiente enlace.
Las instrucciones para descargar los archivos a partir de su URL se pueden consultar aquí.
Toda la información de estos cambios se pueden consultar en el siguiente enlace.
Nominatim
A partir de las coordenadas de latitud y longitud obtenidas con las bases de datos de MaxMind obtenemos la ubicación con la herramienta Nominatim, la cual hace uso de la información de OpenStreetMap.
Esta herramienta tiene una serie de restricciones y limitaciones que debemos de tener en cuenta para su uso. Se encuentran aquí.
Instalación
Se recomienda hacer uso de entornos virtuales de Python para desplegar la herramienta. Puedes consultar como instalarlo en entornos Linux aquí.
Nos ubicamos en el directorio en donde vayamos a desplegar el programa.
cd /path/to/your/program
Descarga con Git
Descargamos el programa con el siguiente comando:
git clone https://sergiobr@bitbucket.org/sergiobr/geoip.git
Instalación manual
Debemos de crear el directorio dbs
en donde se ubicarán las bases de datos de MaxMind.
mkdir -p dbs
Creamos el archivo geoIp.py
con el contenido del programa:
import maxminddb
import sys
from geopy.geocoders import Nominatim
from optparse import OptionParser
from IPy import IP
######################
# Funciones y Clases #
######################
# Clase para interactuar con MaxMind y Nominatim
class getIpInfo:
# Constructor de la clase
def __init__(self,ip):
self.ip = str(ip)
# Obtener la info de MaxMind
def getMaxMind(self):
try:
# Obtener la info de la base de datos City de MaxMind
reader = maxminddb.open_database('dbs/GeoLite2-City.mmdb')
res = reader.get(self.ip)
# Devolver el resultado de MaxMind
return res
except Exception as e:
raise ValueError('class - error getting MaxMind info: %s' % (str(e)))
# Funcion para obtener la direccion a partir de las coordenadas Latitud y Longitud
def getAddress(self,coordenates):
try:
# Crear la aplicacion de Nominatim
geolocator = Nominatim(user_agent="my-application")
# Obtener la localizacion a partir de la Latitud y Longitud
location = geolocator.reverse(coordenates)
# Devolver la informacion con la localizacion
return location.raw
except Exception as e:
raise ValueError('class - error getting Nominatim info from coordenates: %s' % (str(e)))
# Obtener la info del ASN de MaxMind
def getAsnInfo(self):
try:
# Obtener la info de la base de datos ASN de MaxMind
reader = maxminddb.open_database('dbs/GeoLite2-ASN.mmdb')
res = reader.get(self.ip)
# Devolver el resutlado de MaxMind
return res
except Exception as e:
raise ValueError('class - getting MaxMind ASN info: %s' % (str(e)))
# Mostar el resultado final
def printResult(info,arguments):
try:
if info == 'default':
keys = ['IP','Latitude','Longitude','ContCode','Continent','CounCode','Country']
for key in keys:
print('%-15s - %-15s' % (str(key), str(arguments[key])))
elif info == 'full':
keys = ['Region','Address','Timezone','Licence']
for key in keys:
print('%-15s - %-15s' % (str(key), str(arguments[key])))
elif info == 'asn':
keys = ['ASN','ASN Org']
for key in keys:
print('%-15s - %-15s' % (str(key), str(arguments[key])))
else:
print('Nothing to show.')
except Exception as e:
raise ValueError('function - error printing results: %s' % (str(e)))
# Creacion de las opciones e info del programa
def programParser():
try:
usage = "Address info of an IP address.\nUsage: %prog [options] ip_address."
description = 'IP geolocation info. Add only one IP address. Ranges are not allowed.'
version = '%prog: version 1.0'
parser = OptionParser(usage,version=version,description=description,add_help_option=True)
parser.add_option('-a', '--asn', dest = 'asn', help = 'Get only ASN info.', action='store_true')
parser.add_option('-f', '--full', dest = 'full', help = 'Get full address info.', action='store_true')
return parser
except Exception as e:
raise ValueError('function - creating program parser: %s' % (str(e)))
############
# Programa #
############
try:
# Crear el Parser del programa
parser = programParser()
# Obtener los argumentos y opciones
(options, args) = parser.parse_args()
# Confirmar el total de argumentos. Solo se permite uno
if len(args) == 0:
raise ValueError('No argument has been indicated.')
elif len(args) > 1 or len(args) < 0:
raise ValueError('Only one argument is allowed.')
else:
# Confirmar si el argumento es una IP valida o no
try:
ip = IP(args[0])
except Exception as e:
raise ValueError('The IP %s is not valid. %s' % (str(args[0]),str(e)))
# Confirmar si la IP es publica o no
if ip.iptype() == 'PUBLIC':
# Crear la clase para obtener la informacion de la IP
geoIpCommands = getIpInfo(ip)
# Declaramos las variables con los resultados
finalResult = {}
finalResultFull = {}
finalResultAsn = {}
# Obtener la direccion de la IP dependiendo de los argumentos indicados
# Obtener la info de MaxMind
maxMindRes = geoIpCommands.getMaxMind()
# Info por defecto
finalResult = {'IP':str(ip)}
finalResult['Latitude'] = str(maxMindRes['location']['latitude'])
finalResult['Longitude'] = str(maxMindRes['location']['longitude'])
finalResult['ContCode'] = str(maxMindRes['continent']['code'])
finalResult['Continent'] = str(maxMindRes['continent']['names']['en'])
if 'country' in maxMindRes:
finalResult['CounCode'] = str(maxMindRes['country']['iso_code'])
finalResult['Country'] = str(maxMindRes['country']['names']['en'])
elif 'registered_country' in maxMindRes:
finalResult['CounCode'] = str(maxMindRes['registered_country']['iso_code'])
finalResult['Country'] = str(maxMindRes['registered_country']['names']['en'])
else:
finalResult['CounCode'] = 'None'
finalResult['Country'] = 'None'
# Info completa, opcion '-f' o '--full'
if options.full:
# Obtener las coordenadas (Latitud y Longitud)
coordenates = finalResult['Latitude'],finalResult['Longitude']
# Obtener la info de Nominatim a partir de las cordenadas
nominatimRes = geoIpCommands.getAddress(coordenates)
# Info completa
if 'state' in nominatimRes['address']:
finalResultFull['Region'] = nominatimRes['address']['state']
elif 'state_district' in nominatimRes['address']:
finalResultFull['Region'] = nominatimRes['address']['state_district']
else:
finalResultFull['Region'] = 'None'
finalResultFull['Address'] = nominatimRes['display_name']
finalResultFull['Timezone'] = str(maxMindRes['location']['time_zone'])
finalResultFull['Licence'] = nominatimRes['licence']
#finalResultFull['City'] = str(maxMindRes['city']['names']['en'])
#finalResultFull['Postcode'] = str(maxMindRes['postal']['code']
#finalResultFull['Postcode'] = nominatimRes['address']['postcode']
# Info del ASN, opcion '-a' o '--asn'
if options.asn:
# Obtener la info del ASN
asnRes = geoIpCommands.getAsnInfo()
# Info del ASN
finalResultAsn['ASN'] = asnRes['autonomous_system_number']
finalResultAsn['ASN Org'] = asnRes['autonomous_system_organization']
# Mostrar los resultados 'autonomous_system_number' y 'autonomous_system_organization'
printResult('default',finalResult)
if finalResultFull:
printResult('full',finalResultFull)
if finalResultAsn:
printResult('asn',finalResultAsn)
else:
raise ValueError('The IP %s is not public.' % (str(ip)))
except Exception as e:
print('ERROR - Reason: %s' % (str(e)))
sys.exit(1)
finally:
print('\nPowered by https://blog.tiraquelibras.com\n')
Creamos el archivo updateMaxMind.sh
para descargar y actualizar las bases de datos de MaxMind, con el siguiente contenido, sustituyendo YOUR_MAXMIND_TOKEN por el token que debes haber obtenido en tu cuenta de MaxMind:
#!/bin/bash
### Variables
# DB files path
db_path='dbs'
# DB files name
city_file='GeoLite2-City.mmdb'
country_file='GeoLite2-Country.mmdb'
as_file='GeoLite2-ASN.mmdb'
# DB files downloaded
city_file_gz='GeoLite2-City.tar.gz'
country_file_gz='GeoLite2-Country.tar.gz'
as_file_gz='GeoLite2-ASN.tar.gz'
# Max Mind token
maxMind_token='YOUR_MAXMIND_TOKEN'
# URLs to download all databases (City, Country, ASN)
city_url="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=$maxMind_token&suffix=tar.gz
"
country_url="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&date=20191231&license_key=$maxMind_token&suffix=tar.gz"
as_url="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=$maxMind_token&suffix=tar.gz"
### Getting all files
# City
wget -c $city_url -O $city_file_gz
if [ $? != 0 ];then
echo "Failed gettig $city_file_gz MaxMind database"
exit 1
fi
# Country
wget -c $country_url -O $country_file_gz
if [ $? != 0 ];then
echo "Failed gettig $country_file_gz MaxMind database"
exit 1
fi
# ASN
wget -c $as_url -O $as_file_gz
if [ $? != 0 ];then
echo "Failed gettig $as_file_gz MaxMind database"
exit 1
fi
### Deleting all stored databases (.mmdb)
rm -rf $db_path/*
### Uncompress all files downloaded
# City
tar xzvf $city_file_gz
# Country
tar xzvf $country_file_gz
# ASN
tar xzvf $as_file_gz
### Moving all db files (.mmdb) to the final path
# City
path=$( find . -name $city_file -exec dirname {} \;)
mv $path'/'$city_file $db_path'/'
# Country
path2=$( find . -name $country_file -exec dirname {} \;)
mv $path2'/'$country_file $db_path'/'
# ASN
path3=$( find . -name $as_file -exec dirname {} \;)
mv $path3'/'$as_file $db_path'/'
rm -rf ./Geo*
Le damos permisos de ejecución a este archivo:
chmod 744 updateMaxMind.sh
Instalación de módulos necesarios
Instalamos los módulos necesarios indicados en el archivo requirements.txt con el siguiente comando:
pip install -r requirements.txt
Se instalarán los siguientes módulos de Python3:
- maxminddb==1.5.1
- pprint==0.1
- geopy==1.20.0
- IPy==1.00
Descarga/Actualización de las bases de datos de MaxMind
Primero editamos el archivo updateMaxMind.sh
e indicamos nuestro token en la siguiente línea:
# Max Mind token
maxMind_token='YOUR_MAXMIND_TOKEN'
Sustituimos YOUR_MAXMIND_TOKEN por nuestro token creado.
Ejecutamos el script de Bash para descargar las bases de datos de MaxMind que vamos a utilizar:
./updateMaxMind.sh
Podemos configurar este en el Cron de nuestro sistema, por ejemplo para que se ejecute los lunes y viernes:
02 19 * * 1,5 cd /path/to/your/program; ./updateMaxMind.sh
Ejecución del programa
Importante ejecutar el programa en un entorno con Python3 instalado.
El programa dispone de una ayuda contextual descriptiva:
# python geoIp.py --help
Usage: Address info of an IP address.
Usage: geoIp.py [options] ip_address.
IP geolocation info. Add only one IP address. Ranges are not allowed.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-a, --asn Get only ASN info.
-f, --full Get full address info.
Powered by https://blog.tiraquelibras.com
Las opciones disponibles son:
- -h o –help: permite mostrar la ayuda del programa.
- -a o –asn: muestra la información del ASN junto a la información por defecto.
- -f o –full: muestra la información por defecto ampliada.
- –version: muestra la versión desplegada del programa.
Un ejemplo de ejecución con información por defecto:
# python geoIp.py 8.8.8.8
IP - 8.8.8.8
Latitude - 37.751
Longitude - -97.822
ContCode - NA
Continent - North America
CounCode - US
Country - United States
Powered by https://blog.tiraquelibras.com
Otro ejemplo con información del ASN:
# python geoIp.py 8.8.8.8 -a
IP - 8.8.8.8
Latitude - 37.751
Longitude - -97.822
ContCode - NA
Continent - North America
CounCode - US
Country - United States
ASN - 15169
ASN Org - Google LLC
Powered by https://blog.tiraquelibras.com
Y por último con información ampliada:
# python geoIp.py 8.8.8.8 -f
IP - 8.8.8.8
Latitude - 37.751
Longitude - -97.822
ContCode - NA
Continent - North America
CounCode - US
Country - United States
Region - Kansas
Address - Reno County, Kansas, United States
Timezone - America/Chicago
Licence - Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright
Powered by https://blog.tiraquelibras.com
Versión del programa:
# python geoIp.py --version
geoIp.py: version 1.0
Powered by https://blog.tiraquelibras.com
Enlaces de interés
Enlace al proyecto en Bitbucket pincha aquí.
MaxMind pincha aquí
GeoLite2 de MaxMind pincha aquí
Nominatim Wiki pincha aquí
Nominatim política de uso pincha aquí
Nominatim OpenStreetMap tool pincha aquí
OpenStreetMap pincha aquí.
Actualización de Max Mind para el acceso pincha aquí.