Support naming blocklists to help with logging (#201)

* Support naming blocklists to help with logging

* Support naming of lists in response blocklists too

* Add list name to client-blocklist as well
This commit is contained in:
Frank Olbricht
2022-01-09 07:44:53 -07:00
committed by GitHub
parent a48eca521d
commit acc8842fad
18 changed files with 137 additions and 76 deletions

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/miekg/dns"
"github.com/sirupsen/logrus"
)
// Blocklist is a resolver that returns NXDOMAIN or a spoofed IP for every query that
@@ -92,8 +93,8 @@ func (r *Blocklist) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
// Forward to upstream or the optional allowlist-resolver immediately if there's a match in the allowlist
if allowlistDB != nil {
if _, _, rule, ok := allowlistDB.Match(question); ok {
log = log.WithField("rule", rule)
if _, _, match, ok := allowlistDB.Match(question); ok {
log = log.WithFields(logrus.Fields{"list": match.List, "rule": match.Rule})
r.metrics.allowed.Add(1)
if r.AllowListResolver != nil {
log.WithField("resolver", r.AllowListResolver.String()).Debug("matched allowlist, forwarding")
@@ -104,14 +105,14 @@ func (r *Blocklist) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
}
}
ip, name, rule, ok := blocklistDB.Match(question)
ip, name, match, ok := blocklistDB.Match(question)
if !ok {
// Didn't match anything, pass it on to the next resolver
log.WithField("resolver", r.resolver.String()).Debug("forwarding unmodified query to resolver")
r.metrics.allowed.Add(1)
return r.resolver.Resolve(q, ci)
}
log = log.WithField("rule", rule)
log = log.WithFields(logrus.Fields{"list": match.List, "rule": match.Rule})
r.metrics.blocked.Add(1)
// If we got a name for the PTR query, respond to it

View File

@@ -16,7 +16,7 @@ func TestBlocklistRegexp(t *testing.T) {
`(^|\.)block\.test`,
`(^|\.)evil\.test`,
})
m, err := NewRegexpDB(loader)
m, err := NewRegexpDB("testlist", loader)
require.NoError(t, err)
opt := BlocklistOptions{
@@ -51,9 +51,9 @@ func TestBlocklistAllow(t *testing.T) {
allowloader := NewStaticLoader([]string{
`(^|\.)good\.evil\.test`,
})
blockDB, err := NewRegexpDB(blockloader)
blockDB, err := NewRegexpDB("testlist", blockloader)
require.NoError(t, err)
allowDB, err := NewRegexpDB(allowloader)
allowDB, err := NewRegexpDB("testlist", allowloader)
require.NoError(t, err)
opt := BlocklistOptions{

View File

@@ -14,6 +14,7 @@ import (
// .domain.com: matches domain.com and all subdomains
// *.domain.com: matches all subdomains but not domain.com
type DomainDB struct {
name string
root node
loader BlocklistLoader
}
@@ -23,7 +24,7 @@ type node map[string]node
var _ BlocklistDB = &DomainDB{}
// NewDomainDB returns a new instance of a matcher for a list of regular expressions.
func NewDomainDB(loader BlocklistLoader) (*DomainDB, error) {
func NewDomainDB(name string, loader BlocklistLoader) (*DomainDB, error) {
rules, err := loader.Load()
if err != nil {
return nil, err
@@ -55,14 +56,14 @@ func NewDomainDB(loader BlocklistLoader) (*DomainDB, error) {
n = subNode
}
}
return &DomainDB{root, loader}, nil
return &DomainDB{name, root, loader}, nil
}
func (m *DomainDB) Reload() (BlocklistDB, error) {
return NewDomainDB(m.loader)
return NewDomainDB(m.name, m.loader)
}
func (m *DomainDB) Match(q dns.Question) (net.IP, string, string, bool) {
func (m *DomainDB) Match(q dns.Question) (net.IP, string, *BlocklistMatch, bool) {
s := strings.TrimSuffix(q.Name, ".")
var matched []string
parts := strings.Split(s, ".")
@@ -71,18 +72,36 @@ func (m *DomainDB) Match(q dns.Question) (net.IP, string, string, bool) {
part := parts[i]
subNode, ok := n[part]
if !ok {
return nil, "", "", false
return nil, "", nil, false
}
matched = append(matched, part)
if _, ok := subNode[""]; ok { // exact and sub-domain match
return nil, "", matchedDomainParts(".", matched), true
return nil,
"",
&BlocklistMatch{
List: m.name,
Rule: matchedDomainParts(".", matched),
},
true
}
if _, ok := subNode["*"]; ok && i > 0 { // wildcard match on sub-domains
return nil, "", matchedDomainParts("*.", matched), true
return nil,
"",
&BlocklistMatch{
List: m.name,
Rule: matchedDomainParts("*.", matched),
},
true
}
n = subNode
}
return nil, "", matchedDomainParts("", matched), len(n) == 0 // exact match
return nil,
"",
&BlocklistMatch{
List: m.name,
Rule: matchedDomainParts("", matched),
},
len(n) == 0 // exact match
}
func (m *DomainDB) String() string {

View File

@@ -18,7 +18,7 @@ func TestDomainDB(t *testing.T) {
".domain4.com",
})
m, err := NewDomainDB(loader)
m, err := NewDomainDB("testlist", loader)
require.NoError(t, err)
tests := []struct {
@@ -61,7 +61,7 @@ func TestDomainDBError(t *testing.T) {
}
for _, test := range tests {
loader := NewStaticLoader([]string{test.name})
_, err := NewDomainDB(loader)
_, err := NewDomainDB("testlist", loader)
require.Error(t, err)
}
}

View File

@@ -11,6 +11,7 @@ import (
// IP4 and IP6 records can be spoofed independently, however it's not possible to block only one type. If
// IP4 is given but no IP6, then a domain match will still result in an NXDOMAIN for the IP6 address.
type HostsDB struct {
name string
filters map[string]ipRecords
ptrMap map[string]string // PTR lookup map
loader BlocklistLoader
@@ -24,7 +25,7 @@ type ipRecords struct {
var _ BlocklistDB = &HostsDB{}
// NewHostsDB returns a new instance of a matcher for a list of regular expressions.
func NewHostsDB(loader BlocklistLoader) (*HostsDB, error) {
func NewHostsDB(name string, loader BlocklistLoader) (*HostsDB, error) {
rules, err := loader.Load()
if err != nil {
return nil, err
@@ -69,24 +70,36 @@ func NewHostsDB(loader BlocklistLoader) (*HostsDB, error) {
}
ptrMap[reverseAddr] = names[0]
}
return &HostsDB{filters, ptrMap, loader}, nil
return &HostsDB{name, filters, ptrMap, loader}, nil
}
func (m *HostsDB) Reload() (BlocklistDB, error) {
return NewHostsDB(m.loader)
return NewHostsDB(m.name, m.loader)
}
func (m *HostsDB) Match(q dns.Question) (net.IP, string, string, bool) {
func (m *HostsDB) Match(q dns.Question) (net.IP, string, *BlocklistMatch, bool) {
if q.Qtype == dns.TypePTR {
name, ok := m.ptrMap[q.Name]
return nil, name, "", ok
return nil, name, nil, ok
}
name := strings.TrimSuffix(q.Name, ".")
ips, ok := m.filters[name]
if q.Qtype == dns.TypeA {
return ips.ip4, "", ips.ip4.String() + " " + name, ok
return ips.ip4,
"",
&BlocklistMatch{
List: m.name,
Rule: ips.ip4.String() + " " + name,
},
ok
}
return ips.ip6, "", ips.ip6.String() + " " + name, ok
return ips.ip6,
"",
&BlocklistMatch{
List: m.name,
Rule: ips.ip6.String() + " " + name,
},
ok
}
func (m *HostsDB) String() string {

View File

@@ -20,7 +20,7 @@ func TestHostsDB(t *testing.T) {
"192.168.1.1 domain6.com",
})
m, err := NewHostsDB(loader)
m, err := NewHostsDB("testlist", loader)
require.NoError(t, err)
tests := []struct {
@@ -39,8 +39,9 @@ func TestHostsDB(t *testing.T) {
}
for _, test := range tests {
q := dns.Question{Name: test.q, Qtype: test.typ, Qclass: dns.ClassINET}
ip, _, _, ok := m.Match(q)
ip, _, match, ok := m.Match(q)
require.Equal(t, test.match, ok, "query: %s", test.q)
require.Equal(t, test.ip, ip, "query: %s", test.q)
require.Equal(t, "testlist", match.List)
}
}

View File

@@ -30,13 +30,13 @@ func (m MultiDB) Reload() (BlocklistDB, error) {
return NewMultiDB(newDBs...)
}
func (m MultiDB) Match(q dns.Question) (net.IP, string, string, bool) {
func (m MultiDB) Match(q dns.Question) (net.IP, string, *BlocklistMatch, bool) {
for _, db := range m.dbs {
if ip, name, rule, ok := db.Match(q); ok {
return ip, name, rule, ok
if ip, name, match, ok := db.Match(q); ok {
return ip, name, match, ok
}
}
return nil, "", "", false
return nil, "", nil, false
}
func (m MultiDB) String() string {

View File

@@ -10,6 +10,7 @@ import (
// RegexpDB holds a list of regular expressions against which it evaluates DNS queries.
type RegexpDB struct {
name string
rules []*regexp.Regexp
loader BlocklistLoader
}
@@ -17,7 +18,7 @@ type RegexpDB struct {
var _ BlocklistDB = &RegexpDB{}
// NewRegexpDB returns a new instance of a matcher for a list of regular expressions.
func NewRegexpDB(loader BlocklistLoader) (*RegexpDB, error) {
func NewRegexpDB(name string, loader BlocklistLoader) (*RegexpDB, error) {
rules, err := loader.Load()
if err != nil {
return nil, err
@@ -35,20 +36,20 @@ func NewRegexpDB(loader BlocklistLoader) (*RegexpDB, error) {
filters = append(filters, re)
}
return &RegexpDB{filters, loader}, nil
return &RegexpDB{name, filters, loader}, nil
}
func (m *RegexpDB) Reload() (BlocklistDB, error) {
return NewRegexpDB(m.loader)
return NewRegexpDB(m.name, m.loader)
}
func (m *RegexpDB) Match(q dns.Question) (net.IP, string, string, bool) {
func (m *RegexpDB) Match(q dns.Question) (net.IP, string, *BlocklistMatch, bool) {
for _, rule := range m.rules {
if rule.MatchString(q.Name) {
return nil, "", rule.String(), true
return nil, "", &BlocklistMatch{List: m.name, Rule: rule.String()}, true
}
}
return nil, "", "", false
return nil, "", nil, false
}
func (m *RegexpDB) String() string {

View File

@@ -14,7 +14,15 @@ type BlocklistDB interface {
// Returns true if the question matches a rule. If the IP is not nil,
// respond with the given IP. NXDOMAIN otherwise.
Match(q dns.Question) (net.IP, string, string, bool)
Match(q dns.Question) (net.IP, string, *BlocklistMatch, bool)
fmt.Stringer
}
// BlocklistMatch is returned by blocklists when a match is found. It contains
// information about what rule matched, what list it was from etc. Used mostly
// for logging.
type BlocklistMatch struct {
List string // Identifier or name of the blocklist
Rule string // Identifier for the rule that matched
}

View File

@@ -9,6 +9,7 @@ import (
// Network ranges are stored in a trie (one for IP4 and one for IP6) to allow for
// efficient matching
type CidrDB struct {
name string
ip4, ip6 *ipBlocklistTrie
loader BlocklistLoader
}
@@ -16,12 +17,13 @@ type CidrDB struct {
var _ IPBlocklistDB = &CidrDB{}
// NewCidrDB returns a new instance of a matcher for a list of networks.
func NewCidrDB(loader BlocklistLoader) (*CidrDB, error) {
func NewCidrDB(name string, loader BlocklistLoader) (*CidrDB, error) {
rules, err := loader.Load()
if err != nil {
return nil, err
}
db := &CidrDB{
name: name,
ip4: new(ipBlocklistTrie),
ip6: new(ipBlocklistTrie),
loader: loader,
@@ -53,14 +55,16 @@ func NewCidrDB(loader BlocklistLoader) (*CidrDB, error) {
}
func (m *CidrDB) Reload() (IPBlocklistDB, error) {
return NewCidrDB(m.loader)
return NewCidrDB(m.name, m.loader)
}
func (m *CidrDB) Match(ip net.IP) (string, bool) {
func (m *CidrDB) Match(ip net.IP) (*BlocklistMatch, bool) {
if addr := ip.To4(); addr == nil {
return m.ip6.hasIP(ip)
rule, ok := m.ip6.hasIP(ip)
return &BlocklistMatch{List: m.name, Rule: rule}, ok
}
return m.ip4.hasIP(ip)
rule, ok := m.ip4.hasIP(ip)
return &BlocklistMatch{List: m.name, Rule: rule}, ok
}
func (m *CidrDB) Close() error {

View File

@@ -13,7 +13,7 @@ func TestCidrDB(t *testing.T) {
"1.2.0.0/16",
"2a03:2880:f101:83::0/64",
})
db, err := NewCidrDB(loader)
db, err := NewCidrDB("testlist", loader)
require.NoError(t, err)
tests := []struct {

View File

@@ -49,8 +49,8 @@ func NewClientBlocklist(id string, resolver Resolver, opt ClientBlocklistOptions
// REFUSED if the client IP is on the blocklist, or sends the query to an alternative
// resolver if one is configured.
func (r *ClientBlocklist) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
if rule, ok := r.BlocklistDB.Match(ci.SourceIP); ok {
log := Log.WithFields(logrus.Fields{"id": r.id, "qname": qName(q), "rule": rule, "ip": ci.SourceIP})
if match, ok := r.BlocklistDB.Match(ci.SourceIP); ok {
log := Log.WithFields(logrus.Fields{"id": r.id, "qname": qName(q), "list": match.List, "rule": match.Rule, "ip": ci.SourceIP})
r.metrics.blocked.Add(1)
if r.BlocklistResolver != nil {
log.WithField("resolver", r.BlocklistResolver).Debug("client on blocklist, forwarding to blocklist-resolver")

View File

@@ -127,6 +127,7 @@ type group struct {
// Block/Allowlist items for blocklist-v2
type list struct {
Name string
Format string
Source string
CacheDir string `toml:"cache-dir"` // Where to store copies of remote blocklists for faster startup

View File

@@ -323,7 +323,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
if len(g.Blocklist) > 0 && g.Source != "" {
return fmt.Errorf("static blocklist can't be used with 'source' in '%s'", id)
}
blocklistDB, err := newBlocklistDB(list{Format: g.Format, Source: g.Source}, g.Blocklist)
blocklistDB, err := newBlocklistDB(list{Name: id, Format: g.Format, Source: g.Source}, g.Blocklist)
if err != nil {
return err
}
@@ -347,7 +347,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
}
var blocklistDB rdns.BlocklistDB
if len(g.Blocklist) > 0 {
blocklistDB, err = newBlocklistDB(list{Format: g.BlocklistFormat}, g.Blocklist)
blocklistDB, err = newBlocklistDB(list{Name: id, Format: g.BlocklistFormat}, g.Blocklist)
if err != nil {
return err
}
@@ -506,7 +506,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
}
var blocklistDB rdns.IPBlocklistDB
if len(g.Blocklist) > 0 {
blocklistDB, err = newIPBlocklistDB(list{Format: g.BlocklistFormat}, g.LocationDB, g.Blocklist)
blocklistDB, err = newIPBlocklistDB(list{Name: id, Format: g.BlocklistFormat}, g.LocationDB, g.Blocklist)
if err != nil {
return err
}
@@ -579,7 +579,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
}
var blocklistDB rdns.IPBlocklistDB
if len(g.Blocklist) > 0 {
blocklistDB, err = newIPBlocklistDB(list{Format: g.BlocklistFormat}, g.LocationDB, g.Blocklist)
blocklistDB, err = newIPBlocklistDB(list{Name: id, Format: g.BlocklistFormat}, g.LocationDB, g.Blocklist)
if err != nil {
return err
}
@@ -680,6 +680,10 @@ func newBlocklistDB(l list, rules []string) (rdns.BlocklistDB, error) {
if err != nil {
return nil, err
}
name := l.Name
if name == "" {
name = l.Source
}
var loader rdns.BlocklistLoader
if len(rules) > 0 {
loader = rdns.NewStaticLoader(rules)
@@ -698,11 +702,11 @@ func newBlocklistDB(l list, rules []string) (rdns.BlocklistDB, error) {
}
switch l.Format {
case "regexp", "":
return rdns.NewRegexpDB(loader)
return rdns.NewRegexpDB(name, loader)
case "domain":
return rdns.NewDomainDB(loader)
return rdns.NewDomainDB(name, loader)
case "hosts":
return rdns.NewHostsDB(loader)
return rdns.NewHostsDB(name, loader)
default:
return nil, fmt.Errorf("unsupported format '%s'", l.Format)
}
@@ -713,6 +717,10 @@ func newIPBlocklistDB(l list, locationDB string, rules []string) (rdns.IPBlockli
if err != nil {
return nil, err
}
name := l.Name
if name == "" {
name = l.Source
}
var loader rdns.BlocklistLoader
if len(rules) > 0 {
loader = rdns.NewStaticLoader(rules)
@@ -732,9 +740,9 @@ func newIPBlocklistDB(l list, locationDB string, rules []string) (rdns.IPBlockli
switch l.Format {
case "cidr", "":
return rdns.NewCidrDB(loader)
return rdns.NewCidrDB(name, loader)
case "location":
return rdns.NewGeoIPDB(loader, locationDB)
return rdns.NewGeoIPDB(name, loader, locationDB)
default:
return nil, fmt.Errorf("unsupported format '%s'", l.Format)
}

View File

@@ -531,7 +531,7 @@ Options:
- `blocklist-resolver` - Alternative resolver for queries matching the blocklist, rather than responding with NXDOMAIN. Optional.
- `blocklist-format` - The format the blocklist is provided in. Only used if `blocklist-source` is not provided. Can be `regexp`, `domain`, or `hosts`. Defaults to `regexp`.
- `blocklist-refresh` - Time interval (in seconds) in which external (remote or local) blocklists are reloaded. Optional.
- `blocklist-source` - An array of blocklists, each with `format` and `source`.
- `blocklist-source` - An array of blocklists, each with `format`, `source` and optionally `name`.
- `allowlist-resolver` - Alternative resolver for queries matching the allowlist, rather than forwarding to the default resolver.
- `allowlist-format` - The format the allowlist is provided in. Only used if `allowlist-source` is not provided. Can be `regexp`, `domain`, or `hosts`. Defaults to `regexp`.
- `allowlist-refresh` - Time interval (in seconds) in which external allowlists are reloaded. Optional.
@@ -580,7 +580,7 @@ blocklist = [
]
```
Blocklist that loads two rule-sets. One from an HTTP server, the other from a file on disk. Both are reloaded once a day.
Blocklist that loads two rule-sets. One from an HTTP server, the other from a file on disk. Both are reloaded once a day. A `name` can be provided which will be used in logs instead of `source`.
```toml
[groups.cloudflare-blocklist]
@@ -588,7 +588,7 @@ type = "blocklist-v2"
resolvers = ["cloudflare-dot"]
blocklist-refresh = 86400
blocklist-source = [
{format = "domain", source = "https://raw.githubusercontent.com/cbuijs/accomplist/master/deugniets/routedns.blocklist.domain.list"},
{name = "cbuijs/blocklist" format = "domain", source = "https://raw.githubusercontent.com/cbuijs/accomplist/master/deugniets/routedns.blocklist.domain.list"},
{format = "regexp", source = "/path/to/local/regexp.list"},
]
```
@@ -646,7 +646,7 @@ Options:
- For `response-blocklist-ip`, the value can be `cidr`, or `location`. Defaults to `cidr`.
- For `response-blocklist-name`, the value can be `regexp`, `domain`, or `hosts`. Defaults to `regexp`.
- `blocklist-refresh` - Time interval (in seconds) in which external (remote or local) blocklists are reloaded. Optional.
- `blocklist-source` - An array of blocklists, each with `format`, `source` and optionally `cache-dir` (see notes for [Query Blockists](#Query-Blocklist)).
- `blocklist-source` - An array of blocklists, each with `format`, `source` and optionally `cache-dir` (see notes for [Query Blockists](#Query-Blocklist)) as well as `name` which assigns a name to the list used in logs (defaults to `source`).
- `filter` - If set to `true` in `response-blocklist-ip`, matching records will be removed from responses rather than the whole response. If there is no answer record left after applying the filter, NXDOMAIN will be returned unless an alternative `blocklist-resolver` is defined.
- `location-db` - If location-based IP blocking is used, this specifies the GeoIP data file to load. Optional. Defaults to /usr/share/GeoIP/GeoLite2-City.mmdb
@@ -700,7 +700,7 @@ blocklist-source = [
]
```
Response blocklist that is cached on local disk for faster startup
Response blocklist that is cached on local disk for faster startup. By default, logs will contain the source (in this case the URL) of a match, but different name can be specified with `name`.
```toml
[groups.cloudflare-blocklist]
@@ -708,7 +708,7 @@ type = "response-blocklist-ip"
resolvers = ["cloudflare-dot"]
blocklist-refresh = 86400
blocklist-source = [
{source = "https://host/block.cidr.txt", cache-dir="/var/tmp"},
{name = "my-block-list", source = "https://host/block.cidr.txt", cache-dir="/var/tmp"},
]
```
@@ -744,7 +744,7 @@ Options:
- `blocklist-resolver` - Alternative resolver for responses matching a rule, the query will be re-sent to this resolver. Optional.
- `blocklist-format` - The format the blocklist is provided in. Only used if `blocklist-source` is not provided. Values can be `cidr`, or `location`. Defaults to `cidr`.
- `blocklist-refresh` - Time interval (in seconds) in which external (remote or local) blocklists are reloaded. Optional.
- `blocklist-source` - An array of blocklists, each with `format` and `source`.
- `blocklist-source` - An array of blocklists, each with `format` and `source` and optionally `name`.
- `location-db` - If location-based IP blocking is used, this specifies the GeoIP data file to load. Optional. Defaults to /usr/share/GeoIP/GeoLite2-City.mmdb
Examples:

View File

@@ -13,6 +13,7 @@ import (
// its location is looked up in a database and the result is compared to the
// blocklist rules.
type GeoIPDB struct {
name string
loader BlocklistLoader
geoDB *maxminddb.Reader
geoDBFile string
@@ -22,7 +23,7 @@ type GeoIPDB struct {
var _ IPBlocklistDB = &GeoIPDB{}
// NewGeoIPDB returns a new instance of a matcher for a location rules.
func NewGeoIPDB(loader BlocklistLoader, geoDBFile string) (*GeoIPDB, error) {
func NewGeoIPDB(name string, loader BlocklistLoader, geoDBFile string) (*GeoIPDB, error) {
if geoDBFile == "" {
geoDBFile = "/usr/share/GeoIP/GeoLite2-City.mmdb"
}
@@ -51,6 +52,7 @@ func NewGeoIPDB(loader BlocklistLoader, geoDBFile string) (*GeoIPDB, error) {
db[value] = struct{}{}
}
return &GeoIPDB{
name: name,
geoDB: geoDB,
geoDBFile: geoDBFile,
db: db,
@@ -59,10 +61,10 @@ func NewGeoIPDB(loader BlocklistLoader, geoDBFile string) (*GeoIPDB, error) {
}
func (m *GeoIPDB) Reload() (IPBlocklistDB, error) {
return NewGeoIPDB(m.loader, m.geoDBFile)
return NewGeoIPDB(m.name, m.loader, m.geoDBFile)
}
func (m *GeoIPDB) Match(ip net.IP) (string, bool) {
func (m *GeoIPDB) Match(ip net.IP) (*BlocklistMatch, bool) {
var record struct {
Continent struct {
GeoNameID uint64 `maxminddb:"geoname_id"`
@@ -80,7 +82,7 @@ func (m *GeoIPDB) Match(ip net.IP) (string, bool) {
if err := m.geoDB.Lookup(ip, &record); err != nil {
Log.WithField("ip", ip).WithError(err).Error("failed to lookup ip in geo location database")
return "", false
return nil, false
}
// Try to find the continent, country, or city GeoName ID in the blocklist
@@ -90,10 +92,13 @@ func (m *GeoIPDB) Match(ip net.IP) (string, bool) {
}
for _, id := range ids {
if _, ok := m.db[id]; ok {
return fmt.Sprintf("%d", id), true
return &BlocklistMatch{
List: m.name,
Rule: fmt.Sprintf("%d", id),
}, true
}
}
return "", false
return nil, false
}
func (m *GeoIPDB) Close() error {

View File

@@ -28,13 +28,13 @@ func (m MultiIPDB) Reload() (IPBlocklistDB, error) {
return NewMultiIPDB(newDBs...)
}
func (m MultiIPDB) Match(ip net.IP) (string, bool) {
func (m MultiIPDB) Match(ip net.IP) (*BlocklistMatch, bool) {
for _, db := range m.dbs {
if rule, ok := db.Match(ip); ok {
return rule, ok
if match, ok := db.Match(ip); ok {
return match, ok
}
}
return "", false
return nil, false
}
func (m MultiIPDB) Close() error {

View File

@@ -13,7 +13,7 @@ import (
// IPBlocklistDB is a database containing IPs used in blocklists.
type IPBlocklistDB interface {
Reload() (IPBlocklistDB, error)
Match(ip net.IP) (string, bool)
Match(ip net.IP) (*BlocklistMatch, bool)
Close() error
fmt.Stringer
}
@@ -103,8 +103,8 @@ func (r *ResponseBlocklistIP) blockIfMatch(query, answer *dns.Msg, ci ClientInfo
default:
continue
}
if rule, ok := r.BlocklistDB.Match(ip); ok {
log := logger(r.id, query, ci).WithFields(logrus.Fields{"rule": rule, "ip": ip})
if match, ok := r.BlocklistDB.Match(ip); ok {
log := logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.List, "rule": match.Rule, "ip": ip})
if r.BlocklistResolver != nil {
log.WithField("resolver", r.BlocklistResolver).Debug("blocklist match, forwarding to blocklist-resolver")
return r.BlocklistResolver.Resolve(query, ci)
@@ -147,8 +147,8 @@ func (r *ResponseBlocklistIP) filterRR(query *dns.Msg, ci ClientInfo, rrs []dns.
newRRs = append(newRRs, rr)
continue
}
if rule, ok := r.BlocklistDB.Match(ip); ok {
logger(r.id, query, ci).WithFields(logrus.Fields{"rule": rule, "ip": ip}).Debug("filtering response")
if match, ok := r.BlocklistDB.Match(ip); ok {
logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.List, "rule": match.Rule, "ip": ip}).Debug("filtering response")
continue
}
newRRs = append(newRRs, rr)