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:

  1. Analysis Generation: The LLM produces a JSON payload with raw IP strings in fields like source_ips
  2. Enrichment Trigger: process_log_chunk() in src/logsentinelai/core/commons.py (lines 43–45) invokes enrich_source_ips_with_geoip()
  3. Geographic Lookup: Each IP is processed through GeoIPLookup.lookup_city() and replaced with structured data
  4. Elasticsearch Insertion: The enriched payload is indexed into logsentinelai-analysis with proper geo_point mappings

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

  1. Go to Visualize → Create visualization → Maps
  2. Select the logsentinelai-analysis* index pattern
  3. Choose Documents as the data source
  4. Select source_ips.location as 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 events
  • source_ips.city: "San Francisco" for city-specific analysis
  • Distance filters using the geo_distance query on source_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 GeoIPLookup class in src/logsentinelai/core/geoip.py before Elasticsearch indexing
  • The MaxMind City database provides country_code, city, region, and lat/lon coordinates for every public IP
  • Private IP addresses are automatically excluded unless GEOIP_INCLUDE_PRIVATE_IPS is set to true
  • The location object is stored as an Elasticsearch geo_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →