mirror of https://github.com/0xERR0R/blocky.git
Option to handle FQDN only requests (#561)
This commit is contained in:
parent
e313ef7fe8
commit
c912356740
|
@ -425,6 +425,7 @@ type Config struct {
|
|||
KeyFile string `yaml:"keyFile"`
|
||||
BootstrapDNS BootstrapConfig `yaml:"bootstrapDns"`
|
||||
HostsFile HostsFileConfig `yaml:"hostsFile"`
|
||||
FqdnOnly bool `yaml:"fqdnOnly" default:"false"`
|
||||
Filtering FilteringConfig `yaml:"filtering"`
|
||||
}
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ Works only on Linux/\*nix OS due to golang limitations under Windows.
|
|||
- 123.123.123.123
|
||||
```
|
||||
|
||||
|
||||
## Filtering
|
||||
|
||||
Under certain circumstances, it may be useful to filter some types of DNS queries. You can define one or more DNS query
|
||||
|
@ -150,6 +149,18 @@ types, all queries with these types will be dropped (empty answer will be return
|
|||
|
||||
This configuration will drop all 'AAAA' (IPv6) queries.
|
||||
|
||||
## FQDN only
|
||||
|
||||
In domain environments, it may be usefull to only response to FQDN requests. If this option is enabled blocky respond immidiatly
|
||||
with NXDOMAIN if the request is not a valid FQDN. The request is therfore not further processed by other options like custom or conditional.
|
||||
Please be aware that by enabling it your hostname resolution will break unless every hostname is part of a domain.
|
||||
|
||||
!!! example
|
||||
|
||||
```yaml
|
||||
fqdnOnly: true
|
||||
```
|
||||
|
||||
## Custom DNS
|
||||
|
||||
You can define your own domain name to IP mappings. For example, you can use a user-friendly name for a network printer
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
// CUSTOMDNS // the query was resolved by a custom rule
|
||||
// HOSTSFILE // the query was resolved by looking up the hosts file
|
||||
// FILTERED // the query was filtered by query type
|
||||
// NOTFQDN // the query was filtered as it is not fqdn conform
|
||||
// )
|
||||
type ResponseType int
|
||||
|
||||
|
|
|
@ -98,9 +98,12 @@ const (
|
|||
// ResponseTypeFILTERED is a ResponseType of type FILTERED.
|
||||
// the query was filtered by query type
|
||||
ResponseTypeFILTERED
|
||||
// ResponseTypeNOTFQDN is a ResponseType of type NOTFQDN.
|
||||
// the query was filtered as it is not fqdn conform
|
||||
ResponseTypeNOTFQDN
|
||||
)
|
||||
|
||||
const _ResponseTypeName = "RESOLVEDCACHEDBLOCKEDCONDITIONALCUSTOMDNSHOSTSFILEFILTERED"
|
||||
const _ResponseTypeName = "RESOLVEDCACHEDBLOCKEDCONDITIONALCUSTOMDNSHOSTSFILEFILTEREDNOTFQDN"
|
||||
|
||||
var _ResponseTypeNames = []string{
|
||||
_ResponseTypeName[0:8],
|
||||
|
@ -110,6 +113,7 @@ var _ResponseTypeNames = []string{
|
|||
_ResponseTypeName[32:41],
|
||||
_ResponseTypeName[41:50],
|
||||
_ResponseTypeName[50:58],
|
||||
_ResponseTypeName[58:65],
|
||||
}
|
||||
|
||||
// ResponseTypeNames returns a list of possible string values of ResponseType.
|
||||
|
@ -127,6 +131,7 @@ var _ResponseTypeMap = map[ResponseType]string{
|
|||
ResponseTypeCUSTOMDNS: _ResponseTypeName[32:41],
|
||||
ResponseTypeHOSTSFILE: _ResponseTypeName[41:50],
|
||||
ResponseTypeFILTERED: _ResponseTypeName[50:58],
|
||||
ResponseTypeNOTFQDN: _ResponseTypeName[58:65],
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
|
@ -145,6 +150,7 @@ var _ResponseTypeValue = map[string]ResponseType{
|
|||
_ResponseTypeName[32:41]: ResponseTypeCUSTOMDNS,
|
||||
_ResponseTypeName[41:50]: ResponseTypeHOSTSFILE,
|
||||
_ResponseTypeName[50:58]: ResponseTypeFILTERED,
|
||||
_ResponseTypeName[58:65]: ResponseTypeNOTFQDN,
|
||||
}
|
||||
|
||||
// ParseResponseType attempts to convert a string to a ResponseType.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package resolver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/0xERR0R/blocky/config"
|
||||
"github.com/0xERR0R/blocky/model"
|
||||
"github.com/0xERR0R/blocky/util"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type FqdnOnlyResolver struct {
|
||||
NextResolver
|
||||
enabled bool
|
||||
}
|
||||
|
||||
func NewFqdnOnlyResolver(cfg config.Config) ChainedResolver {
|
||||
return &FqdnOnlyResolver{
|
||||
enabled: cfg.FqdnOnly,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FqdnOnlyResolver) Resolve(request *model.Request) (*model.Response, error) {
|
||||
if r.enabled {
|
||||
domainFromQuestion := util.ExtractDomain(request.Req.Question[0])
|
||||
if !strings.Contains(domainFromQuestion, ".") {
|
||||
response := new(dns.Msg)
|
||||
response.Rcode = dns.RcodeNameError
|
||||
|
||||
return &model.Response{Res: response, RType: model.ResponseTypeNOTFQDN, Reason: "NOTFQDN"}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return r.next.Resolve(request)
|
||||
}
|
||||
|
||||
func (r *FqdnOnlyResolver) Configuration() (result []string) {
|
||||
if r.enabled {
|
||||
result = []string{"activated"}
|
||||
} else {
|
||||
result = []string{"deactivated"}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package resolver
|
||||
|
||||
import (
|
||||
"github.com/0xERR0R/blocky/config"
|
||||
. "github.com/0xERR0R/blocky/model"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
var _ = Describe("FqdnOnlyResolver", func() {
|
||||
var (
|
||||
sut *FqdnOnlyResolver
|
||||
sutConfig config.Config
|
||||
m *MockResolver
|
||||
mockAnswer *dns.Msg
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
mockAnswer = new(dns.Msg)
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
sut = NewFqdnOnlyResolver(sutConfig).(*FqdnOnlyResolver)
|
||||
m = &MockResolver{}
|
||||
m.On("Resolve", mock.Anything).Return(&Response{Res: mockAnswer}, nil)
|
||||
sut.Next(m)
|
||||
})
|
||||
|
||||
When("Fqdn only is activated", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = config.Config{
|
||||
FqdnOnly: true,
|
||||
}
|
||||
})
|
||||
It("Should delegate to next resolver if request query is fqdn", func() {
|
||||
resp, err := sut.Resolve(newRequest("example.com", dns.Type(dns.TypeA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeSuccess))
|
||||
Expect(resp.RType).Should(Equal(ResponseTypeRESOLVED))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
|
||||
// delegated to next resolver
|
||||
Expect(m.Calls).Should(HaveLen(1))
|
||||
})
|
||||
It("Should return NXDOMAIN if request query is not fqdn", func() {
|
||||
resp, err := sut.Resolve(newRequest("example", dns.Type(dns.TypeAAAA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeNameError))
|
||||
Expect(resp.RType).Should(Equal(ResponseTypeNOTFQDN))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
|
||||
// no call of next resolver
|
||||
Expect(m.Calls).Should(BeZero())
|
||||
})
|
||||
It("Configure should output activated", func() {
|
||||
c := sut.Configuration()
|
||||
Expect(c).Should(HaveLen(1))
|
||||
Expect(c[0]).Should(Equal("activated"))
|
||||
})
|
||||
})
|
||||
|
||||
When("Fqdn only is deactivated", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = config.Config{
|
||||
FqdnOnly: false,
|
||||
}
|
||||
})
|
||||
It("Should delegate to next resolver if request query is fqdn", func() {
|
||||
resp, err := sut.Resolve(newRequest("example.com", dns.Type(dns.TypeA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeSuccess))
|
||||
Expect(resp.RType).Should(Equal(ResponseTypeRESOLVED))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
|
||||
// delegated to next resolver
|
||||
Expect(m.Calls).Should(HaveLen(1))
|
||||
})
|
||||
It("Should delegate to next resolver if request query is not fqdn", func() {
|
||||
resp, err := sut.Resolve(newRequest("example", dns.Type(dns.TypeAAAA)))
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(resp.Res.Rcode).Should(Equal(dns.RcodeSuccess))
|
||||
Expect(resp.RType).Should(Equal(ResponseTypeRESOLVED))
|
||||
Expect(resp.Res.Answer).Should(BeEmpty())
|
||||
|
||||
// delegated to next resolver
|
||||
Expect(m.Calls).Should(HaveLen(1))
|
||||
})
|
||||
It("Configure should output deactivated", func() {
|
||||
c := sut.Configuration()
|
||||
Expect(c).Should(HaveLen(1))
|
||||
Expect(c[0]).Should(Equal("deactivated"))
|
||||
})
|
||||
})
|
||||
})
|
|
@ -397,6 +397,7 @@ func createQueryResolver(
|
|||
|
||||
r = resolver.Chain(
|
||||
resolver.NewFilteringResolver(cfg.Filtering),
|
||||
resolver.NewFqdnOnlyResolver(*cfg),
|
||||
clientNamesResolver,
|
||||
resolver.NewQueryLoggingResolver(cfg.QueryLog),
|
||||
resolver.NewMetricsResolver(cfg.Prometheus),
|
||||
|
|
Loading…
Reference in New Issue