# How GeoIP Enrichment Works in LogSentinelAI and How to Visualize It in Kibana

> Discover how LogSentinelAI automatically enriches IP addresses with geographic data and visualize it on Kibana maps. Leverage latitude and longitude for insightful network analysis.

- Repository: [JungJungIn/logsentinelai](https://github.com/call518/logsentinelai)
- Tags: how-to-guide
- Published: 2026-02-26

---

**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`](https://github.com/call518/logsentinelai/blob/main/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`](https://github.com/call518/logsentinelai/blob/main/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.

```python

# 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.

```python

# 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`](https://github.com/call518/logsentinelai/blob/main/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`](https://github.com/call518/logsentinelai/blob/main/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`:

```bash
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:

```bash
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`](https://github.com/call518/logsentinelai/blob/main/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:

```json
{
  "@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`](https://github.com/call518/logsentinelai/blob/main/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`](https://github.com/call518/logsentinelai/blob/main/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.