add fallback nameserver, rework configuration

Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
Adphi 2022-10-27 13:18:20 +02:00
parent 5bb565b2ab
commit 14e35b5a5e
Signed by: adphi
GPG Key ID: 46BE4062DB2397FF
6 changed files with 933 additions and 59 deletions

View File

@ -63,17 +63,27 @@ This plugin reports readiness to the ready plugin. It will be immediately ready.
## Examples ## Examples
In this configuration, we forward all queries to 9.9.9.9 and filter out A records pointing to an IP address In this configuration, we forward all queries to 10.10.10.1 and to 9.9.9.9 if 10.10.10.1 did not respond.
in the 10.10.10.0/24 network except for queries coming from the 192.168.0.0/24 and 192.168.1.0/24 networks.
**If only used with the forward plugin, the private dns server must be configured as the first forwarded server in the list. Alose, the policy must be configured as sequential, so that the first server is always tried first and the second only if the first do not return any answer.**
We filter out A records pointing to an IP address in the 10.10.10.0/24 network except for queries coming from the 192.168.0.0/24 and 192.168.1.0/24 networks.
If the allowed networks are not defined, the plugin will allow the requests from the same network, e.g. 10.10.10.0/24.
If the record exists both as public and private, the private record will be filtered, resulting with no records at all.
So you can provide a fallback server that will be used to get the public record.
~~~ corefile ~~~ corefile
. { . {
example { split {
10.10.10.0/24 { net 10.10.10.0/24 allow 192.168.0.0/24 192.168.1.0/24
net 192.168.0.0/24 192.168.1.0/24 net 10.1.1.0/24 10.1.2.0/24 # implicitely: allow 10.1.1.0/24 10.1.2.0/24
} fallback 8.8.8.8
}
file example.org
forward . 9.9.9.9 {
policy sequential
} }
forward . 9.9.9.9
} }
~~~ ~~~

15
example.org Normal file
View File

@ -0,0 +1,15 @@
$ORIGIN example.org.
@ 3600 IN SOA ns1.example.org. hostmaster.example.org. (
2003080800 ; serial number
12h ; refresh
15m ; update retry
3w ; expiry
2h ; minimum
)
ns1 IN A 127.0.0.1
www IN A 100.100.100.10
net1 IN A 10.10.10.10
net2 IN A 10.1.1.10
net2 IN A 12.12.12.10
net3 IN A 10.1.2.10
net3 IN A 12.12.13.10

114
go.mod
View File

@ -4,30 +4,118 @@ go 1.17
require ( require (
github.com/coredns/caddy v1.1.1 github.com/coredns/caddy v1.1.1
github.com/coredns/coredns v1.9.1 github.com/coredns/coredns v1.10.0
github.com/miekg/dns v1.1.47 github.com/miekg/dns v1.1.50
github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_golang v1.13.0
) )
require ( require (
cloud.google.com/go/compute v1.7.0 // indirect
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.2.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect
github.com/DataDog/datadog-go v4.8.2+incompatible // indirect
github.com/DataDog/datadog-go/v5 v5.0.2 // indirect
github.com/DataDog/sketches-go v1.2.1 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/antonmedv/expr v1.9.0 // indirect
github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect
github.com/aws/aws-sdk-go v1.44.95 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnstap/golang-dnstap v0.4.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/farsightsec/golang-framestream v0.3.0 // indirect
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 // indirect
github.com/openzipkin/zipkin-go v0.4.0 // indirect
github.com/oschwald/geoip2-golang v1.8.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/procfs v0.8.0 // indirect
golang.org/x/mod v0.4.2 // indirect github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect github.com/tinylib/msgp v1.1.2 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect go.etcd.io/etcd/api/v3 v3.5.4 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
go.etcd.io/etcd/client/v3 v3.5.4 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/tools v0.1.12 // indirect
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/grpc v1.44.0 // indirect google.golang.org/api v0.95.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.41.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.25.0 // indirect
k8s.io/apimachinery v0.25.0 // indirect
k8s.io/client-go v0.24.4 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
) )

