Cleanup docs and the chaos middleware
Make the CH middleware actually work. Needs a bit of a hack to route the fake version.bind and friends zone to the correct handler. Fiddle with the order in directive.go so that CH queries get logged as well. Secondly add class rewriting to the rewrite middleware handler and also log the class by default.
This commit is contained in:
parent
45ac2dd0c0
commit
6445a3f2f0
10 changed files with 60 additions and 20 deletions
|
@ -51,17 +51,16 @@ var directiveOrder = []directive{
|
||||||
{"shutdown", setup.Shutdown},
|
{"shutdown", setup.Shutdown},
|
||||||
|
|
||||||
// Directives that inject handlers (middleware)
|
// Directives that inject handlers (middleware)
|
||||||
|
{"log", setup.Log},
|
||||||
{"prometheus", setup.Prometheus},
|
{"prometheus", setup.Prometheus},
|
||||||
{"chaos", setup.Chaos},
|
|
||||||
{"rewrite", setup.Rewrite},
|
{"rewrite", setup.Rewrite},
|
||||||
{"loadbalance", setup.Loadbalance},
|
{"loadbalance", setup.Loadbalance},
|
||||||
{"log", setup.Log},
|
|
||||||
{"errors", setup.Errors},
|
|
||||||
|
|
||||||
{"file", setup.File},
|
{"file", setup.File},
|
||||||
|
{"chaos", setup.Chaos},
|
||||||
{"secondary", setup.Secondary},
|
{"secondary", setup.Secondary},
|
||||||
{"etcd", setup.Etcd},
|
{"etcd", setup.Etcd},
|
||||||
{"proxy", setup.Proxy},
|
{"proxy", setup.Proxy},
|
||||||
|
{"errors", setup.Errors},
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterDirective adds the given directive to caddy's list of directives.
|
// RegisterDirective adds the given directive to caddy's list of directives.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# chaos
|
# chaos
|
||||||
|
|
||||||
`chaos`
|
The `chaos` middleware allows CoreDNS to response to TXT queries in CH class.
|
||||||
|
Useful for retrieving version or author information from the server.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
|
@ -11,16 +12,12 @@ chaos [version] [authors...]
|
||||||
* `version` the version to return, defaults to CoreDNS.
|
* `version` the version to return, defaults to CoreDNS.
|
||||||
* `authors` what authors to return. No default.
|
* `authors` what authors to return. No default.
|
||||||
|
|
||||||
|
Note this middleware can only be specified for a zone once. This is because it hijacks
|
||||||
|
the zones `version.bind`, `version.server`, `authors.bind`, `hostname.bind` and
|
||||||
|
`id.server`, which means it can only be routed to one middleware.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
etcd {
|
chaos CoreDNS-001 "Miek Gieben" miek@miek.nl
|
||||||
path /skydns
|
|
||||||
endpoint endpoint...
|
|
||||||
stubzones
|
|
||||||
}
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
* `path` /skydns
|
|
||||||
* `endpoint` endpoints...
|
|
||||||
* `stubzones`
|
|
||||||
|
|
|
@ -22,7 +22,10 @@ func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
||||||
if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT {
|
if state.QClass() != dns.ClassCHAOS || state.QType() != dns.TypeTXT {
|
||||||
return c.Next.ServeDNS(ctx, w, r)
|
return c.Next.ServeDNS(ctx, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
|
m.SetReply(r)
|
||||||
|
|
||||||
hdr := dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
|
hdr := dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
|
||||||
switch state.Name() {
|
switch state.Name() {
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package etcd
|
package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
|
@ -11,6 +12,9 @@ import (
|
||||||
|
|
||||||
func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := middleware.State{W: w, Req: r}
|
state := middleware.State{W: w, Req: r}
|
||||||
|
if state.QClass() != dns.ClassINET {
|
||||||
|
return dns.RcodeServerFailure, fmt.Errorf("etcd: can only deal with ClassINET")
|
||||||
|
}
|
||||||
|
|
||||||
// We need to check stubzones first, because we may get a request for a zone we
|
// We need to check stubzones first, because we may get a request for a zone we
|
||||||
// are not auth. for *but* do have a stubzone forward for. If we do the stubzone
|
// are not auth. for *but* do have a stubzone forward for. If we do the stubzone
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package file
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
@ -24,6 +25,10 @@ type (
|
||||||
|
|
||||||
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
state := middleware.State{W: w, Req: r}
|
state := middleware.State{W: w, Req: r}
|
||||||
|
if state.QClass() != dns.ClassINET {
|
||||||
|
return dns.RcodeServerFailure, fmt.Errorf("file: can only deal with ClassINET")
|
||||||
|
}
|
||||||
|
|
||||||
qname := state.Name()
|
qname := state.Name()
|
||||||
zone := middleware.Zones(f.Zones.Names).Matches(qname)
|
zone := middleware.Zones(f.Zones.Names).Matches(qname)
|
||||||
if zone == "" {
|
if zone == "" {
|
||||||
|
|
|
@ -23,6 +23,7 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
|
||||||
if middleware.Name(state.Name()).Matches(rule.NameScope) {
|
if middleware.Name(state.Name()).Matches(rule.NameScope) {
|
||||||
responseRecorder := middleware.NewResponseRecorder(w)
|
responseRecorder := middleware.NewResponseRecorder(w)
|
||||||
rcode, err := l.Next.ServeDNS(ctx, responseRecorder, r)
|
rcode, err := l.Next.ServeDNS(ctx, responseRecorder, r)
|
||||||
|
|
||||||
if rcode > 0 {
|
if rcode > 0 {
|
||||||
// There was an error up the chain, but no response has been written yet.
|
// There was an error up the chain, but no response has been written yet.
|
||||||
// The error must be handled here so the log entry will record the response size.
|
// The error must be handled here so the log entry will record the response size.
|
||||||
|
@ -58,7 +59,7 @@ const (
|
||||||
// DefaultLogFilename is the default log filename.
|
// DefaultLogFilename is the default log filename.
|
||||||
DefaultLogFilename = "query.log"
|
DefaultLogFilename = "query.log"
|
||||||
// CommonLogFormat is the common log format.
|
// CommonLogFormat is the common log format.
|
||||||
CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{type} {name} {proto} {>do} {>bufsize}" {rcode} {size} {duration}`
|
CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{type} {class} {name} {proto} {>do} {>bufsize}" {rcode} {size} {duration}`
|
||||||
// CommonLogEmptyValue is the common empty log value.
|
// CommonLogEmptyValue is the common empty log value.
|
||||||
CommonLogEmptyValue = "-"
|
CommonLogEmptyValue = "-"
|
||||||
// CombinedLogFormat is the combined log format.
|
// CombinedLogFormat is the combined log format.
|
||||||
|
|
|
@ -20,6 +20,7 @@ type (
|
||||||
// If ServeDNS writes to the response body, it should return a status
|
// If ServeDNS writes to the response body, it should return a status
|
||||||
// code of 0. This signals to other handlers above it that the response
|
// code of 0. This signals to other handlers above it that the response
|
||||||
// body is already written, and that they should not write to it also.
|
// body is already written, and that they should not write to it also.
|
||||||
|
// TODO(miek): explain return codes better.
|
||||||
//
|
//
|
||||||
// If ServeDNS encounters an error, it should return the error value
|
// If ServeDNS encounters an error, it should return the error value
|
||||||
// so it can be logged by designated error-handling middleware.
|
// so it can be logged by designated error-handling middleware.
|
||||||
|
|
|
@ -16,6 +16,9 @@ rewrite from to
|
||||||
If from *and* to look like a DNS type (`A`, `MX`, etc.) the type of the message will be rewriten,
|
If from *and* to look like a DNS type (`A`, `MX`, etc.) the type of the message will be rewriten,
|
||||||
i.e. to rewrite ANY queries to HINFO, use `rewrite ANY HINFO`.
|
i.e. to rewrite ANY queries to HINFO, use `rewrite ANY HINFO`.
|
||||||
|
|
||||||
|
If from *and* to look like a DNS class (`IN`, `CH`, or `HS`) the class of the message will be
|
||||||
|
rewritten. I.e. to rewrite CH queries to IN use `rewrite CH IN`.
|
||||||
|
|
||||||
If it does not look like a type a name is assumed and the qname in the message is rewritten, this
|
If it does not look like a type a name is assumed and the qname in the message is rewritten, this
|
||||||
needs to be a full match of the name `rewrite miek.nl example.org`.
|
needs to be a full match of the name `rewrite miek.nl example.org`.
|
||||||
|
|
||||||
|
|
|
@ -58,24 +58,29 @@ type Rule interface {
|
||||||
// the type of the request is rewritten, otherwise the name is.
|
// the type of the request is rewritten, otherwise the name is.
|
||||||
// Note: TSIG signed requests will be invalid.
|
// Note: TSIG signed requests will be invalid.
|
||||||
type SimpleRule struct {
|
type SimpleRule struct {
|
||||||
From, To string
|
From, To string
|
||||||
fromType, toType uint16
|
fromType, toType uint16
|
||||||
|
fromClass, toClass uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimpleRule creates a new Simple Rule
|
// NewSimpleRule creates a new Simple Rule
|
||||||
func NewSimpleRule(from, to string) SimpleRule {
|
func NewSimpleRule(from, to string) SimpleRule {
|
||||||
tpf := dns.StringToType[from]
|
tpf := dns.StringToType[from]
|
||||||
tpt := dns.StringToType[to]
|
tpt := dns.StringToType[to]
|
||||||
// It's only a type if uppercase is used.
|
clf := dns.StringToClass[from]
|
||||||
|
clt := dns.StringToClass[to]
|
||||||
|
// It's only a type/class if uppercase is used.
|
||||||
if from != strings.ToUpper(from) {
|
if from != strings.ToUpper(from) {
|
||||||
tpf = 0
|
tpf = 0
|
||||||
|
clf = 0
|
||||||
from = middleware.Name(from).Normalize()
|
from = middleware.Name(from).Normalize()
|
||||||
}
|
}
|
||||||
if to != strings.ToUpper(to) {
|
if to != strings.ToUpper(to) {
|
||||||
tpt = 0
|
tpt = 0
|
||||||
|
clt = 0
|
||||||
to = middleware.Name(to).Normalize()
|
to = middleware.Name(to).Normalize()
|
||||||
}
|
}
|
||||||
return SimpleRule{From: from, To: to, fromType: tpf, toType: tpt}
|
return SimpleRule{From: from, To: to, fromType: tpf, toType: tpt, fromClass: clf, toClass: clt}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite rewrites the the current request.
|
// Rewrite rewrites the the current request.
|
||||||
|
@ -89,6 +94,15 @@ func (s SimpleRule) Rewrite(r *dns.Msg) Result {
|
||||||
return RewriteIgnored
|
return RewriteIgnored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// class rewrite
|
||||||
|
if s.fromClass > 0 && s.toClass > 0 {
|
||||||
|
if r.Question[0].Qclass == s.fromClass {
|
||||||
|
r.Question[0].Qclass = s.toClass
|
||||||
|
return RewriteDone
|
||||||
|
}
|
||||||
|
return RewriteIgnored
|
||||||
|
}
|
||||||
|
|
||||||
// name rewite
|
// name rewite
|
||||||
if s.From == r.Question[0].Name {
|
if s.From == r.Question[0].Name {
|
||||||
r.Question[0].Name = s.To
|
r.Question[0].Name = s.To
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/miekg/coredns/middleware/chaos"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +98,6 @@ func New(addr string, configs []Config, gracefulTimeout time.Duration) (*Server,
|
||||||
|
|
||||||
// Set up each zone
|
// Set up each zone
|
||||||
for _, conf := range configs {
|
for _, conf := range configs {
|
||||||
// TODO(miek): something better here?
|
|
||||||
if _, exists := s.zones[conf.Host]; exists {
|
if _, exists := s.zones[conf.Host]; exists {
|
||||||
return nil, fmt.Errorf("cannot serve %s - host already defined for address %s", conf.Address(), s.Addr)
|
return nil, fmt.Errorf("cannot serve %s - host already defined for address %s", conf.Address(), s.Addr)
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,19 @@ func New(addr string, configs []Config, gracefulTimeout time.Duration) (*Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.zones[conf.Host] = z
|
s.zones[conf.Host] = z
|
||||||
|
|
||||||
|
// A bit of a hack. Loop through the middlewares of this zone and check if
|
||||||
|
// they have enabled the chaos middleware. If so add the special chaos zones.
|
||||||
|
Middleware:
|
||||||
|
for _, mid := range z.config.Middleware {
|
||||||
|
fn := mid(nil)
|
||||||
|
if _, ok := fn.(chaos.Chaos); ok {
|
||||||
|
for _, ch := range []string{"authors.bind.", "version.bind.", "version.server.", "hostname.bind.", "id.server."} {
|
||||||
|
s.zones[ch] = z
|
||||||
|
}
|
||||||
|
break Middleware
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue