-
Notifications
You must be signed in to change notification settings - Fork 65
/
client-blocklist.go
86 lines (73 loc) · 2.45 KB
/
client-blocklist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package rdns
import (
"sync"
"time"
"github.com/miekg/dns"
"github.com/sirupsen/logrus"
)
// ClientBlocklist is a resolver that matches the IPs of clients against a blocklist
type ClientBlocklist struct {
id string
ClientBlocklistOptions
resolver Resolver
mu sync.RWMutex
metrics *BlocklistMetrics
}
var _ Resolver = &ClientBlocklist{}
type ClientBlocklistOptions struct {
// Optional, if the client is found to match the blocklist, send the query to this resolver.
BlocklistResolver Resolver
BlocklistDB IPBlocklistDB
// Refresh period for the blocklist. Disabled if 0.
BlocklistRefresh time.Duration
}
// NewClientBlocklistIP returns a new instance of a client blocklist resolver.
func NewClientBlocklist(id string, resolver Resolver, opt ClientBlocklistOptions) (*ClientBlocklist, error) {
blocklist := &ClientBlocklist{
id: id,
resolver: resolver,
ClientBlocklistOptions: opt,
metrics: NewBlocklistMetrics(id),
}
// Start the refresh goroutines if we have a list and a refresh period was given
if blocklist.BlocklistDB != nil && blocklist.BlocklistRefresh > 0 {
go blocklist.refreshLoopBlocklist(blocklist.BlocklistRefresh)
}
return blocklist, nil
}
// Resolve a DNS query after checking the client's IP against a blocklist. Responds with
// 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 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")
return r.BlocklistResolver.Resolve(q, ci)
}
log.Debug("blocking client")
return refused(q), nil
}
r.metrics.allowed.Add(1)
return r.resolver.Resolve(q, ci)
}
func (r *ClientBlocklist) String() string {
return r.id
}
func (r *ClientBlocklist) refreshLoopBlocklist(refresh time.Duration) {
for {
time.Sleep(refresh)
log := Log.WithField("id", r.id)
log.Debug("reloading blocklist")
db, err := r.BlocklistDB.Reload()
if err != nil {
Log.WithError(err).Error("failed to load rules")
continue
}
r.mu.Lock()
r.BlocklistDB.Close()
r.BlocklistDB = db
r.mu.Unlock()
}
}