/geoip-http

GeoIP lookup server over HTTP.

Primary LanguageRustMIT LicenseMIT

geoip-http | Tests

geoip-http is a fast GeoIP lookup service in Rust, using the Axum web framework. It provides one possible server for tzupdate.

Features

  • Fast, uses Axum web framework
  • Simple, less than 300 lines of code
  • Safe hot reload of GeoIP DB without restarting
  • Correct cache behaviour for implicit/explicit IP lookup
  • Direct dump of GeoIP data: no filtering
  • Support for both explicit and implicit (client IP) queries
  • Support for X-Forwarded-For, X-Real-IP, CloudFront, etc
  • Sequence based logging for debugging

Usage

Download GeoLite2-City.mmdb from here, and extract it.

By default, the server runs on TCP 0.0.0.0:3000. You can change this with the --ip and --port options. You can also set the GeoIP database file location with the --db option.

You can then query / to get data for the connecting IP (respecting things like X-Real-IP, X-Forwarded-For, and the like), or /8.8.8.8 to get details for (for example) 8.8.8.8:

% curl --silent http://127.0.0.1:3000/8.8.8.8 | jq '.location'
{
  "accuracy_radius": 5,
  "latitude": 34.0544,
  "longitude": -118.2441,
  "metro_code": 803,
  "time_zone": "America/Los_Angeles"
}

The format matches that of the maxminddb crate's City struct, represented as JSON by its Serialize trait.

Logging

To see debug info, run with RUST_LOG=geoip_http=debug,tower_http=debug.

Rate limiting

geoip-http is designed to be run behind a local reverse proxy, so rate limiting generally should happen there. It can also be added via tower-governor.

Performance

On my T14s Gen 2:

% wrk -t"$(nproc)" -c400 -d30s http://127.0.0.1:3000/8.8.8.8
Running 30s test @ http://127.0.0.1:3000/8.8.8.8
  8 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.28ms    1.11ms  29.11ms   87.45%
    Req/Sec    42.99k    10.07k  158.55k    75.56%
  10269395 requests in 30.09s, 1.44GB read
Requests/sec: 341246.16
Transfer/sec:     49.14MB

Example server config

Nginx proxy config

Fill in ssl_certificate and ssl_certificate_key.

http {
    limit_conn_zone $binary_remote_addr zone=geoip_conn_limit:2m;
    limit_req_zone $binary_remote_addr zone=geoip_rate_limit:2m rate=100r/m;

    upstream geoip-backend {
        server 127.0.0.1:3000;
        keepalive 16;
    }

    server {
        listen 80;
        listen [::]:80;
        server_name geoip.chrisdown.name;

        client_body_timeout 2s;
        client_header_timeout 2s;

        location / {
            limit_req zone=geoip_rate_limit;
            limit_conn addr 5;
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
        server_name geoip.chrisdown.name;

        client_body_timeout 2s;
        client_header_timeout 2s;

        ssl_certificate ...;
        ssl_certificate_key ...;

        location / {
            limit_req zone=geoip_rate_limit;
            limit_conn addr 50;
            proxy_pass http://geoip-backend;
        }
    }
}

Systemd unit for geoip-http

Fill in --db.

[Service]
ExecStart=/usr/bin/geoip-http --db ...
ExecReload=/usr/bin/curl -v http://127.0.0.1:3000/reload/geoip
Restart=always

Attribution

This product is designed to use GeoLite2 data created by MaxMind, available from https://maxmind.com.