Skip to content

Commit

Permalink
Add rdata ip filtering (#377)
Browse files Browse the repository at this point in the history
* Add rdata ip filtering
* Add ipv6 test cases
  • Loading branch information
arvchristos authored Aug 23, 2023
1 parent 4e9f5f6 commit d00bd08
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 0 deletions.
2 changes: 2 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,8 @@ multiplexer:
# # Example to ignore NOERROR dns packets
# # drop-rcodes:
# # - NOERROR
# keep-rdataip-file: ""
# # path file of the rdata IP keep list, one IP address or subnet per line
# drop-rcodes: []
# # forward received queries to configured loggers ?
# log-queries: true
Expand Down
1 change: 1 addition & 0 deletions dnsutils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ type ConfigTransformers struct {
KeepDomainFile string `yaml:"keep-domain-file"`
DropQueryIpFile string `yaml:"drop-queryip-file"`
KeepQueryIpFile string `yaml:"keep-queryip-file"`
KeepRdataFile string `yaml:"keep-rdata-file"`
DropRcodes []string `yaml:"drop-rcodes,flow"`
LogQueries bool `yaml:"log-queries"`
LogReplies bool `yaml:"log-replies"`
Expand Down
1 change: 1 addition & 0 deletions doc/transformers.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ Options:
- `keep-domain-file`: (string) path file to domain keep list (all others are dropped), domains list can be a partial domain name with regexp expression
- `drop-queryip-file`: (string) path file to the query ip or ip prefix drop list
- `keep-queryip-file`: (string) path file to the query ip or ip prefix keep list, addresses in both drop and keep are always kept
- `keep-rdataip-file`: (string) path file to the answer ip or ip prefix keep list. If the answer set includes ips both in drop and keep list, an error is thrown
- `drop-rcodes`: (list of string) rcode list, empty by default
- `log-queries`: (boolean) drop all queries on false
- `log-replies`: (boolean) drop all replies on false
Expand Down
6 changes: 6 additions & 0 deletions testsdata/filtering_rdataip_keep.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
192.168.1.10
192.0.2.1
192.168.1.0/24
2001:db8:85a3::8a2e:370:7334
2301:db8:85a3::8a2e:370:7334
2001:0dbd:85a3:0000::/64
62 changes: 62 additions & 0 deletions transformers/filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type FilteringProcessor struct {
mapRcodes map[string]bool
ipsetDrop *netaddr.IPSet
ipsetKeep *netaddr.IPSet
rDataIpsetKeep *netaddr.IPSet
listFqdns map[string]bool
listDomainsRegex map[string]*regexp.Regexp
listKeepFqdns map[string]bool
Expand Down Expand Up @@ -53,6 +54,7 @@ func NewFilteringProcessor(config *dnsutils.ConfigTransformers, logger *logger.L
mapRcodes: make(map[string]bool),
ipsetDrop: &netaddr.IPSet{},
ipsetKeep: &netaddr.IPSet{},
rDataIpsetKeep: &netaddr.IPSet{},
listFqdns: make(map[string]bool),
listDomainsRegex: make(map[string]*regexp.Regexp),
listKeepFqdns: make(map[string]bool),
Expand All @@ -68,6 +70,7 @@ func NewFilteringProcessor(config *dnsutils.ConfigTransformers, logger *logger.L
d.LoadRcodes()
d.LoadDomainsList()
d.LoadQueryIpList()
d.LoadrDataIpList()

d.LoadActiveFilters()

Expand Down Expand Up @@ -103,6 +106,10 @@ func (p *FilteringProcessor) LoadActiveFilters() {
p.activeFilters = append(p.activeFilters, p.ipFilter)
}

if len(p.config.Filtering.KeepRdataFile) > 0 {
p.activeFilters = append(p.activeFilters, p.keepRdataFilter)
}

if len(p.listFqdns) > 0 {
p.activeFilters = append(p.activeFilters, p.dropFqdnFilter)
}
Expand Down Expand Up @@ -166,6 +173,36 @@ func (p *FilteringProcessor) loadQueryIpList(fname string, drop bool) (uint64, e
return read, err
}

func (p *FilteringProcessor) loadKeepRdataIpList(fname string) (uint64, error) {
file, err := os.Open(fname)
if err != nil {
return 0, err
}

scanner := bufio.NewScanner(file)
var read uint64
var ipsetbuilder netaddr.IPSetBuilder
for scanner.Scan() {
read++
ipOrPrefix := strings.ToLower(scanner.Text())
prefix, err := netaddr.ParseIPPrefix(ipOrPrefix)
if err != nil {
ip, err := netaddr.ParseIP(ipOrPrefix)
if err != nil {
p.LogError("%s in in %s is neither an IP address nor a prefix", ipOrPrefix, fname)
continue
}
ipsetbuilder.Add(ip)
continue
}
ipsetbuilder.AddPrefix(prefix)
}

p.rDataIpsetKeep, err = ipsetbuilder.IPSet()

return read, err
}

func (p *FilteringProcessor) LoadQueryIpList() {
if len(p.config.Filtering.DropQueryIpFile) > 0 {
read, err := p.loadQueryIpList(p.config.Filtering.DropQueryIpFile, true)
Expand All @@ -184,6 +221,16 @@ func (p *FilteringProcessor) LoadQueryIpList() {
}
}

func (p *FilteringProcessor) LoadrDataIpList() {
if len(p.config.Filtering.KeepRdataFile) > 0 {
read, err := p.loadKeepRdataIpList(p.config.Filtering.KeepRdataFile)
if err != nil {
p.LogError("unable to open rdata ip file: ", err)
}
p.LogInfo("loaded with %d rdata ip to the keep list", read)
}
}

func (p *FilteringProcessor) LoadDomainsList() {
if len(p.config.Filtering.DropFqdnFile) > 0 {
file, err := os.Open(p.config.Filtering.DropFqdnFile)
Expand Down Expand Up @@ -294,6 +341,21 @@ func (p *FilteringProcessor) ipFilter(dm *dnsutils.DnsMessage) bool {
return false
}

func (p *FilteringProcessor) keepRdataFilter(dm *dnsutils.DnsMessage) bool {
if len(dm.DNS.DnsRRs.Answers) > 0 {
// If even one exists in filter list then pass through filter
for _, answer := range dm.DNS.DnsRRs.Answers {
if answer.Rdatatype == "A" || answer.Rdatatype == "AAAA" {
ip, _ := netaddr.ParseIP(answer.Rdata)
if p.rDataIpsetKeep.Contains(ip) {
return false
}
}
}
}
return true
}

func (p *FilteringProcessor) dropFqdnFilter(dm *dnsutils.DnsMessage) bool {
if _, ok := p.listFqdns[dm.DNS.Qname]; ok {
return true
Expand Down
93 changes: 93 additions & 0 deletions transformers/filtering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,99 @@ func TestFilteringByQueryIp(t *testing.T) {

}

func TestFilteringByKeepRdataIp(t *testing.T) {
// config
config := dnsutils.GetFakeConfigTransformers()
config.Filtering.KeepRdataFile = "../testsdata/filtering_rdataip_keep.txt"

log := logger.New(false)
outChans := []chan dnsutils.DnsMessage{}

// init subproccesor
filtering := NewFilteringProcessor(config, logger.New(false), "test", 0, outChans, log.Info, log.Error)

dm := dnsutils.GetFakeDnsMessage()
dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "A",
Rdata: "192.168.0.1",
},
}
if filtering.CheckIfDrop(&dm) == false {
t.Errorf("dns query should be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "A",
Rdata: "192.168.1.10",
},
}
if filtering.CheckIfDrop(&dm) == true {
t.Errorf("dns query should not be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "A",
Rdata: "192.168.1.11", // included in subnet
},
}
if filtering.CheckIfDrop(&dm) == true {
t.Errorf("dns query should not be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "A",
Rdata: "192.0.2.3", // dropped by subnet
},
}
if filtering.CheckIfDrop(&dm) == false {
t.Errorf("dns query should be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "A",
Rdata: "192.0.2.1",
},
}
if filtering.CheckIfDrop(&dm) == true {
t.Errorf("dns query should not be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "AAAA",
Rdata: "2001:db8:85a3::8a2e:370:7334",
},
}
if filtering.CheckIfDrop(&dm) == true {
t.Errorf("dns query should not be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "AAAA",
Rdata: "2041::7334",
},
}
if filtering.CheckIfDrop(&dm) == false {
t.Errorf("dns query should be dropped!")
}

dm.DNS.DnsRRs.Answers = []dnsutils.DnsAnswer{
{
Rdatatype: "AAAA",
Rdata: "2001:0dbd:85a3::0001",
},
}
if filtering.CheckIfDrop(&dm) == true {
t.Errorf("dns query should not be dropped!")
}
}

func TestFilteringByFqdn(t *testing.T) {
// config
config := dnsutils.GetFakeConfigTransformers()
Expand Down

0 comments on commit d00bd08

Please sign in to comment.