plugin/geoip: Upgrade to geoip2-golang v2 (#7732)

Signed-off-by: Eric Case <eric.case@gmail.com>
This commit is contained in:
Eric Case
2025-12-08 12:19:47 -08:00
committed by GitHub
parent 2a96ac10fd
commit 8f48704abe
7 changed files with 44 additions and 44 deletions

9
go.mod
View File

@@ -29,11 +29,12 @@ require (
github.com/opentracing/opentracing-go v1.2.0 github.com/opentracing/opentracing-go v1.2.0
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0
github.com/openzipkin/zipkin-go v0.4.3 github.com/openzipkin/zipkin-go v0.4.3
github.com/oschwald/geoip2-golang v1.13.0 github.com/oschwald/geoip2-golang/v2 v2.0.1
github.com/prometheus/client_golang v1.23.0 github.com/prometheus/client_golang v1.23.0
github.com/prometheus/client_model v0.6.2 github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.4 github.com/prometheus/common v0.67.4
github.com/quic-go/quic-go v0.57.1 github.com/quic-go/quic-go v0.57.0
github.com/stretchr/testify v1.11.1
go.etcd.io/etcd/api/v3 v3.6.6 go.etcd.io/etcd/api/v3 v3.6.6
go.etcd.io/etcd/client/v3 v3.6.6 go.etcd.io/etcd/client/v3 v3.6.6
go.uber.org/automaxprocs v1.6.0 go.uber.org/automaxprocs v1.6.0
@@ -49,8 +50,6 @@ require (
sigs.k8s.io/mcs-api v0.3.0 sigs.k8s.io/mcs-api v0.3.0
) )
require github.com/stretchr/testify v1.11.1
require ( require (
cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth v0.17.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
@@ -142,7 +141,7 @@ require (
github.com/onsi/ginkgo/v2 v2.22.1 // indirect github.com/onsi/ginkgo/v2 v2.22.1 // indirect
github.com/onsi/gomega v1.36.2 // indirect github.com/onsi/gomega v1.36.2 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
github.com/oschwald/maxminddb-golang v1.13.0 // indirect github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
github.com/outcaste-io/ristretto v0.2.3 // indirect github.com/outcaste-io/ristretto v0.2.3 // indirect
github.com/philhofer/fwd v1.2.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect

12
go.sum
View File

@@ -290,10 +290,10 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10S
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY= github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY=
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI= github.com/oschwald/geoip2-golang/v2 v2.0.1 h1:YcYoG/L+gmSfk7AlToTmoL0JvblNyhGC8NyVhwDzzi8=
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= github.com/oschwald/geoip2-golang/v2 v2.0.1/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
@@ -321,8 +321,8 @@ github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= github.com/quic-go/quic-go v0.57.0 h1:AsSSrrMs4qI/hLrKlTH/TGQeTMY0ib1pAOX7vA3AdqE=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/quic-go v0.57.0/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY=
github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=

View File

@@ -156,3 +156,7 @@ A limited set of fields will be exported as labels, all values are stored using
| NA | North America | | NA | North America |
| OC | Oceania | | OC | Oceania |
| SA | South America | | SA | South America |
## Notable changes
- In CoreDNS v1.13.2, the `geoip` plugin was upgraded to use [`oschwald/geoip2-golang/v2`](https://github.com/oschwald/geoip2-golang/blob/main/MIGRATION.md), the Go library that reads and parses [`.mmdb`](https://maxmind.github.io/MaxMind-DB/) databases. It has a small, but possibly-breaking change, where the `Location.Latitude` and `Location.Longitude` structs changed from value types to pointers (`float64``*float64`). In `oschwald/geoip2-golang` v1, missing coordinates returned "0" (which is a [valid location](https://en.wikipedia.org/wiki/Null_Island)), and in v2 they now return an empty string "".

View File

@@ -6,7 +6,7 @@ import (
"github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/metadata"
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang/v2"
) )
func (g GeoIP) setASNMetadata(ctx context.Context, data *geoip2.ASN) { func (g GeoIP) setASNMetadata(ctx context.Context, data *geoip2.ASN) {

View File

@@ -7,27 +7,25 @@ import (
"github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/metadata"
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang/v2"
) )
const defaultLang = "en"
func (g GeoIP) setCityMetadata(ctx context.Context, data *geoip2.City) { func (g GeoIP) setCityMetadata(ctx context.Context, data *geoip2.City) {
// Set labels for city, country and continent names. // Set labels for city, country and continent names.
cityName := data.City.Names[defaultLang] cityName := data.City.Names.English
metadata.SetValueFunc(ctx, pluginName+"/city/name", func() string { metadata.SetValueFunc(ctx, pluginName+"/city/name", func() string {
return cityName return cityName
}) })
countryName := data.Country.Names[defaultLang] countryName := data.Country.Names.English
metadata.SetValueFunc(ctx, pluginName+"/country/name", func() string { metadata.SetValueFunc(ctx, pluginName+"/country/name", func() string {
return countryName return countryName
}) })
continentName := data.Continent.Names[defaultLang] continentName := data.Continent.Names.English
metadata.SetValueFunc(ctx, pluginName+"/continent/name", func() string { metadata.SetValueFunc(ctx, pluginName+"/continent/name", func() string {
return continentName return continentName
}) })
countryCode := data.Country.IsoCode countryCode := data.Country.ISOCode
metadata.SetValueFunc(ctx, pluginName+"/country/code", func() string { metadata.SetValueFunc(ctx, pluginName+"/country/code", func() string {
return countryCode return countryCode
}) })
@@ -37,7 +35,7 @@ func (g GeoIP) setCityMetadata(ctx context.Context, data *geoip2.City) {
// a comma separated string, with the exact values provided by the database, even if those were empty strings. // a comma separated string, with the exact values provided by the database, even if those were empty strings.
subdivisionCodes := make([]string, 0, len(data.Subdivisions)) subdivisionCodes := make([]string, 0, len(data.Subdivisions))
for _, sub := range data.Subdivisions { for _, sub := range data.Subdivisions {
subdivisionCodes = append(subdivisionCodes, sub.IsoCode) subdivisionCodes = append(subdivisionCodes, sub.ISOCode)
} }
metadata.SetValueFunc(ctx, pluginName+"/subdivisions/code", func() string { metadata.SetValueFunc(ctx, pluginName+"/subdivisions/code", func() string {
return strings.Join(subdivisionCodes, ",") return strings.Join(subdivisionCodes, ",")
@@ -52,11 +50,17 @@ func (g GeoIP) setCityMetadata(ctx context.Context, data *geoip2.City) {
return continentCode return continentCode
}) })
latitude := strconv.FormatFloat(data.Location.Latitude, 'f', -1, 64) var latitude string
if data.Location.Latitude != nil {
latitude = strconv.FormatFloat(*data.Location.Latitude, 'f', -1, 64)
}
metadata.SetValueFunc(ctx, pluginName+"/latitude", func() string { metadata.SetValueFunc(ctx, pluginName+"/latitude", func() string {
return latitude return latitude
}) })
longitude := strconv.FormatFloat(data.Location.Longitude, 'f', -1, 64) var longitude string
if data.Location.Longitude != nil {
longitude = strconv.FormatFloat(*data.Location.Longitude, 'f', -1, 64)
}
metadata.SetValueFunc(ctx, pluginName+"/longitude", func() string { metadata.SetValueFunc(ctx, pluginName+"/longitude", func() string {
return longitude return longitude
}) })

View File

@@ -4,7 +4,7 @@ package geoip
import ( import (
"context" "context"
"fmt" "fmt"
"net" "net/netip"
"path/filepath" "path/filepath"
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
@@ -12,7 +12,7 @@ import (
"github.com/coredns/coredns/request" "github.com/coredns/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/oschwald/geoip2-golang" "github.com/oschwald/geoip2-golang/v2"
) )
var log = clog.NewWithPlugin(pluginName) var log = clog.NewWithPlugin(pluginName)
@@ -37,7 +37,7 @@ const (
asn asn
) )
var probingIP = net.ParseIP("127.0.0.1") var probingIP = netip.MustParseAddr("127.0.0.1")
func newGeoIP(dbPath string, edns0 bool) (*GeoIP, error) { func newGeoIP(dbPath string, edns0 bool) (*GeoIP, error) {
reader, err := geoip2.Open(dbPath) reader, err := geoip2.Open(dbPath)
@@ -80,13 +80,22 @@ func (g GeoIP) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
// Metadata implements the metadata.Provider Interface in the metadata plugin, and is used to store // Metadata implements the metadata.Provider Interface in the metadata plugin, and is used to store
// the data associated with the source IP of every request. // the data associated with the source IP of every request.
func (g GeoIP) Metadata(ctx context.Context, state request.Request) context.Context { func (g GeoIP) Metadata(ctx context.Context, state request.Request) context.Context {
srcIP := net.ParseIP(state.IP()) srcIP, err := netip.ParseAddr(state.IP())
if err != nil {
log.Debugf("Failed to parse source IP %q: %v", state.IP(), err)
return ctx
}
if g.edns0 { if g.edns0 {
if o := state.Req.IsEdns0(); o != nil { if o := state.Req.IsEdns0(); o != nil {
for _, s := range o.Option { for _, s := range o.Option {
if e, ok := s.(*dns.EDNS0_SUBNET); ok { if e, ok := s.(*dns.EDNS0_SUBNET); ok {
srcIP = e.Address // e.Address is still a net.IP type
if addr, ok := netip.AddrFromSlice(e.Address); ok {
srcIP = addr
} else {
log.Debugf("Failed to parse EDNS0 subnet address %v", e.Address)
}
break break
} }
} }

View File

@@ -2,7 +2,6 @@ package geoip
import ( import (
"fmt" "fmt"
"net"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@@ -19,7 +18,7 @@ var (
) )
func TestProbingIP(t *testing.T) { func TestProbingIP(t *testing.T) {
if probingIP == nil { if !probingIP.IsValid() {
t.Fatalf("Invalid probing IP: %q", probingIP) t.Fatalf("Invalid probing IP: %q", probingIP)
} }
} }
@@ -96,19 +95,4 @@ func TestGeoIPParse(t *testing.T) {
t.Errorf("Test %d: expected db type %d not found, database file provides %d", i, test.expectedDBType, geoIP.db.provides) t.Errorf("Test %d: expected db type %d not found, database file provides %d", i, test.expectedDBType, geoIP.db.provides)
} }
} }
// Set nil probingIP to test unexpected validate error()
defer func(ip net.IP) { probingIP = ip }(probingIP)
probingIP = nil
c = caddy.NewTestController("dns", fmt.Sprintf("%s %s\n", pluginName, cityDBPath))
_, err := geoipParse(c)
if err != nil {
expectedErr := "unexpected failure looking up database"
if !strings.Contains(err.Error(), expectedErr) {
t.Errorf("expected error to contain: %s", expectedErr)
}
} else {
t.Errorf("with a nil probingIP test is expected to fail")
}
} }