Skip to content

Commit

Permalink
feat(pkg/filter/mapfilter): block hostname if any of its parents is b…
Browse files Browse the repository at this point in the history
…locked
  • Loading branch information
qdm12 committed Nov 15, 2023
1 parent 903b287 commit 22b0d39
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 3 deletions.
3 changes: 3 additions & 0 deletions pkg/middlewares/filter/mapfilter/mocks_generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package mapfilter

//go:generate mockgen -destination=mocks_test.go -package=$GOPACKAGE . Metrics
106 changes: 106 additions & 0 deletions pkg/middlewares/filter/mapfilter/mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 19 additions & 3 deletions pkg/middlewares/filter/mapfilter/request.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
package mapfilter

import "github.com/miekg/dns"
import (
"strings"

"github.com/miekg/dns"
)

func (m *Filter) FilterRequest(request *dns.Msg) (blocked bool) {
m.updateLock.RLock()
defer m.updateLock.RUnlock()

for _, question := range request.Question {
fqdnHostname := question.Name
_, blocked = m.fqdnHostnames[fqdnHostname]
if blocked {
labels := dns.SplitDomainName(fqdnHostname)

// Check from least specific to most specific if it is blocked.
// Root domain '' corresponds to `nil` labels and is always allowed.
// Single label domains (i.e. localhost) are checked as well.
for i := range labels {
labelStartIndex := len(labels) - i - 1
parent := strings.Join(labels[labelStartIndex:], ".")
fqdnParent := dns.Fqdn(parent)
_, blocked = m.fqdnHostnames[fqdnParent]
if !blocked {
continue
}
class := dns.ClassToString[question.Qclass]
qType := dns.TypeToString[question.Qtype]
m.metrics.HostnamesFilteredInc(class, qType)
return blocked
}
}

return false
}
117 changes: 117 additions & 0 deletions pkg/middlewares/filter/mapfilter/request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package mapfilter

import (
"testing"

"github.com/golang/mock/gomock"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
)

func Test_FilterRequest(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
makeFilter func(ctrl *gomock.Controller) *Filter
request *dns.Msg
blocked bool
}{
"no_question": {
makeFilter: func(_ *gomock.Controller) *Filter {
return &Filter{}
},
request: &dns.Msg{},
},
"no_filter": {
makeFilter: func(_ *gomock.Controller) *Filter {
return &Filter{}
},
request: &dns.Msg{
Question: []dns.Question{
{Name: "host.domain.com.", Qclass: dns.ClassINET, Qtype: dns.TypeA},
},
},
},
"no_matching_filters": {
makeFilter: func(_ *gomock.Controller) *Filter {
return &Filter{
fqdnHostnames: map[string]struct{}{
"example.org.": {},
},
}
},
request: &dns.Msg{
Question: []dns.Question{
{Name: "host.domain.com.", Qclass: dns.ClassINET, Qtype: dns.TypeA},
},
},
},
"blocked_exact_match": {
makeFilter: func(ctrl *gomock.Controller) *Filter {
metrics := NewMockMetrics(ctrl)
metrics.EXPECT().HostnamesFilteredInc("IN", "A")
return &Filter{
fqdnHostnames: map[string]struct{}{
"host.domain.com.": {},
},
metrics: metrics,
}
},
request: &dns.Msg{
Question: []dns.Question{
{Name: "host.domain.com.", Qclass: dns.ClassINET, Qtype: dns.TypeA},
},
},
blocked: true,
},
"blocked_parent_match": {
makeFilter: func(ctrl *gomock.Controller) *Filter {
metrics := NewMockMetrics(ctrl)
metrics.EXPECT().HostnamesFilteredInc("IN", "A")
return &Filter{
fqdnHostnames: map[string]struct{}{
"domain.com.": {},
},
metrics: metrics,
}
},
request: &dns.Msg{
Question: []dns.Question{
{Name: "host.domain.com.", Qclass: dns.ClassINET, Qtype: dns.TypeA},
},
},
blocked: true,
},
"blocked_grand_parent_match": {
makeFilter: func(ctrl *gomock.Controller) *Filter {
metrics := NewMockMetrics(ctrl)
metrics.EXPECT().HostnamesFilteredInc("IN", "A")
return &Filter{
fqdnHostnames: map[string]struct{}{
"domain.com.": {},
},
metrics: metrics,
}
},
request: &dns.Msg{
Question: []dns.Question{
{Name: "xyz.host.domain.com.", Qclass: dns.ClassINET, Qtype: dns.TypeA},
},
},
blocked: true,
},
}

for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()

ctrl := gomock.NewController(t)
filter := testCase.makeFilter(ctrl)

blocked := filter.FilterRequest(testCase.request)
assert.Equal(t, testCase.blocked, blocked)
})
}
}

0 comments on commit 22b0d39

Please sign in to comment.