Skip to content

Commit

Permalink
add ecs support on geoip (#830)
Browse files Browse the repository at this point in the history
* add ecs support on geoip
* update docs
  • Loading branch information
dmachard authored Oct 1, 2024
1 parent 9798b2f commit 98eedb5
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 4 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<p align="center">
<img src="https://goreportcard.com/badge/github.com/dmachard/go-dns-collector" alt="Go Report"/>
<img src="https://img.shields.io/badge/go%20version-min%201.21-green" alt="Go version"/>
<img src="https://img.shields.io/badge/go%20tests-505-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20tests-506-green" alt="Go tests"/>
<img src="https://img.shields.io/badge/go%20bench-21-green" alt="Go bench"/>
<img src="https://img.shields.io/badge/go%20lines-31634-green" alt="Go lines"/>
<img src="https://img.shields.io/badge/go%20lines-31679-green" alt="Go lines"/>
</p>

<p align="center">
Expand Down
5 changes: 4 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ Default directives:
- `qclass`: dns query class
- `qname`: dns query name
- `latency`: computed latency between queries and replies
- `answercount`: the number of answer
- `qdcount`: the number of question
- `ancount`: the number of answer
- `arcount`: the number of additionnal answer
- `nscount`: the number of nameserver
- `ttl`: answer ttl, only the first one
- `answer`: rdata answer, only the first one, prefer to use the JSON format if you wamt all answers
- `malformed`: malformed dns packet, integer value 1/0
Expand Down
4 changes: 4 additions & 0 deletions docs/transformers/transform_geoip.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ Options:
* `mmdb-asn-file` (string)
> path file to your mmdb asn database
* `lookup-ecs` (bool)
> lookup for about the original client IP (or part of it) if provided
```yaml
transforms:
geoip:
mmdb-country-file: "/GeoIP/GeoLite2-Country.mmdb"
mmdb-city-file: ""
mmdb-asn-file: ""
lookup-ecs: false
```
When the feature is enabled, the following json field are populated in your DNS message:
Expand Down
1 change: 1 addition & 0 deletions pkgconfig/transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type ConfigTransformers struct {
} `yaml:"filtering"`
GeoIP struct {
Enable bool `yaml:"enable" default:"false"`
LookupECS bool `yaml:"lookup-ecs" default:"false"`
DBCountryFile string `yaml:"mmdb-country-file" default:""`
DBCityFile string `yaml:"mmdb-city-file" default:""`
DBASNFile string `yaml:"mmdb-asn-file" default:""`
Expand Down
26 changes: 25 additions & 1 deletion transformers/geoip.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net"
"strconv"
"strings"

"github.com/dmachard/go-dnscollector/dnsutils"
"github.com/dmachard/go-dnscollector/pkgconfig"
Expand Down Expand Up @@ -138,7 +139,17 @@ func (t *GeoIPTransform) geoipTransform(dm *dnsutils.DNSMessage) (int, error) {
dm.Geo = &dnsutils.TransformDNSGeo{CountryIsoCode: "-", City: "-", Continent: "-", AutonomousSystemNumber: "-", AutonomousSystemOrg: "-"}
}

geoInfo, err := t.Lookup(dm.NetworkInfo.QueryIP)
clientIP := dm.NetworkInfo.QueryIP

// lookup ecs ip instead of the query ip?
if t.config.GeoIP.LookupECS && len(dm.EDNS.Options) > 0 {
ecsIP := lookupECSIP(dm)
if ecsIP != "" {
clientIP = ecsIP
}
}

geoInfo, err := t.Lookup(clientIP)
if err != nil {
return ReturnKeep, err
}
Expand All @@ -151,3 +162,16 @@ func (t *GeoIPTransform) geoipTransform(dm *dnsutils.DNSMessage) (int, error) {

return ReturnKeep, nil
}

// lookupECSIP extracts the ECS IP from the EDNS options if available and valid.
func lookupECSIP(dm *dnsutils.DNSMessage) string {
for _, opt := range dm.EDNS.Options {
if opt.Code == 8 {
ecsIP := strings.Split(opt.Data, "/")[0]
if net.ParseIP(ecsIP) != nil {
return ecsIP
}
}
}
return ""
}
34 changes: 34 additions & 0 deletions transformers/geoip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/dmachard/go-dnscollector/dnsutils"
"github.com/dmachard/go-dnscollector/pkgconfig"
"github.com/dmachard/go-logger"
"github.com/stretchr/testify/require"
)

func TestGeoIP_Json(t *testing.T) {
Expand Down Expand Up @@ -123,3 +124,36 @@ func TestGeoIP_LookupAsn(t *testing.T) {
t.Errorf("asn organisation invalid want: XX got: %s", geoInfo.ASO)
}
}

func TestGeoIP_Lookup_ECS(t *testing.T) {
// enable geoip
config := pkgconfig.GetFakeConfigTransformers()
config.GeoIP.Enable = true
config.GeoIP.DBCountryFile = "../tests/testsdata/GeoLite2-Country.mmdb"
config.GeoIP.LookupECS = true

outChans := []chan dnsutils.DNSMessage{}

// init the processor
geoip := NewDNSGeoIPTransform(config, logger.New(false), "test", 0, outChans)
_, err := geoip.GetTransforms()
if err != nil {
t.Fatalf("geoip init failed: %v+", err)
}
defer geoip.Close()

// Create a test DNS message with EDNS ECS data
dm := dnsutils.GetFakeDNSMessage()
dm.NetworkInfo.QueryIP = "1.1.1.1" // AU
dm.EDNS.Options = append(dm.EDNS.Options, dnsutils.DNSOption{Code: 8, Name: "CSUBNET", Data: "1.58.30.6/32"}) // CN

// Apply the transform
returnCode, err := geoip.geoipTransform(&dm)
require.NoError(t, err, "process transform failed")

// Validate the country code
require.Equal(t, "CN", dm.Geo.CountryIsoCode, "country code mismatch")

// Ensure the return code is ReturnKeep
require.Equal(t, ReturnKeep, returnCode, "unexpected return code")
}

0 comments on commit 98eedb5

Please sign in to comment.