diff --git a/go.mod b/go.mod index 3bb0a9bfef4..d8fc00690f6 100644 --- a/go.mod +++ b/go.mod @@ -64,8 +64,7 @@ require ( github.com/moby/moby/client v0.1.1-0.20251116162601-e9ff10bf365a github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/nxadm/tail v1.4.11 - github.com/oschwald/geoip2-golang v1.9.0 - github.com/oschwald/maxminddb-golang v1.12.0 + github.com/oschwald/maxminddb-golang/v2 v2.1.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 @@ -85,7 +84,7 @@ require ( golang.org/x/mod v0.28.0 golang.org/x/net v0.44.0 golang.org/x/sync v0.17.0 - golang.org/x/sys v0.37.0 + golang.org/x/sys v0.38.0 golang.org/x/text v0.29.0 golang.org/x/time v0.13.0 google.golang.org/grpc v1.74.2 diff --git a/go.sum b/go.sum index 49775536f42..982b849378b 100644 --- a/go.sum +++ b/go.sum @@ -458,10 +458,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= -github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= -github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= -github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= +github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc= +github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/petar-dambovaliev/aho-corasick v0.0.0-20250424160509-463d218d4745 h1:Vpr4VgAizEgEZsaMohpw6JYDP+i9Of9dmdY4ufNP6HI= @@ -719,8 +717,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/pkg/acquisition/modules/appsec/utils.go b/pkg/acquisition/modules/appsec/utils.go index 415d192a850..28e7bff21cc 100644 --- a/pkg/acquisition/modules/appsec/utils.go +++ b/pkg/acquisition/modules/appsec/utils.go @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/oschwald/geoip2-golang" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" @@ -61,7 +60,7 @@ func AppsecEventGenerationGeoIPEnrich(src *models.Source) error { if err != nil { return err } else if asndata != nil { - record := asndata.(*geoip2.ASN) + record := asndata.(*exprhelpers.GeoIPASN) src.AsName = record.AutonomousSystemOrganization src.AsNumber = fmt.Sprintf("%d", record.AutonomousSystemNumber) } @@ -70,7 +69,7 @@ func AppsecEventGenerationGeoIPEnrich(src *models.Source) error { if err != nil { return err } else if cityData != nil { - record := cityData.(*geoip2.City) + record := cityData.(*exprhelpers.GeoIPCity) src.Cn = record.Country.IsoCode src.Latitude = float32(record.Location.Latitude) src.Longitude = float32(record.Location.Longitude) diff --git a/pkg/exprhelpers/expr_lib.go b/pkg/exprhelpers/expr_lib.go index 40ec27d6af2..a0d05c15b42 100644 --- a/pkg/exprhelpers/expr_lib.go +++ b/pkg/exprhelpers/expr_lib.go @@ -6,8 +6,6 @@ import ( "net/url" "time" - "github.com/oschwald/geoip2-golang" - "github.com/crowdsecurity/crowdsec/pkg/cticlient" "github.com/crowdsecurity/crowdsec/pkg/cticlient/ctiexpr" ) @@ -493,14 +491,14 @@ var exprFuncs = []exprCustomFunc{ name: "GeoIPEnrich", function: GeoIPEnrich, signature: []any{ - new(func(string) *geoip2.City), + new(func(string) *GeoIPCity), }, }, { name: "GeoIPASNEnrich", function: GeoIPASNEnrich, signature: []any{ - new(func(string) *geoip2.ASN), + new(func(string) *GeoIPASN), }, }, { diff --git a/pkg/exprhelpers/geoip.go b/pkg/exprhelpers/geoip.go index 6d8813dc0ad..111f8ef9339 100644 --- a/pkg/exprhelpers/geoip.go +++ b/pkg/exprhelpers/geoip.go @@ -2,8 +2,66 @@ package exprhelpers import ( "net" + "net/netip" ) +// This struct is lifted from geoip2-golang to avoid dependency and have better control over the names of the fields. +type GeoIPCity struct { + City struct { + Names map[string]string `maxminddb:"names"` + GeoNameID uint `maxminddb:"geoname_id"` + } `maxminddb:"city"` + Postal struct { + Code string `maxminddb:"code"` + } `maxminddb:"postal"` + Continent struct { + Names map[string]string `maxminddb:"names"` + Code string `maxminddb:"code"` + GeoNameID uint `maxminddb:"geoname_id"` + } `maxminddb:"continent"` + Subdivisions []struct { + Names map[string]string `maxminddb:"names"` + IsoCode string `maxminddb:"iso_code"` + GeoNameID uint `maxminddb:"geoname_id"` + } `maxminddb:"subdivisions"` + RepresentedCountry struct { + Names map[string]string `maxminddb:"names"` + IsoCode string `maxminddb:"iso_code"` + Type string `maxminddb:"type"` + GeoNameID uint `maxminddb:"geoname_id"` + IsInEuropeanUnion bool `maxminddb:"is_in_european_union"` + } `maxminddb:"represented_country"` + Country struct { + Names map[string]string `maxminddb:"names"` + IsoCode string `maxminddb:"iso_code"` + GeoNameID uint `maxminddb:"geoname_id"` + IsInEuropeanUnion bool `maxminddb:"is_in_european_union"` + } `maxminddb:"country"` + RegisteredCountry struct { + Names map[string]string `maxminddb:"names"` + IsoCode string `maxminddb:"iso_code"` + GeoNameID uint `maxminddb:"geoname_id"` + IsInEuropeanUnion bool `maxminddb:"is_in_european_union"` + } `maxminddb:"registered_country"` + Location struct { + TimeZone string `maxminddb:"time_zone"` + Latitude float64 `maxminddb:"latitude"` + Longitude float64 `maxminddb:"longitude"` + MetroCode uint `maxminddb:"metro_code"` + AccuracyRadius uint16 `maxminddb:"accuracy_radius"` + } `maxminddb:"location"` + Traits struct { + IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"` + IsAnycast bool `maxminddb:"is_anycast"` + IsSatelliteProvider bool `maxminddb:"is_satellite_provider"` + } `maxminddb:"traits"` +} + +type GeoIPASN struct { + AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` + AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` +} + func GeoIPEnrich(params ...any) (any, error) { if geoIPCityReader == nil { return nil, nil @@ -11,9 +69,14 @@ func GeoIPEnrich(params ...any) (any, error) { ip := params[0].(string) - parsedIP := net.ParseIP(ip) + parsedIP, err := netip.ParseAddr(ip) + if err != nil { + return nil, err + } + + city := &GeoIPCity{} - city, err := geoIPCityReader.City(parsedIP) + err = geoIPCityReader.Lookup(parsedIP).Decode(city) if err != nil { return nil, err } @@ -22,14 +85,20 @@ func GeoIPEnrich(params ...any) (any, error) { } func GeoIPASNEnrich(params ...any) (any, error) { - if geoIPASNReader == nil { + if geoIPASNRangeReader == nil { return nil, nil } ip := params[0].(string) - parsedIP := net.ParseIP(ip) - asn, err := geoIPASNReader.ASN(parsedIP) + parsedIP, err := netip.ParseAddr(ip) + if err != nil { + return nil, err + } + + asn := &GeoIPASN{} + + err = geoIPASNRangeReader.Lookup(parsedIP).Decode(asn) if err != nil { return nil, err } @@ -38,22 +107,25 @@ func GeoIPASNEnrich(params ...any) (any, error) { } func GeoIPRangeEnrich(params ...any) (any, error) { - if geoIPRangeReader == nil { + if geoIPASNRangeReader == nil { return nil, nil } ip := params[0].(string) - var dummy interface{} - - parsedIP := net.ParseIP(ip) - rangeIP, ok, err := geoIPRangeReader.LookupNetwork(parsedIP, &dummy) + parsedIP, err := netip.ParseAddr(ip) if err != nil { return nil, err } - if !ok { - return nil, nil + // We need to convert back to net.IPNet for backwards compatibility + prefix := geoIPASNRangeReader.Lookup(parsedIP).Prefix().Masked() + addr := prefix.Addr() + bits, totalBits := prefix.Bits(), addr.BitLen() + + rangeIP := &net.IPNet{ + IP: addr.AsSlice(), + Mask: net.CIDRMask(bits, totalBits), } return rangeIP, nil diff --git a/pkg/exprhelpers/helpers.go b/pkg/exprhelpers/helpers.go index b7c5e00cb96..b4e2d54e5ab 100644 --- a/pkg/exprhelpers/helpers.go +++ b/pkg/exprhelpers/helpers.go @@ -24,8 +24,7 @@ import ( "github.com/cespare/xxhash/v2" "github.com/davecgh/go-spew/spew" "github.com/expr-lang/expr" - "github.com/oschwald/geoip2-golang" - "github.com/oschwald/maxminddb-golang" + "github.com/oschwald/maxminddb-golang/v2" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "github.com/umahmood/haversine" @@ -63,9 +62,8 @@ func init() { //nolint:gochecknoinits var keyValuePattern = regexp.MustCompile(`(?P[^=\s]+)=(?:"(?P[^"\\]*(?:\\.[^"\\]*)*)"|(?P[^=\s]+)|\s*)`) var ( - geoIPCityReader *geoip2.Reader - geoIPASNReader *geoip2.Reader - geoIPRangeReader *maxminddb.Reader + geoIPCityReader *maxminddb.Reader + geoIPASNRangeReader *maxminddb.Reader ) func GetExprOptions(ctx map[string]any) []expr.Option { @@ -80,19 +78,13 @@ func GetExprOptions(ctx map[string]any) []expr.Option { func GeoIPInit(datadir string) error { var err error - geoIPCityReader, err = geoip2.Open(filepath.Join(datadir, "GeoLite2-City.mmdb")) + geoIPCityReader, err = maxminddb.Open(filepath.Join(datadir, "GeoLite2-City.mmdb")) if err != nil { log.Errorf("unable to open GeoLite2-City.mmdb : %s", err) return err } - geoIPASNReader, err = geoip2.Open(filepath.Join(datadir, "GeoLite2-ASN.mmdb")) - if err != nil { - log.Errorf("unable to open GeoLite2-ASN.mmdb : %s", err) - return err - } - - geoIPRangeReader, err = maxminddb.Open(filepath.Join(datadir, "GeoLite2-ASN.mmdb")) + geoIPASNRangeReader, err = maxminddb.Open(filepath.Join(datadir, "GeoLite2-ASN.mmdb")) if err != nil { log.Errorf("unable to open GeoLite2-ASN.mmdb : %s", err) return err @@ -106,12 +98,8 @@ func GeoIPClose() { geoIPCityReader.Close() } - if geoIPASNReader != nil { - geoIPASNReader.Close() - } - - if geoIPRangeReader != nil { - geoIPRangeReader.Close() + if geoIPASNRangeReader != nil { + geoIPASNRangeReader.Close() } } diff --git a/pkg/parser/enrich_geoip.go b/pkg/parser/enrich_geoip.go index b2ba40d4c46..1472b1dae30 100644 --- a/pkg/parser/enrich_geoip.go +++ b/pkg/parser/enrich_geoip.go @@ -5,7 +5,6 @@ import ( "net" "strconv" - "github.com/oschwald/geoip2-golang" log "github.com/sirupsen/logrus" "github.com/crowdsecurity/crowdsec/pkg/exprhelpers" @@ -56,7 +55,7 @@ func GeoIpASN(field string, _ *pipeline.Event, plog *log.Entry) (map[string]stri return nil, nil } - record, ok := r.(*geoip2.ASN) + record, ok := r.(*exprhelpers.GeoIPASN) if !ok { return nil, nil @@ -92,7 +91,7 @@ func GeoIpCity(field string, _ *pipeline.Event, plog *log.Entry) (map[string]str return nil, nil } - record, ok := r.(*geoip2.City) + record, ok := r.(*exprhelpers.GeoIPCity) if !ok { return nil, nil