Skip to main content

GeoIP enrichment

Riptide can enrich every flow with country and city for the source and destination address (srcCountry/srcCity/dstCountry/dstCity columns, empty string = unknown), and fill AS number/organisation as the enrichment ladder's lowest rung. Lookups run in-process against memory-mapped .mmdb files — no I/O on the hot path. Without any configured database or override the feature is inert.

riptide:
geoip:
databases:
- /usr/share/GeoIP/GeoLite2-ASN.mmdb
- /usr/share/GeoIP/GeoLite2-City.mmdb
refresh-interval: 5m
overrides:
"192.168.0.0/16": { country: "DE", city: "Homelab" }

Databases and providers

databases is an ordered list of .mmdb paths. Each file's provider is auto-detected from its metadata database_type:

  • MaxMind (GeoLite2/GeoIP2) — Country, City, and ASN databases; anything not detected as IPinfo.
  • IPinfodatabase_type starting with ipinfo; combined files (e.g. country_asn.mmdb) contribute geo and ASN data from one entry.

A lookup consults every file: each contributes the fields it resolves, and when two files resolve the same field the later file in the list wins. The usual MaxMind setup lists the ASN and City databases as two entries.

A configured file that does not exist yet (the update sidecar has not downloaded it) is skipped with a warning and picked up automatically once it appears. Files are re-opened when they change on disk (checked every refresh-interval, default 5m); a replacement that cannot be opened keeps the previous data serving.

Manual overrides

overrides maps CIDR prefixes to partial geo data that pins its set fields over the databases — the operator's correction for wrong or missing provider data, and the answer for RFC1918/CGNAT space where providers return nothing:

riptide:
geoip:
overrides:
"192.168.0.0/16": { country: "DE", city: "Homelab" } # city AND country pinned
"203.0.113.0/24": { city: "Fulda" } # country still from the DBs
"10.20.0.0/16": { asn: 64500, org: "Lab Fabric" }

Only the set fields pin; unset fields fall through to the databases. The longest matching prefix wins. Keys are canonicalized to their prefix block (the host form 10.0.0.5/24 means 10.0.0.0/24), and two keys resolving to the same block fail startup — the same contract as riptide.routing.prefixes.

AS precedence

GeoIP AS data never beats operator or network intent. Per side, the AS number resolves as: exporter-provided nonzero → riptide.routing.prefixes → geoip override → geoip databases. The AS organisation follows the same ladder, and a GeoIP organisation is only applied to an AS number that GeoIP itself resolved.

Getting the database files

Riptide does not download databases — point it at files a sidecar keeps fresh. Both recipes below replace the file by atomic rename, which riptide's mtime-based refresh detects; a collector restart is never needed, and riptide's hardened systemd unit needs no changes (it only reads the files).

MaxMind GeoLite2 via a systemd timer

GeoLite2 is free with a MaxMind account. Install geoipupdate (packaged in most distros) and put your credentials in /etc/GeoIP.conf:

AccountID 123456
LicenseKey your-license-key
EditionIDs GeoLite2-ASN GeoLite2-City

/etc/systemd/system/geoipupdate.service:

[Unit]
Description=Refresh MaxMind GeoIP databases
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/geoipupdate -f /etc/GeoIP.conf -d /usr/share/GeoIP

/etc/systemd/system/geoipupdate.timer — GeoLite2 publishes updates on Tuesdays and Fridays, so twice a week with jitter is the polite cadence:

[Unit]
Description=Refresh MaxMind GeoIP databases twice a week

[Timer]
OnCalendar=Tue,Fri 06:00
RandomizedDelaySec=4h
Persistent=true

[Install]
WantedBy=timers.target

Enable it and run the first download immediately:

systemctl enable --now geoipupdate.timer
systemctl start geoipupdate.service

IPinfo via a systemd timer

The free tier needs only a token (put IPINFO_TOKEN=… in /etc/ipinfo.env, mode 0600). The download goes to a temp file and is mved into place — the atomic rename is what makes riptide's hot reload race-free:

/etc/systemd/system/ipinfo-update.service:

[Unit]
Description=Refresh IPinfo GeoIP database
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/ipinfo.env
ExecStart=/bin/sh -c 'curl -fsSL "https://ipinfo.io/data/free/country_asn.mmdb?token=${IPINFO_TOKEN}" \
-o /usr/share/GeoIP/.country_asn.mmdb.tmp \
&& mv /usr/share/GeoIP/.country_asn.mmdb.tmp /usr/share/GeoIP/country_asn.mmdb'

/etc/systemd/system/ipinfo-update.timer:

[Unit]
Description=Refresh IPinfo GeoIP database daily

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
systemctl enable --now ipinfo-update.timer
systemctl start ipinfo-update.service

With either recipe, riptide picks up a refreshed file within refresh-interval (default 5m) — check the log for GeoIP databases changed on disk — reloading.

Schema

The four geo columns are additive. Manage-mode deployments gain them automatically at startup (ALTER TABLE … ADD COLUMN IF NOT EXISTS); provisioned deployments re-run riptide onboard, which emits the same idempotent statements on every run. Existing rows read as empty strings.