Use urlfilter format in rebinding allow list
This commit is contained in:
@@ -593,5 +593,5 @@
|
|||||||
"rebinding_protection_enabled": "Enable protection from DNS rebinding attacks",
|
"rebinding_protection_enabled": "Enable protection from DNS rebinding attacks",
|
||||||
"rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.",
|
"rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.",
|
||||||
"rebinding_allowed_hosts_title": "Allowed domains",
|
"rebinding_allowed_hosts_title": "Allowed domains",
|
||||||
"rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains."
|
"rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains. Here you can specify the exact domain names, wildcards and urlfilter-rules, e.g. 'example.org', '*.example.org' or '||example.org^'."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type Server struct {
|
|||||||
queryLog querylog.QueryLog // Query log instance
|
queryLog querylog.QueryLog // Query log instance
|
||||||
stats stats.Stats
|
stats stats.Stats
|
||||||
access *accessCtx
|
access *accessCtx
|
||||||
|
rebinding *dnsRebindChecker
|
||||||
|
|
||||||
ipset ipsetCtx
|
ipset ipsetCtx
|
||||||
|
|
||||||
@@ -222,6 +223,13 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize DNS rebinding module
|
||||||
|
// --
|
||||||
|
s.rebinding, err = newRebindChecker(s.conf.RebindingAllowedHosts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Register web handlers if necessary
|
// Register web handlers if necessary
|
||||||
// --
|
// --
|
||||||
if !webRegistered && s.conf.HTTPRegister != nil {
|
if !webRegistered && s.conf.HTTPRegister != nil {
|
||||||
|
|||||||
@@ -796,9 +796,9 @@ func TestBlockedDNSRebinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.conf.RebindingProtectionEnabled = true
|
s.conf.RebindingProtectionEnabled = true
|
||||||
s.conf.RebindingAllowedHosts = []string{
|
s.rebinding, _ = newRebindChecker([]string{
|
||||||
"nip.io.",
|
"||nip.io^",
|
||||||
}
|
})
|
||||||
reply, err = dns.Exchange(&req, addr.String())
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ func (s *Server) setConfig(dc dnsConfig) (restart bool) {
|
|||||||
|
|
||||||
if dc.RebindingAllowedHosts != nil {
|
if dc.RebindingAllowedHosts != nil {
|
||||||
s.conf.RebindingAllowedHosts = *dc.RebindingAllowedHosts
|
s.conf.RebindingAllowedHosts = *dc.RebindingAllowedHosts
|
||||||
|
restart = true
|
||||||
}
|
}
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
|
|||||||
@@ -9,10 +9,41 @@ import (
|
|||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/urlfilter"
|
||||||
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dnsRebindChecker struct {
|
type dnsRebindChecker struct {
|
||||||
|
allowDomainEngine *urlfilter.DNSEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRebindChecker(allowedHosts []string) (*dnsRebindChecker, error) {
|
||||||
|
buf := strings.Builder{}
|
||||||
|
for _, s := range allowedHosts {
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
rulesStorage, err := filterlist.NewRuleStorage([]filterlist.RuleList{
|
||||||
|
&filterlist.StringRuleList{
|
||||||
|
ID: int(0),
|
||||||
|
RulesText: buf.String(),
|
||||||
|
IgnoreCosmetic: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dnsRebindChecker{
|
||||||
|
allowDomainEngine: urlfilter.NewDNSEngine(rulesStorage),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isAllowedDomain(domain string) bool {
|
||||||
|
_, ok := c.allowDomainEngine.Match(domain)
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPrivate reports whether ip is a private address, according to
|
// IsPrivate reports whether ip is a private address, according to
|
||||||
@@ -87,14 +118,11 @@ func (s *Server) isResponseRebind(domain, host string) bool {
|
|||||||
defer timer.LogElapsed("DNS Rebinding check for %s -> %s", domain, host)
|
defer timer.LogElapsed("DNS Rebinding check for %s -> %s", domain, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, h := range s.conf.RebindingAllowedHosts {
|
if s.rebinding.isAllowedDomain(domain) {
|
||||||
if strings.HasSuffix(domain, h) {
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c := dnsRebindChecker{}
|
return s.rebinding.isRebindHost(host)
|
||||||
return c.isRebindHost(host)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRebindingFilteringAfterResponse(ctx *dnsContext) int {
|
func processRebindingFilteringAfterResponse(ctx *dnsContext) int {
|
||||||
@@ -157,7 +185,7 @@ func (s *Server) preventRebindResponse(ctx *dnsContext) (*dnsfilter.Result, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debug(m)
|
log.Debug(m)
|
||||||
blocked := s.isResponseRebind(domainName, host)
|
blocked := s.isResponseRebind(strings.TrimSuffix(domainName, "."), host)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestRebindingPrivateAddresses(t *testing.T) {
|
func TestRebindingPrivateAddresses(t *testing.T) {
|
||||||
c := &dnsRebindChecker{}
|
c, _ := newRebindChecker(nil)
|
||||||
|
|
||||||
r1 := byte(rand.Int31() & 0xFE)
|
r1 := byte(rand.Int31() & 0xFE)
|
||||||
r2 := byte(rand.Int31() & 0xFE)
|
r2 := byte(rand.Int31() & 0xFE)
|
||||||
@@ -53,9 +53,11 @@ func TestRebindLocalhost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIsResponseRebind(t *testing.T) {
|
func TestIsResponseRebind(t *testing.T) {
|
||||||
s := &Server{}
|
c, _ := newRebindChecker([]string{
|
||||||
s.conf.RebindingAllowedHosts = []string{
|
"||totally-safe.com^",
|
||||||
"totally-safe.com",
|
})
|
||||||
|
s := &Server{
|
||||||
|
rebinding: c,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, host := range []string{
|
for _, host := range []string{
|
||||||
@@ -84,14 +86,14 @@ func TestIsResponseRebind(t *testing.T) {
|
|||||||
"localhost",
|
"localhost",
|
||||||
} {
|
} {
|
||||||
s.conf.RebindingProtectionEnabled = true
|
s.conf.RebindingProtectionEnabled = true
|
||||||
assert.True(t, s.isResponseRebind("example.com", host))
|
assert.Truef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("absolutely.totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
|
|
||||||
s.conf.RebindingProtectionEnabled = false
|
s.conf.RebindingProtectionEnabled = false
|
||||||
assert.False(t, s.isResponseRebind("example.com", host))
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("absolutely.totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, host := range []string{
|
for _, host := range []string{
|
||||||
@@ -99,13 +101,13 @@ func TestIsResponseRebind(t *testing.T) {
|
|||||||
"another-example.com",
|
"another-example.com",
|
||||||
} {
|
} {
|
||||||
s.conf.RebindingProtectionEnabled = true
|
s.conf.RebindingProtectionEnabled = true
|
||||||
assert.False(t, s.isResponseRebind("example.com", host))
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("absolutely.totally-legit.com", host))
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
|
|
||||||
s.conf.RebindingProtectionEnabled = false
|
s.conf.RebindingProtectionEnabled = false
|
||||||
assert.False(t, s.isResponseRebind("example.com", host))
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("totally-safe.com", host))
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
assert.False(t, s.isResponseRebind("absolutely.totally-legit.com", host))
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user