719
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -14,40 +14,67 @@ func init() { plugin.Register("split", setup) }
// setup is the function that gets called when the config parser see the token "example". Setup is responsible // setup is the function that gets called when the config parser see the token "example". Setup is responsible
// for parsing any extra options the example plugin may have. The first token this function sees is "example". // for parsing any extra options the example plugin may have. The first token this function sees is "example".
func setup(c *caddy.Controller) error { func setup(c *caddy.Controller) error {
s := Split{} var s Split
for c.Next() { for c.Next() {
r := rule{} r := Rule{
args := c.RemainingArgs() Zones: plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys),
r.zones = plugin.OriginsFromArgsOrServerBlock(args, c.ServerBlockKeys) }
if c.NextBlock() { log.Debug("zones", r.Zones)
n := network{} for c.NextBlock() {
_, ipnet, err := net.ParseCIDR(c.Val()) prop := c.Val()
if err != nil { args := c.RemainingArgs()
return err switch prop {
} case "net":
n.record = ipnet log.Debug("net", args)
for c.NextBlock() { if len(args) == 0 {
for c.NextLine() { return c.Errf("net: expected at least 1 argument, got 0")
a := c.Val() }
_ = a var nets []*net.IPNet
argsLoop: var allow bool
for _, v := range c.RemainingArgs() { var allowNets []*net.IPNet
for _, v := range args {
switch v {
case "allow":
allow = true
default:
_, ipnet, err := net.ParseCIDR(v) _, ipnet, err := net.ParseCIDR(v)
if err != nil { if err != nil {
return err return err
} }
for _, vv := range n.allowed { if allow {
if vv.Contains(ipnet.IP) { allowNets = append(allowNets, ipnet)
continue argsLoop } else {
} nets = append(nets, ipnet)
} }
n.allowed = append(n.allowed, ipnet)
} }
} }
r.networks = append(r.networks, n) if len(allowNets) == 0 {
allowNets = nets[:]
}
for _, v := range nets {
r.Networks = append(r.Networks, Network{
RecordNetwork: v,
Allowed: allowNets,
})
}
case "fallback":
log.Debug("fallback", args)
if r.Fallback != nil {
return c.Errf("fallback already set")
}
if len(args) != 1 {
return c.Errf("fallback: expected 1 argument, got %d", len(args))
}
ip := net.ParseIP(args[0])
if ip == nil {
return c.Errf("fallback: invalid IP %s", args[0])
}
r.Fallback = ip
default:
return c.Errf("unknown property '%s'", prop)
} }
s.Rule = append(s.Rule, r)
} }
s.Rules = append(s.Rules, r)
} }
// Add the Plugin to CoreDNS, so Servers can use it in their plugin chain. // Add the Plugin to CoreDNS, so Servers can use it in their plugin chain.

View File

@ -22,17 +22,18 @@ var log = clog.NewWithPlugin("split")
type Split struct { type Split struct {
Next plugin.Handler Next plugin.Handler
Rule []rule Rules []Rule
} }
type rule struct { type Rule struct {
zones []string Zones []string
networks []network Networks []Network
Fallback net.IP
} }
type network struct { type Network struct {
record *net.IPNet RecordNetwork *net.IPNet
allowed []*net.IPNet Allowed []*net.IPNet
} }
// ServeDNS implements the plugin.Handler interface. This method gets called when example is used // ServeDNS implements the plugin.Handler interface. This method gets called when example is used
@ -65,21 +66,21 @@ type ResponsePrinter struct {
state request.Request state request.Request
r *dns.Msg r *dns.Msg
src net.IP src net.IP
rules []rule rules []Rule
} }
// NewResponsePrinter returns ResponseWriter. // NewResponsePrinter returns ResponseWriter.
func (s Split) NewResponsePrinter(w dns.ResponseWriter, r *dns.Msg) *ResponsePrinter { func (s Split) NewResponsePrinter(w dns.ResponseWriter, r *dns.Msg) *ResponsePrinter {
state := request.Request{W: w, Req: r} state := request.Request{W: w, Req: r}
ip := net.ParseIP(state.IP()) ip := net.ParseIP(state.IP())
return &ResponsePrinter{ResponseWriter: w, r: r, src: ip, rules: s.Rule, state: state} return &ResponsePrinter{ResponseWriter: w, r: r, src: ip, rules: s.Rules, state: state}
} }
// WriteMsg calls the underlying ResponseWriter's WriteMsg method and prints "example" to standard output. // WriteMsg calls the underlying ResponseWriter's WriteMsg method and prints "example" to standard output.
func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error { func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
var rule rule var rule Rule
for _, v := range r.rules { for _, v := range r.rules {
zone := plugin.Zones(v.zones).Matches(r.state.Name()) zone := plugin.Zones(v.Zones).Matches(r.state.Name())
if zone == "" { if zone == "" {
continue continue
} }
@ -94,9 +95,9 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
answers = append(answers, v) answers = append(answers, v)
continue continue
} }
var net *network var net *Network
for _, vv := range rule.networks { for _, vv := range rule.Networks {
if vv.record.Contains(rec.A) { if vv.RecordNetwork.Contains(rec.A) {
net = &vv net = &vv
break break
} }
@ -106,7 +107,7 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
continue continue
} }
allowed := false allowed := false
for _, vv := range net.allowed { for _, vv := range net.Allowed {
if vv.Contains(r.src) { if vv.Contains(r.src) {
allowed = true allowed = true
break break
@ -124,5 +125,19 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
} else { } else {
res.Answer = answers res.Answer = answers
} }
if len(res.Answer) != 0 {
return r.ResponseWriter.WriteMsg(res)
}
if rule.Fallback == nil {
return nil
}
c := new(dns.Client)
req := r.state.Req.Copy()
req.Id = dns.Id()
in, _, err := c.Exchange(req, rule.Fallback.String()+":53")
if err != nil {
return err
}
res.Answer = append(res.Answer, in.Answer...)
return r.ResponseWriter.WriteMsg(res) return r.ResponseWriter.WriteMsg(res)
} }