How GeoIP Enrichment Works in LogSentinelAI and How to Visualize It in Kibana
LogSentinelAI automatically enriches every source and destination IP address with geographic metadata using MaxMind's City database, storing latitude and longitude as Elasticsearch geo_point fields that Kibana can render on interactive maps without additional configuration.
GeoIP enrichment in LogSentinelAI transforms raw IP strings from LLM-generated log analyses into structured geographic data before indexing. According to the call518/logsentinelai source code, this pipeline ensures that security analysts can immediately map threat origins and filter events by country or region within Kibana dashboards.
How LogSentinelAI Performs GeoIP Enrichment
The enrichment pipeline consists of four discrete steps handled by the GeoIPLookup class and helper functions in src/logsentinelai/core/geoip.py.
Initializing the GeoIP Service
When LogSentinelAI starts, the global GeoIPLookup instance reads GEOIP_CONFIG from src/logsentinelai/core/config.py (lines 13–19) and validates the MaxMind City database path. If the database is missing, the system automatically downloads GeoLite2-City.mmdb to the configured location.
# From src/logsentinelai/core/geoip.py L21-L38
# The GeoIPLookup class initializes the MaxMind reader
class GeoIPLookup:
def __init__(self, config):
self.db_path = config.get('GEOIP_DATABASE_PATH')
self.include_private = config.get('GEOIP_INCLUDE_PRIVATE_IPS', False)
# Auto-download logic triggers here if file missing
Looking Up Individual IP Addresses
The lookup_city(ip) method (lines 91–127) validates IP addresses, skips private ranges unless include_private_ips is enabled, and queries the MaxMind database. It returns a standardized dictionary containing geographic identifiers.
# Result structure from GeoIPLookup.lookup_city()
{
"ip": "203.0.113.45",
"country_code": "US",
"country_name": "United States",
"city": "San Francisco",
"region": "California",
"region_code": "CA",
"location": {
"lat": 37.7749,
"lon": -122.4194
}
}
Enriching Analysis Payloads
Before data reaches Elasticsearch, the enrich_source_ips_with_geoip(analysis_data) function (lines 154–170) traverses the JSON payload. It identifies IP-containing fields such as source_ips, dest_ips, and top_*_ips, replacing raw strings with the geographic dictionaries returned by lookup_city(). Invalid or private IPs are silently discarded based on configuration.
Indexing Enriched Documents
The send_to_elasticsearch() wrapper in src/logsentinelai/core/commons.py (lines 20–24) orchestrates the final step. It first calls enrich_source_ips_with_geoip(), then forwards the enriched document to send_to_elasticsearch_raw() in src/logsentinelai/core/elasticsearch.py. The location sub-object containing lat and lon is automatically mapped as an Elasticsearch geo_point type upon first indexing.
Configuration and Setup
Enable GeoIP enrichment by setting these environment variables in your .env file or /etc/logsentinelai.config:
GEOIP_ENABLED=true
GEOIP_DATABASE_PATH=~/.logsentinelai/GeoLite2-City.mmdb
GEOIP_FALLBACK_COUNTRY=Unknown
GEOIP_CACHE_SIZE=1000
GEOIP_INCLUDE_PRIVATE_IPS=false
When running an analysis via CLI, the system validates the database path and downloads the MaxMind database if absent:
logsentinelai httpd_access --log-path "/var/log/apache2/access.log" --mode batch
Console output indicates successful initialization:
⚠️ GeoIP database not found at /home/user/.logsentinelai/GeoLite2-City.mmdb
✅ GeoIP database downloaded successfully!
End-to-End Data Flow
The complete enrichment workflow executes as follows:
- Analysis Generation: The LLM produces a JSON payload with raw IP strings in fields like
source_ips - Enrichment Trigger:
process_log_chunk()insrc/logsentinelai/core/commons.py(lines 43–45) invokesenrich_source_ips_with_geoip() - Geographic Lookup: Each IP is processed through
GeoIPLookup.lookup_city()and replaced with structured data - Elasticsearch Insertion: The enriched payload is indexed into
logsentinelai-analysiswith propergeo_pointmappings
An indexed document contains structured geographic data instead of raw strings:
{
"@timestamp": "2026-02-26T15:12:34.567Z",
"@log_type": "httpd_access",
"events": [
{
"event_type": "suspicious_login",
"source_ips": [
{
"ip": "203.0.113.45",
"country_code": "US",
"country_name": "United States",
"city": "San Francisco",
"region": "California",
"region_code": "CA",
"location": { "lat": 37.7749, "lon": -122.4194 }
}
],
"severity": "HIGH"
}
]
}
Visualizing GeoIP Data in Kibana
Once enriched data is indexed, Kibana can visualize geographic distributions immediately without index template modifications.
Create the Index Pattern
Navigate to Stack Management → Index Patterns and add logsentinelai-analysis*. The source_ips.location field automatically registers as a Geo Point data type.
Build a Map Visualization
- Go to Visualize → Create visualization → Maps
- Select the
logsentinelai-analysis*index pattern - Choose Documents as the data source
- Select
source_ips.locationas the geo field
Kibana renders each enriched IP as a pin on the world map. Layer filters can display only HIGH severity events using the query events.severity: HIGH.
Add Geographic Filters
Use KQL syntax in dashboard search bars to filter by country or distance:
source_ips.country_code: "US"for United States eventssource_ips.city: "San Francisco"for city-specific analysis- Distance filters using the
geo_distancequery onsource_ips.location
Tabular Drill-Down
Create a Data Table visualization alongside your map showing columns source_ips.ip, source_ips.country_name, source_ips.city, and source_ips.location for detailed textual analysis of mapped coordinates.
Summary
- LogSentinelAI enriches IPs using the
GeoIPLookupclass insrc/logsentinelai/core/geoip.pybefore Elasticsearch indexing - The MaxMind City database provides
country_code,city,region, andlat/loncoordinates for every public IP - Private IP addresses are automatically excluded unless
GEOIP_INCLUDE_PRIVATE_IPSis set totrue - The
locationobject is stored as an Elasticsearchgeo_point, enabling immediate Kibana map visualizations - No manual mapping is required; the field type is auto-detected on first document insertion
Frequently Asked Questions
How do I enable GeoIP enrichment in LogSentinelAI?
Set GEOIP_ENABLED=true in your environment configuration file. The system will automatically download the MaxMind GeoLite2-City database on first run if GEOIP_DATABASE_PATH points to a non-existent file. Ensure the path is writable by the user running the LogSentinelAI process.
What geographic fields are added during enrichment?
Each IP is replaced with a dictionary containing country_code, country_name, city, region, region_code, and a nested location object with lat and lon coordinates. These fields appear in source_ips and dest_ips arrays within the analysis document.
How does Kibana recognize the location data for mapping?
Elasticsearch automatically maps the location sub-object (containing lat and lon fields) as a geo_point type when the first enriched document is indexed. Kibana detects this mapping and allows you to select the field in Maps visualizations without manual index template configuration.
Can I manually look up individual IP addresses for testing?
Yes. The repository includes a CLI utility in src/logsentinelai/utils/geoip_lookup.py that wraps the lookup service. Run logsentinelai geoip-lookup <ip_address> to test enrichment results and verify database connectivity without processing full log files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →