core: add more transports (#574)
* core: add listening for other protocols Allow CoreDNS to listen for TLS request coming over port 853. This can be enabled with `tls://` in the config file. Implement listening for grps:// as well. a Corefile like: ~~~ . tls://.:1853 { whoami tls } ~~~ Means we listen on 1853 for tls requests, the `tls` config item allows configuration for TLS parameters. We *might* be tempted to use Caddy's Let's Encrypt implementation here. * Refactor coredns/grpc into CoreDNS This makes gRPC a first class citizen in CoreDNS. Add defines as being just another server. * some cleanups * unexport the servers * Move protobuf dir * Hook up TLS properly * Fix test * listen for TLS as well. README updates * disable test, fix package * fix test * Fix tests * Fix remaining test * Some tests * Make the test work * Add grpc test from #580 * fix crash * Fix tests * Close conn * README cleanups * README * link RFC
This commit is contained in:
parent
4985d698e2
commit
bfaf9e0aec
24 changed files with 570 additions and 50 deletions
34
README.md
34
README.md
|
@ -5,9 +5,10 @@
|
|||
[](https://codecov.io/github/coredns/coredns?branch=master)
|
||||
[](https://goreportcard.com/report/coredns/coredns)
|
||||
|
||||
CoreDNS is a DNS server that started as a fork of [Caddy](https://github.com/mholt/caddy/). It has the
|
||||
same model: it chains middleware. In fact it's so similar that CoreDNS is now a server type plugin for
|
||||
Caddy. CoreDNS is also a [Cloud Native Computing Foundation](https://cncf.io) inception level project.
|
||||
CoreDNS is a DNS server that started as a fork of [Caddy](https://github.com/mholt/caddy/). It has
|
||||
the same model: it chains middleware. In fact it's so similar that CoreDNS is now a server type
|
||||
plugin for Caddy. CoreDNS is also a [Cloud Native Computing Foundation](https://cncf.io) inception
|
||||
level project.
|
||||
|
||||
CoreDNS is the successor to [SkyDNS](https://github.com/skynetservices/skydns). SkyDNS is a thin
|
||||
layer that exposes services in etcd in the DNS. CoreDNS builds on this idea and is a generic DNS
|
||||
|
@ -16,6 +17,11 @@ server that can talk to multiple backends (etcd, kubernetes, etc.).
|
|||
CoreDNS aims to be a fast and flexible DNS server. The keyword here is *flexible*: with CoreDNS you
|
||||
are able to do what you want with your DNS data. And if not: write some middleware!
|
||||
|
||||
CoreDNS can listen for DNS request coming in over UDP/TCP (go'old DNS), TLS
|
||||
([RFC 7858](https://tools.ietf.org/html/rfc7858)) and gRPC (not
|
||||
a standard.
|
||||
|
||||
|
||||
Currently CoreDNS is able to:
|
||||
|
||||
* Serve zone data from a file; both DNSSEC (NSEC only) and DNS are supported (*file*).
|
||||
|
@ -44,8 +50,8 @@ Each of the middlewares has a README.md of its own.
|
|||
CoreDNS can be used as an authoritative nameserver for your domains, and should be stable enough to
|
||||
provide you with good DNS(SEC) service.
|
||||
|
||||
There are still a few known [issues](https://github.com/coredns/coredns/issues), and work is ongoing on making
|
||||
things fast and to reduce the memory usage.
|
||||
There are still a few known [issues](https://github.com/coredns/coredns/issues), and work is ongoing
|
||||
on making things fast and to reduce the memory usage.
|
||||
|
||||
All in all, CoreDNS should be able to provide you with enough functionality to replace parts of BIND
|
||||
9, Knot, NSD or PowerDNS and SkyDNS. Most documentation is in the source and some blog articles can
|
||||
|
@ -169,6 +175,24 @@ example.org {
|
|||
}
|
||||
~~~
|
||||
|
||||
Listening on TLS and for gRPC? Use:
|
||||
|
||||
~~~ txt
|
||||
tls://example.org grpc://example.org {
|
||||
# ...
|
||||
}
|
||||
~~~
|
||||
|
||||
Specifying ports works in the same way:
|
||||
|
||||
~~~ txt
|
||||
grpc://example.org:1443 {
|
||||
# ...
|
||||
}
|
||||
~~~
|
||||
|
||||
When no transport protocol is specified the default `dns://` is assumed.
|
||||
|
||||
## Blog and Contact
|
||||
|
||||
Website: <https://coredns.io>
|
||||
|
|
|
@ -11,10 +11,24 @@ import (
|
|||
type zoneAddr struct {
|
||||
Zone string
|
||||
Port string
|
||||
Transport string // dns, tls or grpc
|
||||
}
|
||||
|
||||
// String return z.Zone + ":" + z.Port as a string.
|
||||
func (z zoneAddr) String() string { return z.Zone + ":" + z.Port }
|
||||
// String return the string represenation of z.
|
||||
func (z zoneAddr) String() string { return z.Transport + "://" + z.Zone + ":" + z.Port }
|
||||
|
||||
// Transport returns the protocol of the string s
|
||||
func Transport(s string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(s, TransportTLS+"://"):
|
||||
return TransportTLS
|
||||
case strings.HasPrefix(s, TransportDNS+"://"):
|
||||
return TransportDNS
|
||||
case strings.HasPrefix(s, TransportGRPC+"://"):
|
||||
return TransportGRPC
|
||||
}
|
||||
return TransportDNS
|
||||
}
|
||||
|
||||
// normalizeZone parses an zone string into a structured format with separate
|
||||
// host, and port portions, as well as the original input string.
|
||||
|
@ -23,14 +37,28 @@ func (z zoneAddr) String() string { return z.Zone + ":" + z.Port }
|
|||
func normalizeZone(str string) (zoneAddr, error) {
|
||||
var err error
|
||||
|
||||
// separate host and port
|
||||
// Default to DNS if there isn't a transport protocol prefix.
|
||||
trans := TransportDNS
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(str, TransportTLS+"://"):
|
||||
trans = TransportTLS
|
||||
str = str[len(TransportTLS+"://"):]
|
||||
case strings.HasPrefix(str, TransportDNS+"://"):
|
||||
trans = TransportDNS
|
||||
str = str[len(TransportDNS+"://"):]
|
||||
case strings.HasPrefix(str, TransportGRPC+"://"):
|
||||
trans = TransportGRPC
|
||||
str = str[len(TransportGRPC+"://"):]
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(str)
|
||||
if err != nil {
|
||||
host, port, err = net.SplitHostPort(str + ":")
|
||||
// no error check here; return err at end of function
|
||||
}
|
||||
|
||||
if len(host) > 255 {
|
||||
if len(host) > 255 { // TODO(miek): this should take escaping into account.
|
||||
return zoneAddr{}, fmt.Errorf("specified zone is too long: %d > 255", len(host))
|
||||
}
|
||||
_, d := dns.IsDomainName(host)
|
||||
|
@ -39,8 +67,23 @@ func normalizeZone(str string) (zoneAddr, error) {
|
|||
}
|
||||
|
||||
if port == "" {
|
||||
if trans == TransportDNS {
|
||||
port = Port
|
||||
}
|
||||
|
||||
return zoneAddr{Zone: strings.ToLower(dns.Fqdn(host)), Port: port}, err
|
||||
if trans == TransportTLS {
|
||||
port = TLSPort
|
||||
}
|
||||
if trans == TransportGRPC {
|
||||
port = GRPCPort
|
||||
}
|
||||
}
|
||||
|
||||
return zoneAddr{Zone: strings.ToLower(dns.Fqdn(host)), Port: port, Transport: trans}, err
|
||||
}
|
||||
|
||||
// Supported transports.
|
||||
const (
|
||||
TransportDNS = "dns"
|
||||
TransportTLS = "tls"
|
||||
TransportGRPC = "grpc"
|
||||
)
|
||||
|
|
|
@ -8,10 +8,10 @@ func TestNormalizeZone(t *testing.T) {
|
|||
expected string
|
||||
shouldErr bool
|
||||
}{
|
||||
{".", ".:53", false},
|
||||
{".:54", ".:54", false},
|
||||
{"..", ":", true},
|
||||
{"..", ":", true},
|
||||
{".", "dns://.:53", false},
|
||||
{".:54", "dns://.:54", false},
|
||||
{"..", "://:", true},
|
||||
{"..", "://:", true},
|
||||
} {
|
||||
addr, err := normalizeZone(test.input)
|
||||
actual := addr.String()
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/coredns/coredns/middleware"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
|
@ -21,8 +23,12 @@ type Config struct {
|
|||
// First consumer is the file middleware to looks for zone files in this place.
|
||||
Root string
|
||||
|
||||
// Server is the server that handles this config
|
||||
Server *Server
|
||||
// The transport we implement, normally just "dns" over TCP/UDP, but could be
|
||||
// DNS-over-TLS or DNS-over-gRPC.
|
||||
Transport string
|
||||
|
||||
// TLSConfig when listening for encrypted connections (gRPC, DNS-over-TLS).
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// Middleware stack.
|
||||
Middleware []middleware.Middleware
|
||||
|
@ -50,7 +56,6 @@ func GetConfig(c *caddy.Controller) *Config {
|
|||
// Note that this is order dependent and the order is defined in directives.go, i.e. if your middleware
|
||||
// comes before the middleware you are checking; it will not be there (yet).
|
||||
func GetMiddleware(c *caddy.Controller, name string) middleware.Handler {
|
||||
// TODO(miek): calling the handler h(nil) should be a noop...
|
||||
conf := GetConfig(c)
|
||||
for _, h := range conf.Middleware {
|
||||
x := h(nil)
|
||||
|
|
|
@ -61,15 +61,16 @@ func (h *dnsContext) InspectServerBlocks(sourceFile string, serverBlocks []caddy
|
|||
return nil, err
|
||||
}
|
||||
s.Keys[i] = za.String()
|
||||
if v, ok := dups[za.Zone]; ok {
|
||||
if v, ok := dups[za.String()]; ok {
|
||||
return nil, fmt.Errorf("cannot serve %s - zone already defined for %v", za, v)
|
||||
}
|
||||
dups[za.Zone] = za.String()
|
||||
dups[za.String()] = za.String()
|
||||
|
||||
// Save the config to our master list, and key it for lookups
|
||||
cfg := &Config{
|
||||
Zone: za.Zone,
|
||||
Port: za.Port,
|
||||
Transport: za.Transport,
|
||||
}
|
||||
h.saveConfig(za.String(), cfg)
|
||||
}
|
||||
|
@ -88,11 +89,31 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) {
|
|||
// then we create a server for each group
|
||||
var servers []caddy.Server
|
||||
for addr, group := range groups {
|
||||
// switch on addr
|
||||
switch Transport(addr) {
|
||||
case TransportDNS:
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
|
||||
case TransportTLS:
|
||||
s, err := NewServerTLS(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
|
||||
case TransportGRPC:
|
||||
s, err := NewServergRPC(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers = append(servers, s)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
|
@ -112,14 +133,11 @@ func groupConfigsByListenAddr(configs []*Config) (map[string][]*Config, error) {
|
|||
groups := make(map[string][]*Config)
|
||||
|
||||
for _, conf := range configs {
|
||||
if conf.Port == "" {
|
||||
conf.Port = Port
|
||||
}
|
||||
addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.ListenHost, conf.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrstr := addr.String()
|
||||
addrstr := conf.Transport + "://" + addr.String()
|
||||
groups[addrstr] = append(groups[addrstr], conf)
|
||||
}
|
||||
|
||||
|
@ -129,6 +147,10 @@ func groupConfigsByListenAddr(configs []*Config) (map[string][]*Config, error) {
|
|||
const (
|
||||
// DefaultPort is the default port.
|
||||
DefaultPort = "53"
|
||||
// TLSPort is the default port for DNS-over-TLS.
|
||||
TLSPort = "853"
|
||||
// GRPCPort is the default port for DNS-over-gRPC.
|
||||
GRPCPort = "443"
|
||||
)
|
||||
|
||||
// These "soft defaults" are configurable by
|
||||
|
|
165
core/dnsserver/server-grpc.go
Normal file
165
core/dnsserver/server-grpc.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/peer"
|
||||
|
||||
"github.com/coredns/coredns/pb"
|
||||
)
|
||||
|
||||
// servergRPC represents an instance of a DNS-over-gRPC server.
|
||||
type servergRPC struct {
|
||||
*Server
|
||||
grpcServer *grpc.Server
|
||||
|
||||
listenAddr net.Addr
|
||||
}
|
||||
|
||||
// NewGRPCServer returns a new CoreDNS GRPC server and compiles all middleware in to it.
|
||||
func NewServergRPC(addr string, group []*Config) (*servergRPC, error) {
|
||||
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gs := &servergRPC{Server: s}
|
||||
gs.grpcServer = grpc.NewServer()
|
||||
// trace foo... TODO(miek)
|
||||
pb.RegisterDnsServiceServer(gs.grpcServer, gs)
|
||||
|
||||
return gs, nil
|
||||
}
|
||||
|
||||
// Serve implements caddy.TCPServer interface.
|
||||
func (s *servergRPC) Serve(l net.Listener) error {
|
||||
s.m.Lock()
|
||||
s.listenAddr = l.Addr()
|
||||
s.m.Unlock()
|
||||
|
||||
return s.grpcServer.Serve(l)
|
||||
}
|
||||
|
||||
// ServePacket implements caddy.UDPServer interface.
|
||||
func (s *servergRPC) ServePacket(p net.PacketConn) error { return nil }
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *servergRPC) Listen() (net.Listener, error) {
|
||||
|
||||
// The *tls* middleware must make sure that multiple conflicting
|
||||
// TLS configuration return an error: it can only be specified once.
|
||||
tlsConfig := new(tls.Config)
|
||||
for _, conf := range s.zones {
|
||||
// Should we error if some configs *don't* have TLS?
|
||||
tlsConfig = conf.TLSConfig
|
||||
}
|
||||
|
||||
var (
|
||||
l net.Listener
|
||||
err error
|
||||
)
|
||||
|
||||
if tlsConfig == nil {
|
||||
l, err = net.Listen("tcp", s.Addr[len(TransportGRPC+"://"):])
|
||||
} else {
|
||||
l, err = tls.Listen("tcp", s.Addr[len(TransportGRPC+"://"):], tlsConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *servergRPC) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (s *servergRPC) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
for zone, config := range s.zones {
|
||||
fmt.Println(TransportGRPC + "://" + zone + ":" + config.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *servergRPC) Stop() (err error) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
if s.grpcServer != nil {
|
||||
s.grpcServer.GracefulStop()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Query is the main entry-point into the gRPC server. From here we call ServeDNS like
|
||||
// any normal server. We use a custom responseWriter to pick up the bytes we need to write
|
||||
// back to the client as a protobuf.
|
||||
func (s *servergRPC) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket, error) {
|
||||
msg := new(dns.Msg)
|
||||
err := msg.Unpack(in.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, ok := peer.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("no peer in gRPC context")
|
||||
}
|
||||
|
||||
a, ok := p.Addr.(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no TCP peer in gRPC context: %v", p.Addr)
|
||||
}
|
||||
|
||||
r := &net.IPAddr{IP: a.IP}
|
||||
w := &gRPCresponse{localAddr: s.listenAddr, remoteAddr: r, Msg: msg}
|
||||
|
||||
s.ServeDNS(ctx, w, msg)
|
||||
|
||||
packed, err := w.Msg.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.DnsPacket{Msg: packed}, nil
|
||||
}
|
||||
|
||||
func (g *servergRPC) Shutdown() error {
|
||||
if g.grpcServer != nil {
|
||||
g.grpcServer.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type gRPCresponse struct {
|
||||
localAddr net.Addr
|
||||
remoteAddr net.Addr
|
||||
Msg *dns.Msg
|
||||
}
|
||||
|
||||
// Write is the hack that makes this work. It does not actually write the message
|
||||
// but returns the bytes we need to to write in r. We can then pick this up in Query
|
||||
// and write a proper protobuf back to the client.
|
||||
func (r *gRPCresponse) Write(b []byte) (int, error) {
|
||||
r.Msg = new(dns.Msg)
|
||||
return len(b), r.Msg.Unpack(b)
|
||||
}
|
||||
|
||||
// These methods implement the dns.ResponseWriter interface from Go DNS.
|
||||
func (r *gRPCresponse) Close() error { return nil }
|
||||
func (r *gRPCresponse) TsigStatus() error { return nil }
|
||||
func (r *gRPCresponse) TsigTimersOnly(b bool) { return }
|
||||
func (r *gRPCresponse) Hijack() { return }
|
||||
func (r *gRPCresponse) LocalAddr() net.Addr { return r.localAddr }
|
||||
func (r *gRPCresponse) RemoteAddr() net.Addr { return r.remoteAddr }
|
||||
func (r *gRPCresponse) WriteMsg(m *dns.Msg) error { r.Msg = m; return nil }
|
85
core/dnsserver/server-tls.go
Normal file
85
core/dnsserver/server-tls.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// serverTLS represents an instance of a TLS-over-DNS-server.
|
||||
type serverTLS struct {
|
||||
*Server
|
||||
}
|
||||
|
||||
// NewTLSServer returns a new CoreDNS TLS server and compiles all middleware in to it.
|
||||
func NewServerTLS(addr string, group []*Config) (*serverTLS, error) {
|
||||
|
||||
s, err := NewServer(addr, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &serverTLS{Server: s}, nil
|
||||
}
|
||||
|
||||
// Serve implements caddy.TCPServer interface.
|
||||
func (s *serverTLS) Serve(l net.Listener) error {
|
||||
s.m.Lock()
|
||||
|
||||
// Only fill out the TCP server for this one.
|
||||
s.server[tcp] = &dns.Server{Listener: l, Net: "tcp-tls", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ctx := context.Background()
|
||||
s.ServeDNS(ctx, w, r)
|
||||
})}
|
||||
s.m.Unlock()
|
||||
|
||||
return s.server[tcp].ActivateAndServe()
|
||||
}
|
||||
|
||||
// ServePacket implements caddy.UDPServer interface.
|
||||
func (s *serverTLS) ServePacket(p net.PacketConn) error { return nil }
|
||||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *serverTLS) Listen() (net.Listener, error) {
|
||||
// The *tls* middleware must make sure that multiple conflicting
|
||||
// TLS configuration return an error: it can only be specified once.
|
||||
tlsConfig := new(tls.Config)
|
||||
for _, conf := range s.zones {
|
||||
// Should we error if some configs *don't* have TLS?
|
||||
tlsConfig = conf.TLSConfig
|
||||
}
|
||||
|
||||
var (
|
||||
l net.Listener
|
||||
err error
|
||||
)
|
||||
|
||||
if tlsConfig == nil {
|
||||
l, err = net.Listen("tcp", s.Addr[len(TransportTLS+"://"):])
|
||||
} else {
|
||||
l, err = tls.Listen("tcp", s.Addr[len(TransportTLS+"://"):], tlsConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *serverTLS) ListenPacket() (net.PacketConn, error) { return nil, nil }
|
||||
|
||||
// OnStartupComplete lists the sites served by this server
|
||||
// and any relevant information, assuming Quiet is false.
|
||||
func (s *serverTLS) OnStartupComplete() {
|
||||
if Quiet {
|
||||
return
|
||||
}
|
||||
|
||||
for zone, config := range s.zones {
|
||||
fmt.Println(TransportTLS + "://" + zone + ":" + config.Port)
|
||||
}
|
||||
}
|
|
@ -61,7 +61,6 @@ func NewServer(addr string, group []*Config) (*Server, error) {
|
|||
stack = site.Middleware[i](stack)
|
||||
}
|
||||
site.middlewareChain = stack
|
||||
site.Server = s
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
@ -95,7 +94,7 @@ func (s *Server) ServePacket(p net.PacketConn) error {
|
|||
|
||||
// Listen implements caddy.TCPServer interface.
|
||||
func (s *Server) Listen() (net.Listener, error) {
|
||||
l, err := net.Listen("tcp", s.Addr)
|
||||
l, err := net.Listen("tcp", s.Addr[len(TransportDNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -104,7 +103,7 @@ func (s *Server) Listen() (net.Listener, error) {
|
|||
|
||||
// ListenPacket implements caddy.UDPServer interface.
|
||||
func (s *Server) ListenPacket() (net.PacketConn, error) {
|
||||
p, err := net.ListenPacket("udp", s.Addr)
|
||||
p, err := net.ListenPacket("udp", s.Addr[len(TransportDNS+"://"):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ package dnsserver
|
|||
// care what middleware above them are doing.
|
||||
|
||||
var directives = []string{
|
||||
"tls",
|
||||
"root",
|
||||
"bind",
|
||||
"trace",
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
_ "github.com/coredns/coredns/middleware/rewrite"
|
||||
_ "github.com/coredns/coredns/middleware/root"
|
||||
_ "github.com/coredns/coredns/middleware/secondary"
|
||||
_ "github.com/coredns/coredns/middleware/tls"
|
||||
_ "github.com/coredns/coredns/middleware/trace"
|
||||
_ "github.com/coredns/coredns/middleware/whoami"
|
||||
)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# Local middleware example:
|
||||
# 80:log:log
|
||||
|
||||
1:tls:tls
|
||||
10:root:root
|
||||
20:bind:bind
|
||||
30:trace:trace
|
||||
|
|
|
@ -38,7 +38,7 @@ func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
|
|||
m.Authoritative = true
|
||||
|
||||
// small dance to copy rrA or rrAAAA into a non-pointer var that allows us to overwrite the ownername
|
||||
// in a non-racy manor.
|
||||
// in a non-racy way.
|
||||
switch state.QType() {
|
||||
case dns.TypeA:
|
||||
rr := *(rrA.(*dns.A))
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// See core/dnsserver/address.go - we should unify these two impls.
|
||||
|
||||
// Zones respresents a lists of zone names.
|
||||
type Zones []string
|
||||
|
||||
|
@ -56,12 +58,24 @@ type (
|
|||
)
|
||||
|
||||
// Normalize will return the host portion of host, stripping
|
||||
// of any port. The host will also be fully qualified and lowercased.
|
||||
// of any port or transport. The host will also be fully qualified and lowercased.
|
||||
func (h Host) Normalize() string {
|
||||
|
||||
s := string(h)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(s, TransportTLS+"://"):
|
||||
s = s[len(TransportTLS+"://"):]
|
||||
case strings.HasPrefix(s, TransportDNS+"://"):
|
||||
s = s[len(TransportDNS+"://"):]
|
||||
case strings.HasPrefix(s, TransportGRPC+"://"):
|
||||
s = s[len(TransportGRPC+"://"):]
|
||||
}
|
||||
|
||||
// separate host and port
|
||||
host, _, err := net.SplitHostPort(string(h))
|
||||
host, _, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
host, _, _ = net.SplitHostPort(string(h) + ":")
|
||||
host, _, _ = net.SplitHostPort(s + ":")
|
||||
}
|
||||
return Name(host).Normalize()
|
||||
}
|
||||
|
@ -77,3 +91,10 @@ func (a Addr) Normalize() string {
|
|||
// TODO(miek): lowercase it?
|
||||
return net.JoinHostPort(addr, port)
|
||||
}
|
||||
|
||||
// Duplicated from core/dnsserver/address.go !
|
||||
const (
|
||||
TransportDNS = "dns"
|
||||
TransportTLS = "tls"
|
||||
TransportGRPC = "grpc"
|
||||
)
|
||||
|
|
|
@ -5,16 +5,13 @@ import (
|
|||
"crypto/tls"
|
||||
"log"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
|
||||
|
||||
"github.com/coredns/coredns/middleware/proxy/pb"
|
||||
"github.com/coredns/coredns/middleware/trace"
|
||||
"github.com/coredns/coredns/pb"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
|
|
@ -40,8 +40,7 @@ func reverseParse(c *caddy.Controller) (nets networks, fall bool, err error) {
|
|||
zones := make([]string, len(c.ServerBlockKeys))
|
||||
|
||||
for i, str := range c.ServerBlockKeys {
|
||||
host, _, _ := net.SplitHostPort(str)
|
||||
zones[i] = strings.ToLower(host)
|
||||
zones[i] = middleware.Host(str).Normalize()
|
||||
}
|
||||
|
||||
for c.Next() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/middleware"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
@ -21,7 +22,7 @@ func setup(c *caddy.Controller) error {
|
|||
|
||||
for c.Next() {
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
return middleware.Error("root", c.ArgErr())
|
||||
}
|
||||
config.Root = c.Val()
|
||||
}
|
||||
|
@ -34,7 +35,7 @@ func setup(c *caddy.Controller) error {
|
|||
// But make sure the user knows!
|
||||
log.Printf("[WARNING] Root path does not exist: %s", config.Root)
|
||||
} else {
|
||||
return c.Errf("Unable to access root path '%s': %v", config.Root, err)
|
||||
return middleware.Error("root", c.Errf("unable to access root path '%s': %v", config.Root, err))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestRoot(t *testing.T) {
|
|||
|
||||
// Predefined error substrings
|
||||
parseErrContent := "Parse error:"
|
||||
unableToAccessErrContent := "Unable to access root path"
|
||||
unableToAccessErrContent := "unable to access root path"
|
||||
|
||||
existingDirPath, err := getTempDirPath()
|
||||
if err != nil {
|
||||
|
|
13
middleware/tls/README.md
Normal file
13
middleware/tls/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# tls
|
||||
|
||||
*tls* extra TLS configuration.
|
||||
|
||||
## Syntax
|
||||
|
||||
~~~ txt
|
||||
tls [STUFF]
|
||||
~~~
|
||||
|
||||
**STUFF** is things you'll need to configure TLS.
|
||||
|
||||
## Examples
|
37
middleware/tls/tls.go
Normal file
37
middleware/tls/tls.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/middleware"
|
||||
"github.com/coredns/coredns/middleware/pkg/tls"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("tls", caddy.Plugin{
|
||||
ServerType: "dns",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
config := dnsserver.GetConfig(c)
|
||||
|
||||
if config.TLSConfig != nil {
|
||||
return middleware.Error("tls", c.Errf("TLS already configured for this server instance"))
|
||||
}
|
||||
|
||||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
if len(args) != 3 {
|
||||
return middleware.Error("tls", c.ArgErr())
|
||||
}
|
||||
tls, err := tls.NewTLSConfig(args[0], args[1], args[2])
|
||||
if err != nil {
|
||||
return middleware.Error("tls", c.ArgErr())
|
||||
}
|
||||
config.TLSConfig = tls
|
||||
}
|
||||
return nil
|
||||
}
|
44
middleware/tls/tls_test.go
Normal file
44
middleware/tls/tls_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func TestTLS(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
expectedRoot string // expected root, set to the controller. Empty for negative cases.
|
||||
expectedErrContent string // substring from the expected error. Empty for positive cases.
|
||||
}{
|
||||
// positive
|
||||
// negative
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.input)
|
||||
err := setup(c)
|
||||
//cfg := dnsserver.GetConfig(c)
|
||||
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if !test.shouldErr {
|
||||
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), test.expectedErrContent) {
|
||||
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
test/grpc_test.go
Normal file
62
test/grpc_test.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/coredns/coredns/pb"
|
||||
)
|
||||
|
||||
func TestGrpc(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
|
||||
corefile := `grpc://.:0 {
|
||||
whoami
|
||||
}
|
||||
`
|
||||
g, err := CoreDNSServer(corefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS serving instance: %s", err)
|
||||
}
|
||||
|
||||
_, tcp := CoreDNSServerPorts(g, 0)
|
||||
defer g.Stop()
|
||||
|
||||
conn, err := grpc.Dial(tcp, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(5*time.Second))
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got: %s", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := pb.NewDnsServiceClient(conn)
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("whoami.example.org.", dns.TypeA)
|
||||
msg, _ := m.Pack()
|
||||
|
||||
reply, err := client.Query(context.TODO(), &pb.DnsPacket{Msg: msg})
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
d := new(dns.Msg)
|
||||
err = d.Unpack(reply.Msg)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error but got: %s", err)
|
||||
}
|
||||
|
||||
if d.Rcode != dns.RcodeSuccess {
|
||||
t.Errorf("Expected success but got %s", d.Rcode)
|
||||
}
|
||||
|
||||
if len(d.Extra) != 2 {
|
||||
t.Errorf("Expected 2 RRs in additional section, but got %s", len(d.Extra))
|
||||
}
|
||||
t.Logf("Message %v\n", d)
|
||||
}
|
|
@ -46,7 +46,7 @@ func TestLookupProxy(t *testing.T) {
|
|||
}
|
||||
// expect answer section with A record in it
|
||||
if len(resp.Answer) == 0 {
|
||||
t.Error("Expected to at least one RR in the answer section, got none")
|
||||
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||
}
|
||||
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue