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
In this configuration, we forward all queries to 9.9.9.9 and 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.
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.
**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
. {
example {
10.10.10.0/24 {
net 192.168.0.0/24 192.168.1.0/24
}
split {
net 10.10.10.0/24 allow 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 (
github.com/coredns/caddy v1.1.1
github.com/coredns/coredns v1.9.1
github.com/miekg/dns v1.1.47
github.com/prometheus/client_golang v1.12.1
github.com/coredns/coredns v1.10.0
github.com/miekg/dns v1.1.50
github.com/prometheus/client_golang v1.13.0
)
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/aws/aws-sdk-go v1.44.95 // indirect
github.com/beorn7/perks v1.0.1 // 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/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/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/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/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/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/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tinylib/msgp v1.1.2 // 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/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect
google.golang.org/grpc v1.44.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/api v0.95.0 // 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
// for parsing any extra options the example plugin may have. The first token this function sees is "example".
func setup(c *caddy.Controller) error {
s := Split{}
var s Split
for c.Next() {
r := rule{}
args := c.RemainingArgs()
r.zones = plugin.OriginsFromArgsOrServerBlock(args, c.ServerBlockKeys)
if c.NextBlock() {
n := network{}
_, ipnet, err := net.ParseCIDR(c.Val())
if err != nil {
return err
}
n.record = ipnet
for c.NextBlock() {
for c.NextLine() {
a := c.Val()
_ = a
argsLoop:
for _, v := range c.RemainingArgs() {
r := Rule{
Zones: plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys),
}
log.Debug("zones", r.Zones)
for c.NextBlock() {
prop := c.Val()
args := c.RemainingArgs()
switch prop {
case "net":
log.Debug("net", args)
if len(args) == 0 {
return c.Errf("net: expected at least 1 argument, got 0")
}
var nets []*net.IPNet
var allow bool
var allowNets []*net.IPNet
for _, v := range args {
switch v {
case "allow":
allow = true
default:
_, ipnet, err := net.ParseCIDR(v)
if err != nil {
return err
}
for _, vv := range n.allowed {
if vv.Contains(ipnet.IP) {
continue argsLoop
}
if allow {
allowNets = append(allowNets, ipnet)
} 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.

View File

@ -22,17 +22,18 @@ var log = clog.NewWithPlugin("split")
type Split struct {
Next plugin.Handler
Rule []rule
Rules []Rule
}
type rule struct {
zones []string
networks []network
type Rule struct {
Zones []string
Networks []Network
Fallback net.IP
}
type network struct {
record *net.IPNet
allowed []*net.IPNet
type Network struct {
RecordNetwork *net.IPNet
Allowed []*net.IPNet
}
// ServeDNS implements the plugin.Handler interface. This method gets called when example is used
@ -65,21 +66,21 @@ type ResponsePrinter struct {
state request.Request
r *dns.Msg
src net.IP
rules []rule
rules []Rule
}
// NewResponsePrinter returns ResponseWriter.
func (s Split) NewResponsePrinter(w dns.ResponseWriter, r *dns.Msg) *ResponsePrinter {
state := request.Request{W: w, Req: r}
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.
func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
var rule rule
var rule Rule
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 == "" {
continue
}
@ -94,9 +95,9 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
answers = append(answers, v)
continue
}
var net *network
for _, vv := range rule.networks {
if vv.record.Contains(rec.A) {
var net *Network
for _, vv := range rule.Networks {
if vv.RecordNetwork.Contains(rec.A) {
net = &vv
break
}
@ -106,7 +107,7 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
continue
}
allowed := false
for _, vv := range net.allowed {
for _, vv := range net.Allowed {
if vv.Contains(r.src) {
allowed = true
break
@ -124,5 +125,19 @@ func (r *ResponsePrinter) WriteMsg(res *dns.Msg) error {
} else {
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)
}