# GeoIP API A high-performance HTTP service that provides MaxMind GeoIP data for IP geolocation lookups. Designed to run as a lightweight daemon within Kubernetes clusters to serve geolocation data to other services. ## Features - **Fast HTTP API** for IP geolocation lookups - **Multiple response formats**: country codes and full JSON data - **OpenTelemetry tracing** with standard Traceparent headers - **Automatic database discovery** in standard system paths - **Health check endpoint** with actual database verification - **Go client library** for easy integration - **Comprehensive test coverage** with unit, integration, and E2E tests ## API Endpoints ### Country Lookup ``` GET /api/country?ip=192.0.2.1 ``` Returns the lowercase ISO country code (e.g., `us`, `gb`) ### Full JSON Data ``` GET /api/json?ip=192.0.2.1 ``` Returns complete MaxMind GeoIP data in JSON format including: - Country, city, and region information - Latitude/longitude coordinates - ISP and organization data (if available) - Time zone information ### Health Check ``` GET /healthz ``` Performs an actual GeoIP lookup to verify database connectivity and returns the country code for a test IP. ## Installation ### From Source ```bash go build -o geoipapi ./geoipapi ``` ### Using Docker ```bash docker build -t geoipapi . docker run -p 8009:8009 geoipapi ``` ## Database Setup The service automatically searches for MaxMind databases in standard locations: - `/usr/share/GeoIP/` (Linux default) - `/usr/local/share/GeoIP/` (FreeBSD) - `/opt/local/share/GeoIP/` (MacPorts) - `/opt/homebrew/var/GeoIP/` (Homebrew) ### Supported Database Files - **Country databases**: `GeoIP2-Country.mmdb`, `GeoLite2-Country.mmdb` - **City databases**: `GeoIP2-City.mmdb`, `GeoLite2-City.mmdb` - **ISP databases**: `GeoIP2-ISP.mmdb` ### Installing GeoLite2 Databases (Free) 1. Create a free MaxMind account at https://www.maxmind.com/en/geolite2/signup 2. Download the databases manually, or 3. Use the built-in MaxMind package for automatic downloads: ```go import "go.ntppool.org/geoipapi/maxmind" maxmind.LicenseKey = "your_license_key_here" maxmind.EditionIDs = "GeoLite2-City,GeoLite2-Country" maxmind.Path = "/usr/share/GeoIP/" err := maxmind.DownloadGeoLite2DB() ``` ## Configuration ### Environment Variables - **OpenTelemetry**: Standard OTel environment variables are supported - `OTEL_EXPORTER_OTLP_ENDPOINT` - `OTEL_SERVICE_NAME` - `OTEL_RESOURCE_ATTRIBUTES` ### Server Configuration The server runs on port 8009 by default with the following timeouts: - **Read timeout**: 1 second - **Write timeout**: 10 seconds ## OpenTelemetry Support The service includes comprehensive OpenTelemetry instrumentation: - **HTTP requests** are automatically traced - **Database lookups** are instrumented with spans - **Health checks** are filtered from tracing to reduce noise - **Custom attributes** include IP addresses and operation details Tracing works seamlessly with the OpenTelemetry Collector and common observability platforms. ## Go Client Library Use the provided Go client for easy integration: ```go package main import ( "context" "fmt" "log" "net/netip" "os" "go.ntppool.org/geoipapi/client/geoipapi" ) func main() { // Set the service endpoint os.Setenv("geoip_service", "geoip-service:8009") ctx := context.Background() ip := netip.MustParseAddr("8.8.8.8") city, err := geoipapi.GetCity(ctx, ip) if err != nil { log.Fatal(err) } fmt.Printf("IP: %s\n", ip) fmt.Printf("Country: %s\n", city.Country.IsoCode) fmt.Printf("City: %s\n", city.City.Names["en"]) fmt.Printf("Location: %f, %f\n", city.Location.Latitude, city.Location.Longitude) } ``` ### Client Configuration Set the `geoip_service` environment variable to point to your GeoIP API service: ```bash export geoip_service="geoip-service.default.svc.cluster.local:8009" ``` ## Command Line Usage The service can also be used as a command-line tool for IP lookups: ```bash ./geoipapi 8.8.8.8 1.1.1.1 192.168.1.1 ``` Output: ``` 8.8.8.8: us 1.1.1.1: us 192.168.1.1: ``` ## Development ### Running Tests The project includes comprehensive test coverage: ```bash # Run all tests go test ./... # Run tests with race detection go test -race ./... # Run only fast tests go test -short ./... # Run with coverage go test -cover ./... ``` ### Test Types - **Unit tests**: Test individual functions and components - **Integration tests**: Test HTTP API endpoints with a running server - **End-to-end tests**: Test complete client-server workflows - **Race condition tests**: Verify thread safety under concurrent load ### Code Quality ```bash # Format code gofumpt -w . # Lint code go vet ./... # Verify dependencies go mod verify ``` ## Architecture ### Core Components 1. **HTTP Server** (`geoipapi.go`): Main API server with endpoint handlers 2. **MaxMind Package** (`maxmind/`): Database download and management utilities 3. **Client Library** (`client/geoipapi/`): Go client for consuming the HTTP API ### Database Discovery The service automatically discovers MaxMind databases by: 1. Searching standard system paths 2. Looking for supported database filenames 3. Opening the first available database for each type 4. Gracefully handling missing databases ### Error Handling - **Invalid IP addresses** return HTTP 500 with "data error" - **Missing databases** are detected during health checks - **Network errors** in the client include proper context - **Tracing errors** are recorded in spans for debugging ## Deployment ### Kubernetes Example Kubernetes deployment: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: geoipapi spec: replicas: 3 selector: matchLabels: app: geoipapi template: metadata: labels: app: geoipapi spec: containers: - name: geoipapi image: your-registry/geoipapi:latest ports: - containerPort: 8009 env: - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "http://otel-collector:4317" - name: OTEL_SERVICE_NAME value: "geoipapi" volumeMounts: - name: geoip-data mountPath: /usr/share/GeoIP readinessProbe: httpGet: path: /healthz port: 8009 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8009 initialDelaySeconds: 15 periodSeconds: 20 volumes: - name: geoip-data configMap: name: geoip-databases --- apiVersion: v1 kind: Service metadata: name: geoipapi spec: selector: app: geoipapi ports: - port: 8009 targetPort: 8009 ``` ### Performance Considerations - **Database caching**: MaxMind databases are loaded once on startup - **Connection pooling**: HTTP client uses connection pooling for better performance - **Concurrent requests**: Server handles multiple concurrent requests efficiently - **Memory usage**: Minimal memory footprint suitable for container environments ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes with tests 4. Run the full test suite: `go test ./...` 5. Format code: `gofumpt -w .` 6. Submit a pull request ### CI/CD The project uses Drone CI with the following pipeline: 1. **Dependencies**: Download Go modules 2. **Testing**: Run unit, integration, and race tests 3. **Code Quality**: Run `go vet`, `gofumpt`, and `go mod verify` 4. **Build**: Compile the binary 5. **Docker**: Build and push container image ## License This project is licensed under the terms specified in the LICENSE file. ## Support For issues and questions: - **Bug reports**: Create an issue in the GitHub repository - **Feature requests**: Submit a feature request with use case details - **Documentation**: Check the Go docs: `go doc go.ntppool.org/geoipapi` ## Related Projects - **MaxMind GeoIP2**: https://www.maxmind.com/en/geoip2-services-and-databases - **OpenTelemetry Go**: https://github.com/open-telemetry/opentelemetry-go - **Kubernetes ingress-nginx**: https://github.com/kubernetes/ingress-nginx (inspiration for MaxMind handling)