Skip to content

MaxMind ​

The maxmind package provides MaxMind GeoIP database integration for IP geolocation, including automatic database downloading and updates.

Overview ​

The maxmind package implements MaxMind GeoIP database management, providing IP geolocation lookups for country and city information. It supports automatic database downloading, scheduled updates, and thread-safe access.

Key Features ​

  • MaxMind GeoIP database loading
  • Automatic database downloading from MaxMind
  • Scheduled updates every 24 hours
  • City lookup with cache support
  • IP geolocation (country, city, timezone)
  • Thread-safe access

Architecture ​

Core Components ​

MaxMind Structure ​

go
type MaxMind struct {
    *Config
    lastUpdate time.Time
    db         struct {
        *maxminddb.Reader
        sync.RWMutex
    }
}

Configuration ​

go
type Config struct {
    Database   string  // Database type (GeoLite2 or GeoIP2)
    AccountID  int
    LicenseKey Secret
}

IP Information ​

go
type IPInfo struct {
    IP          net.IP
    Str         string
    Country     *Country
    City        *City
    Location    *Location
}

type Country struct {
    IsoCode string
    Name    string
}

type City struct {
    Country   *Country
    Name      string
    Location  *Location
}

type Location struct {
    TimeZone string
    Latitude float64
    Longitude float64
}

Public API ​

Initialization ​

go
// LoadMaxMindDB loads or downloads the MaxMind database.
func (cfg *MaxMind) LoadMaxMindDB(parent task.Parent) error

Lookup ​

go
// LookupCity looks up city information for an IP.
func LookupCity(info *IPInfo) (city *City, ok bool)

Usage ​

Basic Setup ​

go
maxmindCfg := &maxmind.Config{
    Database:   maxmind.MaxMindGeoLite,
    AccountID:  123456,
    LicenseKey: "your-license-key",
}

err := maxmindCfg.LoadMaxMindDB(parent)
if err != nil {
    log.Fatal(err)
}

IP Lookup ​

go
// Create IP info
ipInfo := &maxmind.IPInfo{
    IP:  net.ParseIP("8.8.8.8"),
    Str: "8.8.8.8",
}

// Lookup city
city, ok := maxmind.LookupCity(ipInfo)
if ok {
    fmt.Printf("Country: %s\n", city.Country.IsoCode)
    fmt.Printf("City: %s\n", city.Name)
    fmt.Printf("Timezone: %s\n", city.Location.TimeZone)
}

Database Types ​

go
const (
    MaxMindGeoLite = "GeoLite2-Country"
    MaxMindGeoIP2  = "GeoIP2-Country"
)

Data Flow ​

Database Download ​

Download Process ​

go
func (cfg *MaxMind) download() error {
    resp, err := cfg.doReq(http.MethodGet)
    if err != nil {
        return err
    }

    // Read response
    databaseGZ, err := io.ReadAll(resp.Body)
    if err != nil {
        return err
    }

    // Extract from tar.gz
    err = extractFileFromTarGz(databaseGZ, cfg.dbFilename(), tmpDBPath)
    if err != nil {
        return err
    }

    // Validate
    db, err := maxmindDBOpen(tmpDBPath)
    if err != nil {
        os.Remove(tmpDBPath)
        return err
    }
    db.Close()

    // Rename to final location
    os.Rename(tmpDBPath, dbFile)
    return nil
}

Security Checks ​

The download process includes tar bomb protection:

go
sumSize := int64(0)
for {
    hdr, err := tr.Next()
    if err == io.EOF {
        break
    }
    sumSize += hdr.Size
    if sumSize > 30*1024*1024 {
        return errors.New("file size exceeds 30MB")
    }
}

Update Scheduling ​

go
func (cfg *MaxMind) scheduleUpdate(parent task.Parent) {
    task := parent.Subtask("maxmind_schedule_update", true)
    ticker := time.NewTicker(updateInterval) // 24 hours

    cfg.loadLastUpdate()
    cfg.update()

    for {
        select {
        case <-task.Context().Done():
            return
        case <-ticker.C:
            cfg.update()
        }
    }
}

Thread Safety ​

The database uses a read-write mutex:

go
type MaxMind struct {
    *Config
    db struct {
        *maxminddb.Reader
        sync.RWMutex
    }
}

// Lookups use RLock
func (cfg *MaxMind) lookup(ip net.IP) (*maxminddb.City, error) {
    cfg.db.RLock()
    defer cfg.db.RUnlock()
    return cfg.db.Lookup(ip)
}

Configuration ​

Environment Variables ​

VariableDescription
MAXMIND_ACCOUNT_IDMaxMind account ID
MAXMIND_LICENSE_KEYMaxMind license key

YAML Configuration ​

yaml
providers:
  maxmind:
    database: geolite2
    account_id: 123456
    license_key: your-license-key

Integration Points ​

The maxmind package integrates with:

  • ACL: IP-based access control (country/timezone matching)
  • Config: Configuration management
  • Logging: Update notifications
  • City Cache: IP geolocation caching

Error Handling ​

go
var (
    ErrResponseNotOK   = errors.New("response not OK")
    ErrDownloadFailure = errors.New("download failure")
)

Performance Considerations ​

  • 24-hour update interval reduces unnecessary downloads
  • Database size ~10-30MB
  • City lookup cache reduces database queries
  • RLock for concurrent reads

Released under the MIT License.