blackholing & prom

This commit is contained in:
core 2023-10-03 18:46:12 -04:00
parent b11d4ede6c
commit 1e6c04ce76
Signed by: core
GPG key ID: FDBF740DADDCEECF
6 changed files with 165 additions and 4 deletions

View file

@ -1,6 +1,25 @@
package pancheri
import "slices"
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"slices"
)
var (
blockedDomains = promauto.NewGauge(prometheus.GaugeOpts{
Name: "pancheri_blackhole_blocked_domains_total",
Help: "Number of blocked domains on the blackhole blocklist",
})
blockHits = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_blackhole_block_hits_total",
Help: "Number of requests that have hit the blocklist",
})
blockMisses = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_blackhole_misses_total",
Help: "Number of requests that have missed the blocklist",
})
)
type BlackholeFile struct {
DenyDomains []string `yaml:"deny_domains"`
@ -11,5 +30,13 @@ type Blackholer struct {
}
func (b *Blackholer) ShouldBlock(domain string) bool {
return slices.Contains(b.DenyDomains, domain)
blockedDomains.Set(float64(len(b.DenyDomains)))
blocked := slices.Contains(b.DenyDomains, domain)
if blocked {
blockHits.Inc()
} else {
blockMisses.Inc()
}
return blocked
}

View file

@ -51,6 +51,18 @@ func main() {
zones[authoritativeZone] = zone
}
logrus.WithFields(logrus.Fields{
"port": "2112",
}).Info("starting promhttp listener")
go func() {
err := pancheri.PromMain()
if err != nil {
logrus.Errorf("error in prom listener: %s", err)
os.Exit(1)
}
}()
logrus.WithFields(logrus.Fields{
"host": c.Server.Host,
"port": c.Server.Port,
@ -74,7 +86,7 @@ func main() {
}).Info("enabling blackholer")
b = &pancheri.Blackholer{
DenyDomains: *new([]string),
DenyDomains: []string{},
}
for _, file := range c.Blackhole.BlockLists {
@ -104,6 +116,10 @@ func main() {
logrus.WithFields(logrus.Fields{
"file": file,
}).Infof("loaded %d hosts", len(cfg.DenyDomains))
if !b.ShouldBlock("eu1.clevertap-prod.com") {
logrus.Errorf("failed sanity check!")
}
}
logrus.WithFields(logrus.Fields{
@ -117,6 +133,7 @@ func main() {
A: &pancheri.Authority{
Zones: zones,
},
B: b,
}
server := &dns.Server{
Addr: c.Server.Host + ":" + c.Server.Port,

9
go.mod
View file

@ -3,11 +3,20 @@ module git.e3t.cc/e3team/pancheri
go 1.21
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.56 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

25
go.sum
View file

@ -1,8 +1,27 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -11,11 +30,17 @@ golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View file

@ -2,6 +2,8 @@ package pancheri
import (
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/sirupsen/logrus"
"strings"
)
@ -13,14 +15,58 @@ type Handler struct {
B *Blackholer
}
func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
var (
numberOfRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_requests_total",
Help: "Number of DNS requests that have been handled by pancheri",
})
numberOfARequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_a_requests_total",
Help: "Number of type A DNS requests that have been handled by pancheri",
})
numberOfARequestsResultingInCNAME = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_a_to_cname_requests_total",
Help: "Number of type A DNS requests that resolved as a CNAME that have been handled by pancheri",
})
numberOfAAAARequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_aaaa_requests_total",
Help: "Number of type A DNS requests that have been handled by pancheri",
})
numberOfAAAARequestsResultingInCNAME = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_aaaa_to_cname_requests_total",
Help: "Number of type AAAA DNS requests that resolved as a CNAME that have been handled by pancheri",
})
numberOfCNAMERequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_cname_requests_total",
Help: "Number of type CNAME DNS requests that have been handled by pancheri",
})
numberOfTXTRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_txt_requests_total",
Help: "Number of type CNAME DNS requests that have been handled by pancheri",
})
numberOfUnsupportedRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_unsupported_requests_total",
Help: "Number of unsupported type requests that have been handled by pancheri",
})
numberOfMultiQuestionRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_multiq_requests_total",
Help: "Number of unsupported multi-question requests that have been handled by pancheri",
})
numberOfUpstreamedRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "pancheri_handler_upstreamed_requests_total",
Help: "Number of requests that were sent upstream",
})
)
func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
numberOfRequests.Inc()
// figure out how we should resolve this
msg := new(dns.Msg)
msg.SetReply(r)
msg.Authoritative = true
if len(r.Question) != 1 {
numberOfMultiQuestionRequests.Inc()
msg.Rcode = dns.RcodeFormatError
err := w.WriteMsg(msg)
if err != nil {
@ -32,6 +78,23 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
// is it blackholed?
if h.B.ShouldBlock(strings.TrimRight(q.Name, ".")) {
// return nxdomain
// alright, send an nxdomain
if r.RecursionDesired {
msg.RecursionAvailable = true
}
msg.Rcode = dns.RcodeNameError
err := w.WriteMsg(msg)
if err != nil {
logrus.Errorf("error responding: %s", err)
return
}
return
}
// is it ours?
for authority, zone := range h.A.Zones {
if strings.HasSuffix(q.Name, authority) {
@ -44,11 +107,13 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}).Trace("responding to query for authoritative zone")
if q.Qtype == dns.TypeA {
numberOfARequests.Inc()
record, ok := zone.ARecords[q.Name]
if !ok {
// SPECIAL CASE: for A and AAAA records, return with a CNAME if and only if that cname exists
cname, cok := zone.CNAMERecords[q.Name]
if cok {
numberOfARequestsResultingInCNAME.Inc()
// return with the CNAME record instead and resolve the CNAME
rendered := cname.Render()
msg.Rcode = dns.RcodeSuccess
@ -125,10 +190,12 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
return
} else if q.Qtype == dns.TypeAAAA {
record, ok := zone.AAAARecords[q.Name]
numberOfAAAARequests.Inc()
if !ok {
// SPECIAL CASE: for A and AAAA records, return with a CNAME if and only if that cname exists
cname, cok := zone.CNAMERecords[q.Name]
if cok {
numberOfAAAARequestsResultingInCNAME.Inc()
// return with the CNAME record instead and resolve the CNAME
rendered := cname.Render()
msg.Rcode = dns.RcodeSuccess
@ -205,6 +272,7 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}
return
} else if q.Qtype == dns.TypeCNAME {
numberOfCNAMERequests.Inc()
record, ok := zone.CNAMERecords[q.Name]
if !ok {
// send an nxdomain
@ -235,6 +303,7 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}
return
} else if q.Qtype == dns.TypeTXT {
numberOfTXTRequests.Inc()
record, ok := zone.TXTRecords[q.Name]
if !ok {
// send an nxdomain
@ -265,6 +334,7 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}
return
} else {
numberOfUnsupportedRequests.Inc()
// not supported
logrus.WithFields(logrus.Fields{
"name": q.Name,
@ -288,6 +358,7 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
}
// no. do we have upstream resolution enabled?
if h.C.Resolver.Enable {
numberOfUpstreamedRequests.Inc()
// alright, resolve it with the resolver
resp, err := h.R.Resolve(q.Name, q.Qtype)
resp.SetReply(r)

12
stats.go Normal file
View file

@ -0,0 +1,12 @@
package pancheri
import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"net/http"
)
func PromMain() error {
http.Handle("/metrics", promhttp.Handler())
err := http.ListenAndServe(":2112", nil)
return err
}